Aggiungendo alla grande risposta di FatalError, la linea return f(b)^f(a-1);
potrebbe essere spiegata meglio. In breve, è perché XOR ha queste meravigliose proprietà:
- È associativo : posiziona le parentesi dove vuoi
- È commutativo : ciò significa che puoi spostare gli operatori (possono "spostarsi")
Ecco entrambi in azione:
(a ^ b ^ c) ^ (d ^ e ^ f) = (f ^ e) ^ (d ^ a ^ b) ^ c
Come questo:
a ^ b = c
c ^ a = b
Somma e moltiplica sono due esempi di altri operatori associativi / commutativi, ma non si invertono. Ok, allora, perché queste proprietà sono importanti? Bene, un percorso semplice è espanderlo in quello che è realmente, e poi puoi vedere queste proprietà all'opera.
Per prima cosa, definiamo ciò che vogliamo e chiamiamolo n:
n = (a ^ a+1 ^ a+2 .. ^ b)
Se aiuta, pensa a XOR (^) come se fosse un'aggiunta.
Definiamo anche la funzione:
f(b) = 0 ^ 1 ^ 2 ^ 3 ^ 4 .. ^ b
b
è maggiore di a
, quindi semplicemente inserendo in modo sicuro alcune parentesi extra (cosa che possiamo fare perché è associativa), possiamo anche dire questo:
f(b) = ( 0 ^ 1 ^ 2 ^ 3 ^ 4 .. ^ (a-1) ) ^ (a ^ a+1 ^ a+2 .. ^ b)
Che si semplifica a:
f(b) = f(a-1) ^ (a ^ a+1 ^ a+2 .. ^ b)
f(b) = f(a-1) ^ n
Successivamente, usiamo la proprietà di inversione e la commutività per darci la linea magica:
n = f(b) ^ f(a-1)
Se stavi pensando a XOR come un add, avresti fatto cadere una sottrazione lì. XOR sta a XOR ciò che aggiungere è sottrarre!
Come posso arrivare a questo da solo?
Ricorda le proprietà degli operatori logici. Lavora con loro quasi come aggiungere o moltiplicare se aiuta. Sembra insolito che and (&), xor (^) e or (|) siano associativi, ma lo sono!
Esegui prima l'implementazione ingenua, cerca i modelli nell'output, quindi inizia a trovare le regole che confermano che il modello è vero. Semplifica ulteriormente la tua implementazione e ripeti. Questo è probabilmente il percorso intrapreso dal creatore originale, evidenziato dal fatto che non è del tutto ottimale (cioè usa un'istruzione switch piuttosto che un array).