Побитовое умножение и добавление в Java

У меня есть методы, которые делают как умножение, так и сложение, но я просто не могу обойти их. Оба они с внешних сайтов, а не мои:

public static void bitwiseMultiply(int n1, int n2) {
    int a = n1, b = n2, result=0;
    while (b != 0) // Iterate the loop till b==0
    {
        if ((b & 01) != 0) // Logical ANDing of the value of b with 01
        {
            result = result + a; // Update the result with the new value of a.
        }
        a <<= 1;              // Left shifting the value contained in 'a' by 1.
        b >>= 1;             // Right shifting the value contained in 'b' by 1.
    }
    System.out.println(result);
}

public static void bitwiseAdd(int n1, int n2) {
    int x = n1, y = n2;
    int xor, and, temp;
    and = x & y;
    xor = x ^ y;

    while (and != 0) {
        and <<= 1;
        temp = xor ^ and;
        and &= xor;
        xor = temp;
    }
    System.out.println(xor);
}

Я попытался выполнить пошаговую отладку, но это действительно не имело большого смысла для меня, хотя это работает.

то, что я, возможно, ищу, это попытаться понять, как это работает (математическая основа, возможно?).

Edit: это не домашнее задание, я просто пытаюсь учиться побитовые операции в Java.

4 ответов


давайте начнем с поиска кода умножения. Идея на самом деле довольно умный. Предположим, что у вас n1 и n2 написано в двоичной системе. Тогда вы можете думать о n1 как о сумме степеней двух: n2 = c30 230 + c29 229 + ... + c1 21 + c0 20, где каждый cЯ - 0 или 1. Тогда вы можете думать о продукте n1 n2 as

n1 n2 =

n1 (c30 230 + c29 229 + ... + c1 21 + c0 20) =

n1 c30 230 + n1 c29 229 + ... + n1 c1 21 + n1 c0 20

это немного плотное, но идея заключается в том, что произведение двух чисел задается первым числом, умноженным на степени двух, составляющих второе число, умноженное на значение двоичных цифр второго числа.

теперь вопрос в том, можем ли мы вычислить члены этой суммы без каких-либо фактических умножений. Чтобы сделать это, мы должны быть в состоянии прочитайте двоичные цифры n2. К счастью, мы можем сделать это с помощью смены. В частности, предположим, что мы начнем с n2 а затем просто посмотрите на последний бит. Это c0. Если мы затем сдвинем значение вниз на одну позицию, то последний бит будет c0, etc. В более общем случае, после смещения значения n2 вниз по позициям i, самый низкий бит будет cЯ. Чтобы прочитать самый последний бит, мы можем просто побитовое и значение с номером 1. Это двоичное представление, которое равно нулю везде, кроме последней цифры. Поскольку 0 и n = 0 для любого n, это очищает все верхние биты. Более того, поскольку 0 и 1 = 0 и 1 и 1 = 1, эта операция сохраняет последний бит числа.

хорошо-теперь мы знаем, что можем читать значения cЯ, Так что? Ну, хорошая новость заключается в том, что мы также можем вычислить значения ряда n1 2Я аналогично. В частности, рассмотрим последовательность значений n11

public static void bitwiseMultiply(int n1, int n2) {
    /* This value will hold n1 * 2^i for varying values of i.  It will
     * start off holding n1 * 2^0 = n1, and after each iteration will 
     * be updated to hold the next term in the sequence.
     */
    int a = n1;

    /* This value will be used to read the individual bits out of n2.
     * We'll use the shifting trick to read the bits and will maintain
     * the invariant that after i iterations, b is equal to n2 >> i.
     */
    int b = n2;

    /* This value will hold the sum of the terms so far. */
    int result = 0;

    /* Continuously loop over more and more bits of n2 until we've
     * consumed the last of them.  Since after i iterations of the
     * loop b = n2 >> i, this only reaches zero once we've used up
     * all the bits of the original value of n2.
     */
    while (b != 0)
    {
        /* Using the bitwise AND trick, determine whether the ith 
         * bit of b is a zero or one.  If it's a zero, then the
         * current term in our sum is zero and we don't do anything.
         * Otherwise, then we should add n1 * 2^i.
         */
        if ((b & 1) != 0)
        {
            /* Recall that a = n1 * 2^i at this point, so we're adding
             * in the next term in the sum.
             */
            result = result + a;
        }

        /* To maintain that a = n1 * 2^i after i iterations, scale it
         * by a factor of two by left shifting one position.
         */
        a <<= 1;

        /* To maintain that b = n2 >> i after i iterations, shift it
         * one spot over.
         */
        b >>>= 1;
    }

    System.out.println(result);
}

надеюсь, что это помогает!


похоже, что ваша проблема не java, а просто вычисление с двоичными числами. Начало простое: (все числа двоичные:)

0 + 0 = 0   # 0 xor 0 = 0
0 + 1 = 1   # 0 xor 1 = 1
1 + 0 = 1   # 1 xor 0 = 1
1 + 1 = 10  # 1 xor 1 = 0 ( read 1 + 1 = 10 as 1 + 1 = 0 and 1 carry)

Ok... Вы видите, что вы можете добавить два однозначных числа с помощью операции XOR. Теперь вы можете узнать, есть ли у вас бит" carry", который очень похож на добавление чисел с помощью ручки и бумаги. (До этого момента у вас есть что-то, называемое Полусуммой). Когда вы добавляете следующие два бита, вам также нужно добавить бит переноса к ним две цифры. Принимая это во внимание, вы можете получить полный сумматор. Вы можете прочитать о понятиях полу-Аддеров и полных Аддеров в Википедии: http://en.wikipedia.org/wiki/Adder_ (электроника) И еще много мест в интернете. Надеюсь, это даст вам толчок.

с умножением это очень похоже, кстати. Просто помните, как вы делали умножение с ручкой и бумагой в начальной школе. Вот что здесь происходит. Только то, что это происходит с двоичными числами, а не с десятичными числами.


объяснение метода bitwiseAdd:

я знаю, что этот вопрос был задан некоторое время назад, но поскольку не было дано полного ответа о том, как работает метод bitwiseAdd, вот один.

ключ к пониманию логики, инкапсулированной в bitwiseAdd, находится в отношениях между дополнение операции и xor и и побитовые операции. Эта связь определяется следующее уравнение (численный пример этого уравнения см. В Приложении 1):

x + y = 2 * (x&y)+(x^y)     (1.1)

или проще:

x + y = 2 * and + xor       (1.2)

with
    and = x & y
    xor = x ^ y

возможно, вы заметили что-то знакомое в этом уравнении:и и xor переменные такие же, как те, которые определены в начале bitwiseAdd. Существует также умножение на два, которое в bitwiseAdd выполняется в начале цикла while. Но я вернусь к этому позже.

позвольте мне также сделать быструю заметку о побитовом операторе"&", прежде чем мы продолжим дальше. этот оператор в основном "захватывает" пересечение битовых последовательностей, против которых он применяется. Например, 9 & 13 = 1001 & 1101 = 1001 (=9). Из этого результата видно, что в результат копируются только те биты, которые являются общими для обеих битовых последовательностей. Это происходит из того, что когда две битовые последовательности не имеют общего бита, результат применения ' & ' на них урожайность 0. это имеет важное последствие для побитового отношения сложения, которое скоро станет ясным

теперь проблема заключается в том, что уравнение 1.2 использует оператор"+", а bitwiseAdd-нет (он использует только"^", " & "и"и выражение вернет 0. И мы делаем это, используя рекурсия.

чтобы продемонстрировать это, я собираюсь рекурсивное уравнение 1.2 один раз (этот шаг может быть немного сложным сначала, но при необходимости есть подробный шаг за шагом результат в Приложении 2):

x + y = 2*(2*and & xor) + (2*and ^ xor)     (1.3)

или проще:

x + y = 2 * and[1] + xor[1]     (1.4)

with
    and[1] = 2*and & xor,
    xor[1] = 2*and ^ xor,
    [1] meaning 'recursed one time'

есть несколько интересных вещей, чтобы отметить здесь. Сначала вы заметили, как концепция рекурсии звучит близко к концепции цикла, как и в bitwiseAdd на самом деле. Это соединение становится еще более очевидным, когда вы считаете, что а[1] и xor[1]: они такие же выражения, как и и xor выражения, определения внутри цикл while в bitwiseAdd. Отметим также, что возникает закономерность: уравнение 1.4 выглядит точно так же, как уравнение 1.2!

в результате этого выполнение дальнейших рекурсий-это ветер, если сохранить рекурсивную нотацию. Здесь мы рекурсивное уравнение 1.2 еще два раза:

x + y = 2 * and[2] + xor[2]
x + y = 2 * and[3] + xor[3]

теперь это должно подчеркнуть роль переменной "temp", найденной в bitwiseAdd:temp позволяет перейти с одного уровня рекурсии на другой.

мы также замечаем умножение на два во всех этих уравнениях. Как упоминалось ранее, это умножение выполняется в начале цикла while в bitwiseAdd с помощью и заявление. Это умножение имеет последствия на следующем этапе рекурсии поскольку биты в и[i] отличаются от битов в и[i] предыдущего этапа (и если вы помните небольшую заметку, которую я сделал ранее о операторе"&", вы, вероятно, видите, куда это происходит сейчас).

общая форма уравнения 1.4 теперь становится:

x + y = 2 * and[x] + xor[x]     (1.5)
with x the nth recursion

FINALY:

Итак, когда именно заканчивается этот бизнес рекурсии?

ответ: он заканчивается, когда пересечение между двумя битами последовательности в и[x] выражение уравнения 1.5 возвращает 0. Эквивалент этого в bitwiseAdd происходит, когда условие цикла while становится ложным. В этот момент уравнение 1.5 становится:

    x + y = xor[x]      (1.6)

и это объясняет, почему в bitwiseAdd мы возвращаемся только xor в конце!

и мы сделали! Довольно умный кусок кода этого bitwiseAdd я должен сказать:)

я надеюсь, что это помогли

приложение:

1) числовой пример уравнения 1.1

уравнение 1.1 говорит:

    x + y = 2(x&y)+(x^y)        (1.1)

чтобы проверить это утверждение можно взять простой пример, скажем добавив 9 и 13 вместе. Шаги показаны ниже (побитовые представления в скобках):

у нас есть

    x = 9 (1001)
    y = 13  (1101)

и

    x + y = 9 + 13 = 22
    x & y = 9 & 13 = 9 (1001 & 1101 = 1001)
    x ^ y = 9^13 = 4 (1001 ^ 1101 = 0100)

электросети, что обратно в уравнение 1.1 мы найти:

    9 + 13 = 2 * 9 + 4 = 22 et voila!

2) демонстрация первого шага рекурсии

первое уравнение рекурсии в представлении (уравнение 1.3) говорит, что

если

x + y = 2 * and + xor           (equation 1.2)

затем

x + y = 2*(2*and & xor) + (2*and ^ xor)     (equation 1.3)

чтобы получить этот результат, мы просто взяли 2* и + xor часть уравнения 1.2 выше и применил сложение / побитовое отношение операндов дано уравнением 1.1 к нему. Это показали как следует:

если

    x + y = 2(x&y) + (x^y)      (equation 1.1)

затем

     [2(x&y)] + (x^y)     =      2 ([2(x&y)] & (x^y)) + ([2(x&y)] ^ (x^y))
(left side of equation 1.1)  (after applying the addition/bitwise operands relationship)

упрощение этого с определениями и и xor переменные уравнения 1.2 дают результат уравнения 1.3:

[2(x&y)] + (x^y) = 2*(2*and & xor) + (2*and ^ xor)

with
    and = x&y
    xor = x^y

и использование того же упрощения дает результат уравнения 1.4:

2*(2*and & xor) + (2*and ^ xor) = 2*and[1] + xor[1]

with
    and[1] = 2*and & xor
    xor[1] = 2*and ^ xor
    [1] meaning 'recursed one time'

вот еще один подход к умножению

/**
 * Multiplication of binary numbers without using '*' operator
 * uses bitwise Shifting/Anding
 *
 * @param n1
 * @param n2
 */
public static void multiply(int n1, int n2) {
    int temp, i = 0, result = 0;

    while (n2 != 0) {
        if ((n2 & 1) == 1) {
            temp = n1;

            // result += (temp>>=(1/i)); // To do it only using Right shift
            result += (temp<<=i); // Left shift (temp * 2^i)
        }
        n2 >>= 1;   // Right shift n2 by 1.
        i++;
    }

    System.out.println(result);
}