Thuật toán tìm giải pháp cho A xor X = B + X


46

Cho số nguyên A và B, tìm số nguyên X sao cho:

  • A, B <2 * 1e18
  • Một xor X = B + X

Tôi rất nghi ngờ có thể giải phương trình này bằng toán học. Đây là một vấn đề mã hóa tôi đã gặp cách đây 3 năm và thậm chí bây giờ tôi không thể tự giải quyết vấn đề này.

Mã của tôi cho đến nay: (đây là giải pháp vũ phu)

#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
Nếu bạn đọc thêm một chút về độc quyền hoặc bạn nên tìm sự tương đương đại số a xor b = a + b mod 2. Hãy thử suy nghĩ về sự tương đương đó một chút.
Một số lập trình viên anh chàng

16
@Someprogrammerdude Đó là nếu ab là biến Boolean, tức là 0 hoặc 1 và xor là xor Boolean. Kết nối với bitwise xor là gì?
John Kugelman

1
fwiw, tôi nghĩ rằng sử dụng vũ lực ở đây là cách để đi trừ khi bạn muốn viết một cái gì đó có thể chứng minh các phương trình tổng quát hơn. Hãy xem xét rằng bạn phải kiểm tra mã của mình để chắc chắn rằng nó là chính xác và dễ nhất là kiểm tra thuật toán vũ lực, nhưng sau đó bạn có thể sử dụng lực lượng vũ phu ngay từ đầu. Mặt khác, việc áp dụng toán học cuối cùng sẽ khiến cho việc chạy bất kỳ mã nào trở nên không cần thiết.
idclev 463035818

1
@molbdnilo Ồ, một trong những ý kiến ​​cho rằng một xor b = a + b mod 2 và tôi nghĩ nó cũng đề cập đến số nguyên. Tôi sẽ loại bỏ một phần của bài viết của tôi.
AAaAa

1
@JohnKugelman Ông có nghĩa mod 2như trong toán học (mod 2), tức là 3 === 7 (mod 2). Vấn đề là bạn có thể khám phá một phương trình cho bit đầu tiên của X, sau đó chuyển sang bit tiếp theo trong đó (tôn trọng thực hiện) bạn có được một phương trình cho bit thứ hai, v.v., như câu trả lời của Daniel.
Max Langhof

Câu trả lời:


45

Lưu ý rằng A + X == (A xor X) + ((A and X)<<1). Vì thế:

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

Và chúng ta có:

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

Nếu điều kiện được đáp ứng, đối với bất kỳ số nguyên Y nào không có bit được đặt trong A, (((A - B) >> 1) hoặc Y) là một giải pháp. Nếu bạn chỉ muốn một giải pháp, bạn có thể sử dụng ((A - B) >> 1), trong đó Y = 0. Nếu không thì không có giải pháp.

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

15
+1. Điều này là bằng cách lưu ý rằng A xor X"bổ sung mà không mang theo" và ((A and X)<<1)là "thực hiện bổ sung". Vì A + Xlà "thêm với mang", phương trình đầu tiên có ý nghĩa.
ngay

3
(A and X)<<1về cơ bản 2*(A and X)và bởi vì điều này tương đương với A-Bnó nói rằng vấn đề có thể có giải pháp chỉ khi A và B là số lẻ hoặc cả hai sự kiện.
axiac

1
Tôi nghĩ rằng nó sẽ có cái gì đó để làm với phép trừ nhưng tôi đã không đến lúc này.
SS Anne

38

Đó là không phải là rất khó khăn, bạn chỉ cần phải suy nghĩ nhỏ: giả sử chúng ta đang viết A, BXtrong hệ nhị phân và Aᵢlà giá trị tương ứng với 2 bìa phải bit.

Chúng tôi biết rằng : Aₒ ⊕ Xₒ = Bₒ + Xₒ.

Hãy sử dụng một ví dụ để khám phá cách đánh giá rằng: A = 15 và B = 6. Chuyển đổi thành nhị phân:

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

Bây giờ chúng tôi có một số khả năng. Hãy phân tích các bit ngoài cùng bên phải của A và B:

1  d = 0 + d

Chúng tôi biết rằng dchỉ có thể là 0 hoặc 1, vì vậy:

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)

Điều đáng chú ý là XOR hoạt động giống như tổng nhị phân (với sự khác biệt là XOR không tạo ra sự chuyển giao cho tổng bit tiếp theo):

    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

vì vậy sẽ không bao giờ có thể tìm thấy một X thỏa mãn A ⊕ X = B + X, bởi vì không có giá trị dnào thỏa mãn 1 + d = 0 + d.

Dù sao, nếu X tồn tại, bạn có thể tìm ra nó theo cách này, từ phải sang trái, tìm từng chút một.


VÍ DỤ HOÀN TOÀN

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 

Ở đây, cả d = 0 và d = 1 đều áp dụng, thì sao? Chúng ta cần kiểm tra bit tiếp theo. Giả sử 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)

vì vậy trong trường hợp này, d phải bằng 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

Nhưng còn b? chúng ta cần kiểm tra bit tiếp theo, như mọi khi:

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)

và bây giờ, cho 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

ở đây acó thể là 0 và 1, nhưng nó phải là 0, để tránh việc chuyển giao trong tổng B + X.

Sau đó, X = 0 1 0 0do đó 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;
}

Bạn có thể kiểm tra nó ở đây .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.