Algoritmo para encontrar uma solução para A xor X = B + X

46

Dado o número inteiro A e B, encontre o número X para que:

  • A, B <2 * 1e18
  • A xou X = B + X

Eu duvido que seja possível resolver essa equação usando matemática. Este é um problema de codificação que me deparei há 3 anos e, mesmo agora, não consigo resolver isso sozinho.

Meu código até agora: (esta é a solução de força bruta)

#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;
}
AAaAa
fonte
11
Se você ler um pouco mais sobre exclusividade ou deve encontrar a equivalência algébrica a xor b = a + b mod 2. Tente pensar nessa equivalência por um tempo.
Algum programador,
16
@Someprogrammerdude Isto é, se um e b são variáveis booleanas, ou seja, 0 ou 1, e XOR é um xor booleana. Qual é a conexão com o bitor xor?
John Kugelman
11
Acho que usar a força bruta aqui é o caminho a menos, a menos que você queira escrever algo que possa provar equações mais gerais. Considere que você deve testar seu código para ter certeza de que ele está correto e o mais fácil seria testar um algoritmo de força bruta, mas, em primeiro lugar, você pode usar a força bruta. Por outro lado, aplicar matemática acabará tornando desnecessário executar qualquer código.
idclev 463035818 31/03
11
@molbdnilo Oh, um dos comentários sugeriu que a xor b = a + b mod 2 e eu pensei que também se referia a números inteiros. Vou remover essa parte da minha postagem.
AAaAa 31/03
11
@JohnKugelman Ele quis dizer o mod 2que na matemática (mod 2), ou seja, 3 === 7 (mod 2). O ponto é que você pode descobrir uma equação para o primeiro bit de X e depois passar para o próximo bit, onde (respeitando o carry), obtém uma equação para o segundo bit, etc., como a resposta de Daniel.
Max Langhof

Respostas:

45

Note isso A + X == (A xor X) + ((A and X)<<1). Assim:

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

E nós temos:

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

Se a condição for atendida, para qualquer número inteiro Y que não tenha bits definidos em A, (((A - B) >> 1) ou Y) é uma solução. Se você quiser apenas uma solução, poderá usar ((A - B) >> 1), onde Y = 0. Caso contrário, não há solução.

int solve(int a, int b){
    int x = (a - b) >> 1;
    if ((a ^ x) == b + x)
        return x;
    else
        return ERROR;
}
user23013
fonte
15
+1. Isso ocorre observando que A xor Xé "adição sem transporte" e ((A and X)<<1)"transporte na adição". Como A + Xé "adição com transporte", a primeira equação faz sentido.
justhalf
3
(A and X)<<1é basicamente 2*(A and X)e, como isso é igual, A-Bele diz que o problema pode ter uma solução somente se A e B forem ímpares ou ambos os eventos.
axiac
11
Eu pensei que teria algo a ver com subtração, mas não cheguei a isso a tempo.
SS Anne
38

Não é muito difícil, você só precisa pensar pequeno: suponha que estamos escrevendo A, Be Xem binário e Aᵢé o valor correspondente à mais à direita 2 bit.

Sabemos que: Aₒ ⊕ Xₒ = Bₒ + Xₒ.

Vamos usar um exemplo para descobrir como avaliar isso: A = 15 e B = 6. Convertendo em binário:

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

Agora temos algumas possibilidades. Vamos analisar os bits mais à direita de A e B:

1  d = 0 + d

Sabemos que dsó pode ser 0 ou 1, então:

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)

É notável que o XOR se comporta exatamente como a soma binária (com a diferença de que o XOR não cria uma transição para a próxima soma de bits):

    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

portanto, nem sempre é possível encontrar um X que satisfaça A ⊕ X = B + X, porque não há um valor dque satisfaça 1 + d = 0 + d.

De qualquer forma, se o X existe, você pode descobrir dessa maneira, da direita para a esquerda, encontrando pouco a pouco.


EXEMPLO COMPLETO DE TRABALHO

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 

Aqui, tanto d = 0 ed = 1 se aplicam, então o que? Precisamos verificar o próximo bit. Suponha que 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)

portanto, nesse caso, d deve ser 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

mas e b? precisamos verificar o próximo bit, como sempre:

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)

e agora, para 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

aqui apode ser 0 e 1, mas deve ser 0, para evitar uma transição na soma B + X.

Então, X = 0 1 0 0então X = 4.


CÓDIGO

#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;
}

Você pode testá-lo aqui .

Daniel
fonte