Подтвердить что ты не робот

A + B без арифметических операторов, Python vs С++

Я пытался решить старый вопрос:

Напишите функцию, которая добавит два [целочисленных] числа A и B. Вы не должны использовать + или любые арифметические операторы.

Лучшее решение похоже на это: " проблема LintCode-A + B:

При a + b в любой базе мы можем рассматривать плюс как две части: 1. a + b без переноса; 2. перенос, генерируемый a + b. Затем a + b равен части 1 плюс часть 2. Если part1 + part2 генерирует больше переносов, мы можем повторить эту процедуру до тех пор, пока не будет перенесено.

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

class Solution:
    """
    @param a: The first integer
    @param b: The second integer
    @return:  The sum of a and b
    """
    def aplusb(self, a, b):

        while b != 0:
            carry = a & b
            a = a ^ b
            b = carry << 1

        return a

Но удивительно, что это дало мне ошибку Time Limit Exceeded в тестовом случае [100, -100]. Поэтому я запустил его локально и напечатал a, b для каждого цикла:

(-8, 8)
(-16, 16)
(-32, 32)
(-64, 64)
(-128, 128)
(-256, 256)
(-512, 512)
(-1024, 1024)
(-2048, 2048)
(-4096, 4096)
(-8192, 8192)
(-16384, 16384)
(-32768, 32768)
(-65536, 65536)
(-131072, 131072)
...

Расчет правильный, поэтому я думаю, что этот алгоритм не работает для такого ввода, но когда я написал тот же алгоритм в С++, он просто работает:

class Solution {
public:
    int aplusb(int a, int b) {
        while (b!=0){
            int carry = a & b;
            a = a^b; 
            b = carry << 1;
        }
        return a;
    }
};

Я не знаю, что именно следует задать, в основном вопросы:

  • Почему С++ дает правильный вывод 0, в то время как Python не работает?
  • Если я использую Python, как мне изменить этот алгоритм, чтобы он работал?
4b9b3361

Ответ 1

Бинарное, 2-комплементное представление -4 равно

...11100

Да, я действительно имею в виду бесконечное число 1 слева; это двоичная повторяющаяся цифра. Технически, 4 также является повторяющейся цифрой:

...00100

он просто повторяет 0 влево.

Проблема с добавлением

   ...11100
+  ...00100
--------------------
   ...00000

Операторы ^, << и & не имеют проблем с вычислением с бесконечным числом двоичных цифр, но проблема в том, что существует бесконечно много переносов, и вы вычисляете их по одной цифре за раз. Это никогда не закончится.

Таким образом, вам нужно узнать, когда этот алгоритм застрянет в этой ситуации и сделает что-то еще для его учета.


Вы не сталкиваетесь с этой проблемой в C/С++, потому что, например, если int является 32-битным, тогда все цифры, кроме самых правых 31 цифр, сворачиваются в один бит, поэтому остальное несет все сразу.

Однако, с технической точки зрения, значение сдвига влево int имеет значение как целое число, а не как битовый шаблон, поэтому вы вызываете поведение undefined, если два наиболее значимых бита carry всегда разные, потому что тогда carry << 1 приведет к переполнению).

Ответ 2

Проблема - это отрицательные числа или, как они представлены. В Python целые числа имеют произвольную точность, а С++ ints - 32-битные или 64-битные. Поэтому в Python вам приходится обрабатывать отрицательные числа, например. вычитания, отдельно или ограничить количество бит вручную.

Ответ 3

Следуя большому объяснению @Hurkyl, я прошел через алгоритм для a=4 и b=-4, используя тот факт, что python реализует бесконечное два представления комплимента:

Step 0:

a = ...(0)...000100
b = ...(1)...111100

carry = a & b = ...(0)...000100
a = a ^ b = ...(1)...111000
b = carry << 1 = ...(0)...001000

Step 1:

a = ...(1)...111000
b = ...(0)...001000

carry = a & b = ...(0)...001000
a = a ^ b = ...(1)...110000
b = carry << 1 = ...(0)...010000

Step 2:

a = ...(1)...110000
b = ...(0)...010000

carry = a & b = ...(0)...010000
a = a ^ b = ...(1)...100000
b = carry << 1 = ...(0)...100000

Понятно, что должно быть эффективное обрезание для эмуляции чего-то вроде 32-битного подписанного двух комплиментов integer. После того, как бит переноса пузырится выше максимального бита, алгоритм должен быть остановлен. Появится следующее:

MAX_BIT = 2**32
MAX_BIT_COMPLIMENT = -2**32

def aplusb(a, b):

    while b != 0:
        if b == MAX_BIT:
            return a ^ MAX_BIT_COMPLIMENT
        carry = a & b
        a = a ^ b
        b = carry << 1

    return a

Результаты:

>>> aplusb(100,-100)
0
>>> aplusb(100,-99)
1
>>> aplusb(97,-99)
-2
>>> aplusb(1000,-500)
500
>>> aplusb(-1000,8000)
7000

Ответ 4

Если 1-битные бинарные математические операции (^) запрещены, перейдите к унарным!

from itertools import chain

def unary(x):
    "Unary representation of x"
    return ''.join(['x' for _ in range(0,x)])

def uplus(x, y):
    "Unary sum of x and y"
    return [c for c in chain(x,y)]

def plus(i, j):
    "Return sum calculated using unary math"
    return len(uplus(unary(i), unary(j)))

Ответ 5

Это связано с тем, что в python обычно не используется 32-битный подписанный int.

Смотрите: ctypes.c_int32

Принятое решение:

class Solution:
"""
@param a: The first integer
@param b: The second integer
@return:  The sum of a and b
"""
def aplusb(self, a, b):
    import ctypes
    a = ctypes.c_int32(a).value
    a = ctypes.c_int32(a).value
    while b != 0:
        carry = ctypes.c_int32(a & b).value
        a = ctypes.c_int32(a ^ b).value
        b = ctypes.c_int32(carry << 1).value

    return a

Ответ 6

Мое решение:

def foo(a, b):
"""iterate through a and b, count iteration via a list, check len"""
    x = []
    for i in range(a):
            x.append(a)
    for i in range(b):
            x.append(b)
    print len(x)

Как уже было сказано, поразрядное лучше.