web-dev-qa-db-ja.com

算術演算子なしのA + B、Python vs C ++

私は古い質問を解決しようとしていました:

2つの[整数]の数値AとBを加算する関数を記述します。+や算術演算子は使用しないでください。

最良の解決策は次のようになり、 " LintCode-A + B Problem "から引用されます。

任意の基数のa + bの場合、プラスを2つの部分として扱うことができます。1。キャリーなしのa + b; 2. + 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)
...

計算は正しいので、このアルゴリズムはそのような入力では機能しないと思いますが、C++で同じアルゴリズムを記述した場合、それは機能します。

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

正確に何を尋ねるべきかわかりません。基本的に質問は次のとおりです。

  1. C++が正しい出力0を提供するのに、Pythonは提供しないのはなぜですか?
  2. Pythonを使用している場合、このアルゴリズムを変更して機能させるにはどうすればよいですか?
30
laike9m

-4の2の補数表現は次のとおりです。

...11100

はい、私は本当に左側に無限に多くの1を意味します。これは2進数の繰り返し数字です。技術的には、4も繰り返し数字です。

...00100

左に0を繰り返しているだけです。

あなたの足し算の問題は

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

演算子^<<、および&は、無限に多くの2進数で問題なく計算できますが、問題は、無限に多くの桁上げがあり、それらを計算していることです一度に1桁。これは決して終わらないでしょう。

したがって、このアルゴリズムがこの状況でスタックする時期を認識し、それを説明するために何か他のことをする必要があります。


たとえば、intが32ビットの場合、右端の31桁を除くすべての桁が1つのビットに折りたたまれるので、C/C++ではこの問題は発生しません。残りのキャリーは一度に行います。

ただし、技術的に言えば、intを左シフトすることの意味は、ビットパターンではなく整数としての値であるため、2つがあれば未定義の振る舞いを呼び出します。最上位ビットcarryは常に異なります。これは、carry << 1がオーバーフローを生成するためです)。

25
Hurkyl

問題は負の数、またはそれらがどのように表されるかです。 In Python整数は任意の精度ですが、C++ intは32ビットまたは64ビットです。したがって、Pythonでは、減算などの負の数を個別に処理するか、手動でビット数を制限する必要があります。

10
Daniel

@Hurkylによるすばらしい説明に続いて、pythonが無限の2の補数表現を実装しているという事実を使用して、a=4およびb=-4のアルゴリズムをステップスルーしました。

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ビットの符号付き2の補数整数のようなものをエミュレートするには、効果的なカットオフが必要であることは明らかです。キャリービットが最高ビットを超えてバブルアップしたら、アルゴリズムを停止する必要があります。以下が機能しているようです。

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
7
Eric Appelt

1ビットの2進数学演算(^)が禁止されている場合は、単項演算を実行してください。

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

これは、pythonは通常32ビットのsignedintを使用していないためです。

参照: 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
2
Howard Liang

私の解決策:

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)

すでに述べたように、ビット単位の方が優れています。

0
James