อัลกอริทึมเพื่อค้นหาวิธีแก้ปัญหาสำหรับ 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 นั่นคือถ้าaและbเป็นตัวแปรบูลีนเช่น 0 หรือ 1 และxorเป็น Boolean xor การเชื่อมต่อกับ bitwise xor คืออะไร?
John Kugelman

1
fwiw ฉันคิดว่าการใช้กำลังดุร้ายนี่คือหนทางไปเว้นแต่คุณต้องการเขียนสิ่งที่สามารถพิสูจน์สมการทั่วไปมากขึ้น พิจารณาว่าคุณต้องทดสอบโค้ดของคุณเพื่อให้แน่ใจว่าถูกต้องและวิธีที่ง่ายที่สุดคือการทดสอบกับอัลกอริทึมแรงเดรัจฉาน แต่จากนั้นคุณสามารถใช้แรงเดรัจฉานได้ตั้งแต่แรก ในทางกลับกันการใช้คณิตศาสตร์ในที่สุดจะทำให้ไม่จำเป็นต้องเรียกใช้รหัส
idclev 463035818

1
@ Molbdnilo โอ้หนึ่งในความคิดเห็นที่แนะนำว่า xor b = a + b mod 2 และฉันคิดว่ามันยังหมายถึงจำนวนเต็ม ฉันจะลบส่วนนั้นของโพสต์ของฉัน
AAaAa

1
@JohnKugelman เขาหมายถึงmod 2เช่นเดียวกับในทางคณิตศาสตร์ (mod 2) คือ 3 === 7 (mod 2) ประเด็นก็คือคุณสามารถค้นพบสมการสำหรับบิตแรกของ X จากนั้นไปยังบิตถัดไปที่ (เทียบกับกระเป๋าถือ) คุณได้รับสมการสำหรับบิตที่สอง ฯลฯ เช่นคำตอบของ Daniel
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

หากตรงตามเงื่อนไขสำหรับจำนวนเต็ม Y ใด ๆ ที่ไม่มีบิตที่ตั้งค่าใน A, (((A - B) >> 1) หรือ Y) เป็นวิธีแก้ปัญหา หากคุณต้องการวิธีแก้ปัญหาเพียงวิธีเดียวคุณสามารถใช้ ((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เป็นการ "เติมด้วยการพกพา" สมการแรกจึงสมเหตุสมผล
justhalf

3
(A and X)<<1เป็นพื้น2*(A and X)และเพราะนี่เท่ากับA-Bว่ามันบอกว่าปัญหาอาจมีทางออกเฉพาะถ้า A และ B เป็นเหตุการณ์ที่แปลกหรือทั้งสองอย่าง
axiac

1
ฉันคิดว่ามันจะมีบางอย่างเกี่ยวกับการลบ แต่ฉันไม่ได้มาถึงเรื่องนี้ในเวลา
SS Anne

38

มันไม่ได้เป็นเรื่องยากมากที่คุณเพียงแค่ต้องคิดเล็ก: สมมติว่าเรากำลังเขียนA, BและXในไบนารีและAᵢคุ้มค่าที่สอดคล้องกับขวาสุด 2 บิต

เรารู้ว่า: 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 ทำงานเหมือนกับผลรวมเลขฐานสอง (ด้วยความแตกต่างที่ 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

แต่แล้วขล่ะ เราจำเป็นต้องตรวจสอบบิตถัดไปเช่นเคย:

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.