Добавив к отличному ответу FatalError, эту строку return f(b)^f(a-1);
можно было бы лучше объяснить. Короче говоря, это потому, что XOR обладает этими замечательными свойствами:
- Это ассоциативно - размещайте скобки, где хотите
- Он коммутативен - это означает, что вы можете перемещать операторов (они могут «коммутировать»)
Вот оба в действии:
(a ^ b ^ c) ^ (d ^ e ^ f) = (f ^ e) ^ (d ^ a ^ b) ^ c
Как это:
a ^ b = c
c ^ a = b
Сложение и умножение - это два примера других ассоциативных / коммутативных операторов, но они не меняют свое значение. Итак, почему эти свойства важны? Что ж, простой способ - расширить его до того, чем он является на самом деле, и тогда вы сможете увидеть эти свойства в действии.
Во-первых, давайте определим, что мы хотим, и назовем это n:
n = (a ^ a+1 ^ a+2 .. ^ b)
Если это поможет, подумайте о XOR (^), как о добавлении.
Также определим функцию:
f(b) = 0 ^ 1 ^ 2 ^ 3 ^ 4 .. ^ b
b
больше чем a
, поэтому, просто поставив несколько дополнительных скобок (что мы можем сделать, потому что это ассоциативно), мы также можем сказать следующее:
f(b) = ( 0 ^ 1 ^ 2 ^ 3 ^ 4 .. ^ (a-1) ) ^ (a ^ a+1 ^ a+2 .. ^ b)
Что упрощает:
f(b) = f(a-1) ^ (a ^ a+1 ^ a+2 .. ^ b)
f(b) = f(a-1) ^ n
Затем мы используем это свойство разворота и коммуникативность, чтобы дать нам волшебную строку:
n = f(b) ^ f(a-1)
Если бы вы думали о XOR как о сложении, вы бы добавили здесь вычитание. XOR - это XOR, а добавление - вычитание!
Как я сам это придумал?
Помните о свойствах логических операторов. Работайте с ними почти как сложение или умножение, если это помогает. Кажется необычным, что и (&), xor (^) и или (|) ассоциативны, но это так!
Сначала запустите наивную реализацию, поищите шаблоны в выходных данных, а затем начните находить правила, подтверждающие, что шаблон верен. Еще больше упростите реализацию и повторите. Вероятно, это путь, по которому пошел первоначальный создатель, подчеркнутый тем фактом, что он не полностью оптимален (т.е. использовать оператор switch, а не массив).