Алгоритм обмена XOR - XOR swap algorithm

С помощью трех операций XOR двоичные значения 1010 и 0011 обмениваются между переменными.
Использование алгоритма обмена XOR для обмена грызет между переменными без использования временного хранилища

В компьютерное программирование, то Замена XOR является алгоритм который использует XOR побитовая операция к своп ценности различных переменные имея такой же тип данных без использования временной переменной. «Различный» означает, что переменные хранятся по разным, непересекающимся адресам памяти, поскольку алгоритм установит единичное значение с псевдонимом в ноль; фактические значения переменных не должны отличаться.

Алгоритм

Обычная замена требует использования переменной временного хранения. Однако при использовании алгоритма подкачки XOR временное хранилище не требуется. Алгоритм следующий:[1][2]

Икс := Икс XOR YY := Y XOR ИксИкс := Икс XOR Y

Алгоритм обычно соответствует трем Машинный код инструкции. Поскольку XOR - это коммутативная операция, X XOR Y можно заменить на Y XOR X в любой из строк. При написании кода на ассемблере эта коммутативность часто проявляется во второй строке:

ПсевдокодIBM Система / 370 сборкасборка x86
Икс := Икс XOR YXR R1,R2xor eax, ebx
Y := Y XOR ИксXR R2,R1xor ebx, eax
Икс := Икс XOR YXR R1,R2xor eax, ebx

В приведенном выше примере кода сборки System / 370 R1 и R2 различны. регистры, и каждый XR операция оставляет свой результат в регистре, указанном в первом аргументе. При использовании сборки x86 значения X и Y находятся в регистрах eax и ebx (соответственно), а xor помещает результат операции в первый регистр.

Однако алгоритм не работает, если Икс и y используйте то же место хранения, поскольку значение, хранящееся в этом месте, будет обнулено первой инструкцией XOR, а затем останется нулевым; он не будет «заменен самим собой». Это не так же, как если бы Икс и y имеют одинаковые значения. Беда приходит только тогда, когда Икс и y использовать одно и то же место хранения, и в этом случае их значения уже должны быть равны. То есть, если Икс и y используйте то же место хранения, затем строку:

Икс := Икс XOR Y

наборы Икс к нулю (потому что Икс = y поэтому X XOR Y равен нулю) и наборы y до нуля (поскольку он использует то же место хранения), что вызывает Икс и y потерять свои первоначальные ценности.

Доказательство правильности

В бинарная операция XOR над битовыми строками длины проявляет следующие свойства (где обозначает XOR):[а]

Предположим, что у нас есть два разных регистра R1 и R2 как в таблице ниже, с начальными значениями А и B соответственно. Мы последовательно выполняем указанные ниже операции и уменьшаем наши результаты, используя свойства, перечисленные выше.

ШагОперацияРегистр 1Регистр 2Сокращение
0Первоначальный значение
1R1: = R1 исключающее ИЛИ R2
2R2: = R1 исключающее ИЛИ R2

L2
L4
L3
3R1: = R1 исключающее ИЛИ R2


L1
L2
L4
L3

Интерпретация линейной алгебры

Поскольку XOR можно интерпретировать как двоичное сложение, а пару битов можно интерпретировать как вектор в двумерном векторное пространство над поле с двумя элементами, шаги в алгоритме можно интерпретировать как умножение на матрицы 2 × 2 над полем с двумя элементами. Для простоты предположим изначально, что Икс и y являются отдельными битами, а не битовыми векторами.

Например, шаг:

Икс := Икс XOR Y

который также имеет неявное:

Y := Y

соответствует матрице так как

Последовательность операций тогда выражается как:

(работает с двоичными значениями, поэтому ), что выражает элементарная матрица переключения двух строк (или столбцов) с точки зрения трансвекция (ножницы) добавления одного элемента к другому.

Чтобы обобщить, где X и Y не одиночные биты, а вместо этого битовые векторы длины п, эти 2 × 2 матрицы заменяются на 2п×2п блочные матрицы такие как

Эти матрицы работают на ценности, не на переменные (с местами хранения), поэтому эта интерпретация абстрагируется от проблем с местом хранения и от проблемы того, что обе переменные используют одно и то же место хранения.

Пример кода

А C функция, реализующая алгоритм обмена XOR:

пустота XorSwap(int *Икс, int *y) {  если (Икс != y)  {    *Икс ^= *y;    *y ^= *Икс;    *Икс ^= *y;  }}

Код сначала проверяет, различны ли адреса. В противном случае, если бы они были равны, алгоритм свернулся бы до тройки * x ^ = * x, что привело бы к нулю.

Алгоритм обмена XOR также можно определить с помощью макроса:

#define XORSWAP_UNSAFE (a, b)   ((а) ^ = (б), (б) ^ = (а),    (а) ^ = (б)) / * Не работает, если a и b - один и тот же объект - присваивает ноль                   (0) к объекту в этом случае * /#define XORSWAP (a, b)   ((& (a) == & (b))? (a) / * Проверить отдельные адреса * /                    \                  : XORSWAP_UNSAFE (а, б))

Причины использования на практике

В большинстве практических сценариев более эффективен простой алгоритм обмена с использованием временного регистра. Ограниченные ситуации, в которых замена XOR может быть практичной, включают:

  • на процессоре, где кодирование набора команд позволяет кодировать замену XOR в меньшем количестве байтов
  • в регионе с высоким регистрировать давление, это может позволить распределитель регистров избежать проливание реестра
  • в микроконтроллеры где доступная оперативная память очень ограничена.
  • в криптографических приложениях, которым требуются функции постоянного времени для предотвращения атак по побочным каналам, основанным на времени[3]

Поскольку эти ситуации редки, большинство оптимизирующих компиляторов не генерируют код подкачки XOR.

Причины отказа на практике

Большинство современных компиляторов могут оптимизировать временную переменную в трехстороннем свопе, и в этом случае он будет использовать тот же объем памяти и такое же количество регистров, что и своп XOR, и будет по крайней мере так же быстро, а часто и быстрее. В дополнение к этому, замена XOR полностью непрозрачна для всех, кто не знаком с этой техникой.

На современном Архитектура ЦП, метод XOR может быть медленнее, чем использование временной переменной для подкачки. По крайней мере, на последних процессорах x86, как AMD, так и Intel, перемещение между регистрами регулярно приводит к нулевой задержке. (Это называется устранением MOV.) Даже если нет доступных для использования архитектурных регистров, XCHG инструкция будет как минимум такой же быстрой, как три XOR вместе взятые. Другая причина заключается в том, что современные процессоры стремятся выполнять инструкции параллельно через конвейеры команд. В технике XOR входные данные для каждой операции зависят от результатов предыдущей операции, поэтому они должны выполняться в строго последовательном порядке, сводя на нет любые преимущества параллелизм на уровне инструкций.[4]

Сглаживание

Замена XOR также усложняется на практике из-за сглаживание. Если предпринята попытка выполнить XOR-замену содержимого некоторого местоположения с самим собой, результатом будет то, что местоположение будет обнулено и его значение потеряно. Следовательно, замена XOR не должна использоваться вслепую на языке высокого уровня, если возможно использование псевдонимов.

Подобные проблемы возникают с позвонить по имени, как в Устройство Дженсена, где обмен я и A [i] через временную переменную дает неверные результаты из-за связанных аргументов: замена через temp = i; я = А [я]; A [i] = temp изменяет значение для я во втором операторе, что затем приводит к неправильному значению i для A [i] в третьем заявлении.

Вариации

Базовый принцип алгоритма обмена XOR может быть применен к любой операции, удовлетворяющей критериям от L1 до L4 выше. Замена XOR сложением и вычитанием дает немного иную, но в значительной степени эквивалентную формулировку:

пустота AddSwap( беззнаковый int* Икс, беззнаковый int* y ){  если (Икс != y)  {    *Икс = *Икс + *y;    *y = *Икс - *y;    *Икс = *Икс - *y;  }}

В отличие от обмена XOR, этот вариант требует, чтобы базовый процессор или язык программирования использовал такой метод, как модульная арифметика или бигнумы чтобы гарантировать, что вычисление X + Y не может вызвать ошибку из-за целочисленное переполнение. Поэтому на практике он встречается даже реже, чем своп XOR.

Однако реализация AddSwap выше в языке программирования C всегда работает даже в случае целочисленного переполнения, поскольку, согласно стандарту C, сложение и вычитание целых чисел без знака следуют правилам модульная арифметика, я. е. сделаны в циклическая группа где это количество битов беззнаковое целое. Действительно, корректность алгоритма следует из того, что формулы и держать в любом абелева группа. На самом деле это обобщение доказательства для алгоритма обмена XOR: XOR - это как сложение, так и вычитание в абелевой группе (какой прямая сумма из s копии ).

Этого не происходит при работе с подписанный int тип (по умолчанию для int). Знаковое целочисленное переполнение является неопределенным поведением в C, и поэтому модульная арифметика не гарантируется стандартом (соответствующий стандарту компилятор может оптимизировать такой код, что приведет к неверным результатам).

Смотрите также

Заметки

  1. ^ Первые три свойства, наряду с существованием инверсии для каждого элемента, являются определением абелева группа. Последнее свойство - утверждение, что каждый элемент является инволюция, то есть имея порядок 2, что верно не для всех абелевых групп.

использованная литература

  1. ^ "Магия XOR". Cs.umd.edu. Получено 2014-04-02.
  2. ^ «Замена значений с помощью XOR». graphics.stanford.edu. Получено 2014-05-02.
  3. ^ Брюс Шнайер, Тадаёши Коно, Нильс Фергюсон (2010). Криптографическая инженерия: принципы проектирования и практическое применение. Индианаполис, ИН: Wiley Pub., Inc. п. 251 сл. ISBN  978-0-470-47424-2.CS1 maint: лишняя пунктуация (ссылка на сайт)
  4. ^ Амарасингхе, Саман; Лейзерсон, Чарльз (2010). «6.172 Инженерия производительности программных систем, лекция 2». MIT OpenCourseWare. Массачусетский Институт Технологий. Получено 27 января 2015.