Найти XOR всех чисел в заданном диапазоне
вам дается большой диапазон [a, b], где 'a' и 'b' обычно могут быть от 1 до 4,000,000,000 включительно. Вы должны узнать XOR всех чисел в данном диапазоне.
эта проблема была использована в TopCoder SRM. Я видел одно из решений, представленных в матче, и я не могу понять, как он работает.
может кто - нибудь помочь объяснить выигрышное решение:
long long f(long long a) {
long long res[] = {a,1,a+1,0};
return res[a%4];
}
long long getXor(long long a, long long b) {
return f(b)^f(a-1);
}
здесь getXor() является фактической функцией для вычисления xor все числа в переданном диапазоне [a, b] и "f()" является вспомогательной функцией.
4 ответов:
Это довольно умное решение-он использует тот факт, что есть шаблон результатов в запущенных XORs. Элемент
f()функция вычисляет общий запуск XOR из [0, a]. Взгляните на эту таблицу для 4-разрядных чисел:0000 <- 0 [a] 0001 <- 1 [1] 0010 <- 3 [a+1] 0011 <- 0 [0] 0100 <- 4 [a] 0101 <- 1 [1] 0110 <- 7 [a+1] 0111 <- 0 [0] 1000 <- 8 [a] 1001 <- 1 [1] 1010 <- 11 [a+1] 1011 <- 0 [0] 1100 <- 12 [a] 1101 <- 1 [1] 1110 <- 15 [a+1] 1111 <- 0 [0]где первый столбец-это двоичное представление, а затем десятичный результат и его отношение к его индексу (a) в списке XOR. Это происходит потому, что все верхние биты отменяются, а самые низкие два бита циклически повторяются каждые 4. Итак, вот как приходите к этой маленькой таблице поиска.
теперь рассмотрим для общего диапазона [a, b]. Мы можем использовать
f()чтобы найти XOR для [0,a-1] и [0, b]. Так как любое значение XOR'D с собой равно нулю, тоf(a-1)просто отменяет все значения в XOR выполнить меньше, чемa, оставляя вас с XOR диапазона [a, b].
добавление к большому ответу 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 как о добавлении, вы я бы там зашел в вычитание. Гаммирования заключается в гаммирования, что добавить, убавить!
как мне самому это придумать?
запомните свойства логических операторов. Работать с ними почти как добавить или умножить, если это помогает. Он чувствует себя необычно, что и (&), исключающее ИЛИ (^) и или (|) являются ассоциативными, но они есть!
сначала запустите наивную реализацию, найдите шаблоны в выводе, а затем начните находить правила, которые подтверждают закономерность верна. Упростите свою реализацию еще больше и повторите. это, вероятно, маршрут, который взял оригинальный создатель, выделенный тем фактом, что он не полностью оптимален (т. е. используйте оператор switch, а не массив).
я узнал, что приведенный ниже код также работает как решение, приведенное в вопросе.
может быть, это немного оптимизировано, но это именно то, что я получил от наблюдения повторения, как указано в принятом ответе,
Я хотел бы знать / понимать математическое доказательство данного кода, как описано в ответе @Luke Briggs
вот этот JAVA-код
public int findXORofRange(int m, int n) { int[] patternTracker; if(m % 2 == 0) patternTracker = new int[] {n, 1, n^1, 0}; else patternTracker = new int[] {m, m^n, m-1, (m-1)^n}; return patternTracker[(n-m) % 4]; }
Я решил проблему с помощью рекурсии. Я просто делю набор данных на почти равную часть для каждой итерации.
public int recursion(int M, int N) { if (N - M == 1) { return M ^ N; } else { int pivot = this.calculatePivot(M, N); if (pivot + 1 == N) { return this.recursion(M, pivot) ^ N; } else { return this.recursion(M, pivot) ^ this.recursion(pivot + 1, N); } } } public int calculatePivot(int M, int N) { return (M + N) / 2; }позвольте мне знать ваши мысли над решением. Рад получить обратную связь по улучшению. Предложенное решение вычисляет XOR в 0 (log N) сложности.
спасибо
Comments