A xor X = B + Xの解を見つけるアルゴリズム


46

整数AおよびBが与えられた場合、整数Xを見つけて次のようにします。

  • A、B <2 * 1e18
  • A xor X = B + X

数学を使ってこの方程式を解くことができるかどうか、私は強く疑います。これは私が3年前に遭遇したコーディングの問題であり、今でもこれを自分で解決することはできません。

これまでの私のコード:(これはブルートフォースソリューションです)

#include <iostream>

using namespace std;

int main()
{

    unsigned long long a, b;
    cin >> a >> b;
    for (unsigned long long x = 1; x < max(a, b); x++) {
        unsigned long long c = a ^ x;
        unsigned long long d = b + x;
        if (c == d) {
            cout << x << endl;
            break;
            return 0;
        }
    }

    cout << -1; //if no such integer exists

    return 0;
}

11
排他についてもう少し読んだり、代数的等価性を見つけたりする必要がある場合a xor b = a + b mod 2。その同等性についてしばらく考えてみてください。
プログラマー

16
@Someprogrammerdudeこれabがブール変数、つまり0または1であり、xorがブールxorである場合です。ビットワイズxorへの接続は何ですか?
John Kugelman

1
fwiw、ここでブルートフォースを使用するのは、より一般的な方程式を証明できるものを書きたいのでない限り、進むべき道だと思います。コードが正しいことを確認するためにコードをテストする必要があり、ブルートフォースアルゴリズムに対してテストするのが最も簡単であることを考慮してください。ただし、そもそもブルートフォースを使用できます。一方、数学を適用すると、最終的にはコードを実行する必要がなくなります。
idclev 463035818

1
@molbdniloああ、コメントの1つはa xor b = a + b mod 2であると示唆しており、整数も参照していると思いました。投稿のその部分を削除します。
AAaAa

1
@JohnKugelman彼はmod 2数学の(mod 2)と同じように、つまり3 === 7(mod 2)を意味しました。重要なのは、Xの最初のビットの方程式を見つけてから、次のビットに進み、(キャリーに関して)2番目のビットの方程式を得るなどです(ダニエルの答えなど)。
Max Langhof

回答:


45

注意してくださいA + X == (A xor X) + ((A and X)<<1)。そう:

A xor X = A + X - ((A and X)<<1) = B + X
A - B = (A and X)<<1

そして私たちは持っています:

(A - B) and not (A<<1) = 0    (All bits in (A - B) are also set in (A<<1))
(A - B)>>1 = A and X

条件が満たされた場合、Aに設定されているビットを持たない整数Yについては、(((A-B)>> 1)またはY)が解決策です。解が1つだけ必要な場合は、((A-B)>> 1)を使用できます。ここで、Y = 0です。それ以外の場合、解はありません。

int solve(int a, int b){
    int x = (a - b) >> 1;
    if ((a ^ x) == b + x)
        return x;
    else
        return ERROR;
}

15
+1。これは、「キャリーA xor Xなしの加算」であり、((A and X)<<1)「キャリー付き加算」であることに注意してください。以来A + X、「キャリーとの加算」で、最初の式は意味があります。
半分

3
(A and X)<<1これは基本的2*(A and X)に、これと等しいためA-B、AとBが両方とも奇数または両方のイベントである場合にのみ問題が解決する可能性があることを示しています。
axiac

1
それは引き算と関係があるのではないかと思ったが、間に合わなかった。
SSアン

38

それほど難しくはありません。小さく考える必要があるだけです。私たちはを書いていてA、バイナリで、一番右の2ⁱビットに対応する値であるBXします。Aᵢ

私たちはそれを知っています:Aₒ ⊕ Xₒ = Bₒ + Xₒ

例を使用して、それを評価する方法を見つけましょう:A = 15およびB =6。バイナリに変換します。

A = 1 1 1 1           B = 0 1 1 0
X = a b c d           X = a b c d

今、いくつかの可能性があります。AとBの右端のビットを分析してみましょう。

1  d = 0 + d

dこれは0または1のいずれかであることがわかっているので、

for d = 0
1  d = 0 + d    =>    1  0 = 0 + 0    =>    1 = 0 (not possible)

for d = 1
1  d = 0 + d    =>    1  1 = 0 + 1    =>    0 = 1 (not possible)

XORは2進和と同じように動作することがわかります(ただし、XORは次のビット和のキャリーオーバーを作成しない点が異なります)。

    XOR           SUM
0  0 = 0  |   0 + 0 = 0
0  1 = 1  |   0 + 1 = 1
1  0 = 1  |   1 + 0 = 1
1  1 = 0  |   1 + 1 = 0

満たすXを見つけることが常に可能ではありませんのでA ⊕ X = B + X、そこではないため、値dを満たすこと1 + d = 0 + d

とにかく、Xが存在する場合は、この方法で右から左に少しずつ見つけることができます。


完全な動作例

A = 15、B = 7:

A = 1 1 1 1           B = 0 1 1 1
X = a b c d           X = a b c d

1  d = 1 + d 

ここでは、d = 0とd = 1の両方が適用されます。次のビットをチェックする必要があります。d = 1と仮定します。

A = 1 1 1 1           B = 0 1 1 1
X = a b c d           X = a b c d

1  d = 1 + d    =>    1  1 = 1 + 1    =>    0 = 0 (possible)

BUT 1 + 1 = 0 generates a carryover for the next bit sum:

Instead of 1  c = 1 + c, we have 1  c = 1 + c (+1) =
                                   1  c = c  (not possible)

したがって、この場合、dは0でなければなりません。

carryover                              0
         A = 1 1 1 1           B = 0 1 1 1
         X = a b 0 0           X = a b 0 0
        -----------------------------------
                   0                     0

we know that c must be 0:

carryover                            0 0
         A = 1 1 1 1           B = 0 1 1 1
         X = a b 0 0           X = a b 0 0
        -----------------------------------
                 1 1                   1 1

しかし、bはどうですか?いつものように、次のビットをチェックする必要があります。

if b = 0, there won't be a carryover, so we'll have:

1  a = 0 + a  (and this is not possible)

so we try b = 1:

1  b = 1 + b    =>    1  1 = 1 + 1    =>    0 = 0 (with carryover)

そして今、のためにa

carryover                          1 0 0
         A = 1 1 1 1           B = 0 1 1 1
         X = a 1 0 0           X = a 1 0 0
        -----------------------------------
               0 0 0                 0 0 0


1  a = 0 + a (+1)    =>    1  a = 1 + a

ここでaは0と1を指定できますが、合計のキャリーオーバーを避けるために0にする必要がありますB + X

次に、X = 0 1 0 0したがって、X = 4。


コード

#include <iostream>
using namespace std;

inline int bit(int a, int n) {
    if(n > 31) return 0; 
    return (a & ( 1 << n )) >> n; 
}

int main(){
    int A = 19;
    int B = 7;

    int X = 0;
    int carryover = 0;
    int aCurrent, aNext, bCurrent, bNext;

    for(int i = 0; i < 32; i++){
        aCurrent =  bit(A, i);      bCurrent =  bit(B, i);
        aNext =     bit(A, i + 1);  bNext =     bit(B, i + 1);

        if(aCurrent == 0 && bCurrent == 0){
            if(carryover) {X = -1; break;}
            if(aNext != bNext){
                X += 1 << i;
            }
            carryover = 0;
        }
        else if(aCurrent == 0 && bCurrent == 1){
            if(!carryover) {X = -1; break;}
            if(aNext == bNext){
                X += 1 << i;
            }
            carryover = 1;
        }
        else if(aCurrent == 1 && bCurrent == 0){
            if(!carryover) {X = -1; break;}
            if(aNext != bNext){
                X += 1 << i;
                carryover = 1;
            }
            else {
                carryover = 0;
            }
        }
        else if(aCurrent == 1 && bCurrent == 1){
            if(carryover) {X = -1; break;}
            if(aNext != bNext){
                X += 1 << i;
                carryover = 1;
            }
            else {
                carryover = 0;
            }
        }

    }

    if(X != -1) cout<<"X = "<<X<<endl;
    else cout<<"X doesnt exist"<<endl;

    return 0;
}

ここでテストできます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.