it-swarm.com.de

A + B ohne arithmetische Operatoren, Python vs C++

Ich habe versucht, eine alte Frage zu lösen:

Schreiben Sie eine Funktion, die zwei [Integer] -Zahlen A und B addiert. Sie sollten kein + oder keine arithmetischen Operatoren verwenden.

Die beste Lösung ist wie folgt, zitiert aus " LintCode-A + B Problem ":

Für a + b in jeder Basis können wir das Plus als zwei Teile behandeln: 1. a + b ohne Carry; 2. der durch a + b generierte Übertrag. A + b entspricht dann Teil 1 plus Teil 2. Wenn Teil1 + Teil2 mehr Übertrag erzeugt, können wir diesen Vorgang wiederholen, bis kein Übertrag mehr vorhanden ist.

Ich kann diesen Algorithmus verstehen und alles scheint gut zu sein, also habe ich ihn mit lintcode mit untenstehendem Code getestet.

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

Aber überraschenderweise gab es einen Time Limit Exceeded-Fehler im Testfall [100, -100]. Also habe ich es lokal ausgeführt und a, b für jede Schleife gedruckt:

(-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)
...

Die Berechnung ist korrekt, daher denke ich, dass dieser Algorithmus für solche Eingaben nicht funktioniert, aber wenn ich denselben Algorithmus in C++ geschrieben habe, funktioniert er einfach:

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

Ich weiß nicht, was genau gefragt werden sollte, im Grunde sind die Fragen:

  1. Warum gibt C++ die korrekte Ausgabe 0, während Python dies nicht tut?
  2. Wenn ich Python verwende, wie kann ich diesen Algorithmus modifizieren, damit er funktioniert?
30
laike9m

Die binäre 2-Komplement-Darstellung von -4 ist

...11100

Ja, ich meine wirklich unendlich viele 1s auf der linken Seite; Dies ist eine binäre Wiederholungszahl. Technisch gesehen ist 4 auch eine sich wiederholende Zahl:

...00100

es wiederholt nur 0s auf der linken Seite.

Ihr Additionsproblem ist

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

Die Operatoren ^, << und & haben keine Probleme mit unendlich vielen Binärzahlen, aber das Problem ist, dass es unendlich viele Übertragungen gibt, und Sie berechnen sie jeweils eine Ziffer . Das wird niemals enden.

Daher müssen Sie erkennen, wann dieser Algorithmus in dieser Situation stecken bleibt, und etwas anderes tun, um dies zu berücksichtigen.


In C/C++ stößt man nicht auf dieses Problem. Wenn beispielsweise int 32-Bit ist, werden alle Ziffern mit Ausnahme der rechten 31 Ziffern in einem einzigen Bit zusammengefasst auf einmal.

Technisch gesehen bedeutet die Verschiebung einer int nach links jedoch den Wert als Ganzzahl und nicht als Bitmuster. Sie rufen also undefined behaviour auf, wenn die beiden höchstwertigen Bits carry jemals sind anders, weil dann carry << 1 einen Überlauf erzeugen würde).

25
Hurkyl

Das Problem sind negative Zahlen oder wie sie dargestellt werden. In Python haben Integer-Zahlen eine beliebige Genauigkeit, während C++ - Werte 32-Bit oder 64-Bit sind. In Python müssen Sie also mit negativen Zahlen umgehen, z. Subtraktion separat oder begrenzen Sie die Anzahl der Bits von Hand.

10
Daniel

Nach der großartigen Erklärung von @Hurkyl ging ich den Algorithmus für a=4 und b=-4 durch, wobei Python eine unendliche Zweierkomplementdarstellung implementiert:

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

Es ist klar, dass es einen effektiven Cutoff geben muss, um so etwas wie eine 32-Bit-Zweiergruppe mit Vorzeichen zu simulieren. Sobald das Übertragsbit über das höchste Bit hinaus sprudelt, muss der Algorithmus angehalten werden. Folgendes scheint zu funktionieren:

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

Ergebnisse:

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

Wenn binäre 1-Bit-Mathematikoperationen (^) verboten sind, gehen Sie für unary!

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)))
3
guest

Das liegt daran, dass Python normalerweise kein 32-Bit-Integer mit Vorzeichen verwendet.

Siehe: ctypes.c_int32

Akzeptierte Lösung:

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
1
Howard Liang

Meine Lösung:

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)

Wie schon gesagt, ist bitweise besser.

0
James