J'essayais de résoudre une vieille question:
Ecrivez une fonction qui ajoute deux nombres [entiers] A et B. Vous ne devez pas utiliser + ni d'opérateurs arithmétiques.
La meilleure solution est la suivante, citée dans " LintCode-A + B Problem ":
Pour a + b dans n'importe quelle base, nous pouvons traiter le plus comme deux parties: 1. a + b sans retenue; 2. le report généré par a + b. Le a + b est alors égal à la partie 1 plus la partie 2. Si part1 + partie2 génère plus de report, nous pouvons alors répéter cette procédure jusqu'à ce qu'il n'y ait plus de report.
Je peux comprendre cet algorithme et tout semble bon, je l’ai donc testé sur lintcode avec le code collé ci-dessous.
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
Mais étonnamment, cela m'a donné l'erreur Time Limit Exceeded
dans le scénario de test [100, -100]
. Alors je l'ai couru localement et imprimer a, b pour chaque boucle:
(-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)
...
Le calcul est correct, je pense donc que cet algorithme ne fonctionne pas pour une telle entrée, mais lorsque j'ai écrit le même algorithme en C++, cela fonctionne:
class Solution {
public:
int aplusb(int a, int b) {
while (b!=0){
int carry = a & b;
a = a^b;
b = carry << 1;
}
return a;
}
};
Je ne sais pas ce qui devrait être demandé exactement, les questions sont essentiellement:
0
, contrairement à Python?La représentation binaire, complément à 2 de -4
est
...11100
Oui, je veux vraiment dire infiniment beaucoup de 1
à gauche; c'est un chiffre binaire qui se répète. Techniquement, 4
est également un chiffre qui se répète:
...00100
il ne fait que répéter les 0
à gauche.
Votre problème d'addition est
...11100
+ ...00100
--------------------
...00000
Les opérateurs ^
, <<
et &
n'ont aucune difficulté à calculer avec une infinité de chiffres binaires, mais le problème est qu’il existe un nombre infini de reports et que vous les calculez un chiffre à la fois . Cela ne finira jamais.
Ainsi, vous devez reconnaître quand cet algorithme va rester bloqué dans cette situation et faire autre chose pour en tenir compte.
Vous ne rencontrez pas ce problème en C/C++, car, par exemple, si int
est 32 bits, tous les chiffres, à l’exception des 31 derniers chiffres situés à la droite, sont réduits en un seul bit, le reste immediatement.
Cependant, techniquement parlant, le fait de déplacer à gauche un int
correspond à la valeur en tant qu’entier, plutôt qu’en tant que motif binaire, de sorte que vous appelez comportement non défini si les deux bits les plus significatifs carry
sont toujours différent, car carry << 1
produirait un débordement).
Le problème sont les nombres négatifs, ou comment ils sont représentés. En Python, les nombres entiers ont une précision arbitraire, alors que les entiers C++ ont 32 bits ou 64 bits. Donc, en Python, vous devez gérer les nombres négatifs, par exemple. soustraction, séparément ou limiter le nombre de bits à la main.
Suite à la grande explication de @Hurkyl, j’ai exploré l’algorithme pour a=4
et b=-4
en utilisant le fait que python implémente une représentation complémentaire à deux infinis:
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
Il est clair qu’il faut une coupure efficace pour émuler quelque chose comme un entier complémentaire de 32 bits signé. Une fois que le bit de retenue bouillonne au-delà du bit le plus élevé, l'algorithme doit être arrêté. Ce qui suit semble fonctionner:
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
Résultats:
>>> aplusb(100,-100)
0
>>> aplusb(100,-99)
1
>>> aplusb(97,-99)
-2
>>> aplusb(1000,-500)
500
>>> aplusb(-1000,8000)
7000
Si les opérations mathématiques binaires 1 bit (^) sont interdites, optez pour unaire!
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)))
En effet, python n’utilise normalement pas d’entier signé 32 bits.
Voir: ctypes.c_int32
Solution acceptée:
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
Ma solution:
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)
Comme déjà indiqué, bitwise est préférable.