Алгоритм пошуку рішення для A xor X = B + X


46

Давши цілі числа A і B, знайдіть ціле число X так, щоб:

  • А, В <2 * 1е18
  • 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 - булевий xor. Який зв’язок з розрядним xor?
Джон Кугельман

1
fwiw, я думаю, що тут застосовується груба сила, якщо ви не хочете написати щось, що може довести більш загальні рівняння. Вважайте, що вам слід перевірити свій код, щоб бути впевненим, що він правильний, і найпростіше було б протестувати проти алгоритму грубої сили, але тоді ви можете в першу чергу використовувати грубу силу. З іншого боку, застосування математики з часом зробить непотрібним запуск будь-якого коду.
idclev 463035818

1
@molbdnilo О, один із коментарів припустив, що xor b = a + b mod 2, і я вважав, що це також стосується цілих чисел. Я видалю цю частину своєї публікації.
AAaAa

1
@JohnKugelman Він мав на увазі mod 2як у математичному (мод 2), тобто 3 === 7 (мод 2). Сенс у тому, що ви можете виявити рівняння для першого біта X, а потім перейти до наступного біта, де (поважаючи перенесення) ви отримаєте рівняння для другого біта тощо, як відповідь Даніеля.
Макс Ленгоф

Відповіді:


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 є непарними або обома подіями.
аксіак

1
Я думав, що це має щось спільне з відніманням, але я цього не прийшов вчасно.
СС Енн

38

Це не дуже важко, потрібно просто думати малим: припустимо, ми пишемо A, Bі Xв двійковому, і Aᵢце значення, відповідне правому 2 right біту.

Ми знаємо , що: 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

але що з 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;
}

Ви можете протестувати його тут .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.