Somando-se à ótima resposta de FatalError, a linha return f(b)^f(a-1);
poderia ser explicada melhor. Resumindo, é porque o XOR tem essas propriedades maravilhosas:
- É associativo - coloque colchetes onde quiser
- É comutativo - isso significa que você pode movimentar os operadores (eles podem "se deslocar")
Ambos estão em ação:
(a ^ b ^ c) ^ (d ^ e ^ f) = (f ^ e) ^ (d ^ a ^ b) ^ c
Como isso:
a ^ b = c
c ^ a = b
Adicionar e multiplicar são dois exemplos de outros operadores associativos / comutativos, mas não se revertem. Ok, então, por que essas propriedades são importantes? Bem, um caminho simples é expandi-lo para o que realmente é, e então você poderá ver essas propriedades em funcionamento.
Primeiro, vamos definir o que queremos e chamá-lo de n:
n = (a ^ a+1 ^ a+2 .. ^ b)
Se ajudar, pense em XOR (^) como se fosse um acréscimo.
Vamos também definir a função:
f(b) = 0 ^ 1 ^ 2 ^ 3 ^ 4 .. ^ b
b
é maior que a
, então, apenas colocando com segurança alguns colchetes extras (o que podemos porque é associativo), também podemos dizer o seguinte:
f(b) = ( 0 ^ 1 ^ 2 ^ 3 ^ 4 .. ^ (a-1) ) ^ (a ^ a+1 ^ a+2 .. ^ b)
O que simplifica para:
f(b) = f(a-1) ^ (a ^ a+1 ^ a+2 .. ^ b)
f(b) = f(a-1) ^ n
Em seguida, usamos essa propriedade de reversão e comutividade para nos dar a linha mágica:
n = f(b) ^ f(a-1)
Se você tem pensado em XOR como um acréscimo, você teria adicionado um subtrato ali. XOR é para XOR o que adicionar é subtrair!
Como faço isso sozinho?
Lembre-se das propriedades dos operadores lógicos. Trabalhe com eles quase como adicionar ou multiplicar se ajudar. Parece incomum que and (&), xor (^) e or (|) sejam associativos, mas são!
Execute primeiro a implementação ingênua, procure padrões na saída e, em seguida, comece a encontrar regras que confirmem que o padrão é verdadeiro. Simplifique sua implementação ainda mais e repita. Este é provavelmente o caminho que o criador original seguiu, destacado pelo fato de que não é totalmente ideal (ou seja, use uma instrução switch em vez de um array).