Как поменять местами два числа без использования временных переменных или арифметических операций?

Это уравнение меняет местами два числа без временной переменной, но использует арифметические операции:

a = (a+b) - (b=a);

Как обойтись без арифметических операций? Я думал о XOR.


person Vishwanath Dalvi    schedule 05.09.2010    source источник
comment
Зачем тебе это нужно?   -  person Mark Byers    schedule 05.09.2010
comment
прочтите эту PRB в 1 книге .. вот и все. как поменять местами без арифметических операторов   -  person Vishwanath Dalvi    schedule 05.09.2010
comment
@ Извините, ребята, я редактировал свой вопрос ... без температуры ... и без арифметического оператора   -  person Vishwanath Dalvi    schedule 05.09.2010
comment
@mr_eclair: 1) пожалуйста, объясните свои слова, это намного удобнее. 2) просмотрите статью Оли, размещенную ниже, и выясните, почему вы не хотите делать такой обмен на практике.   -  person Michael Petrotta    schedule 05.09.2010
comment
Между прочим, поведение этого кода не определено. И a, и b читаются и записываются без промежуточной точки последовательности. Во-первых, компилятор имеет право оценивать b=a перед тем, как оценивать a+b.   -  person Steve Jessop    schedule 05.09.2010
comment
почему отрицательные голоса за вопрос, ребята? он спросил это из любопытства. Это был хороший вопрос, и он позволил многим из нас узнать об этом методе, а также о его недостатках. Да ладно, ребята, что с вами? один голос, чтобы выровнять.   -  person Sandeepan Nath    schedule 05.09.2010
comment
@sandeepan: Я не голосовал против, но не буду голосовать за. Код, который он утверждает, будет менять значения переменных, сломан. Наивные люди наткнутся на этот пост, увидят код и с радостью применит его в своей работе. Проголосование против - это способ предупредить Дж. Кодера, что, возможно, это плохая идея.   -  person Michael Petrotta    schedule 05.09.2010
comment
@Michael Petrotta, возможно, люди не должны копировать / вставлять код из любого вопроса, заданного на SO, не прочитав сначала тему ... И сообщение OP не содержит кода.   -  person Colin Hebert    schedule 05.09.2010
comment
@Colin: странный комментарий. Люди будут публиковать код, который они видят в сети, вот что они делают. Давайте поможем им не прострелить себе ногу, вот для чего мы здесь. И сообщение OP действительно содержит код, вы его читали?   -  person Michael Petrotta    schedule 05.09.2010
comment
@ Майкл, я понимаю это, и поэтому я думаю, что для этого достаточно приложить предостережения, как это сделал Оли Чарльзуорт в своем ответе. Никто не всегда может защитить этих людей, которые просто копируют и вставляют код , не осознавая плюсов и минусов.   -  person Sandeepan Nath    schedule 05.09.2010
comment
@Michael, OP заявляет, что это уравнение, а не утверждение, но вы правы, точка с запятой может указывать на классический оператор (который не сработает)   -  person Colin Hebert    schedule 05.09.2010
comment
Используемый в вопросе арифметический метод неверен; он не работает на языках, для которых отмечен этот вопрос (C, C ++, ObjC).   -  person R.. GitHub STOP HELPING ICE    schedule 05.09.2010
comment
-1 за бесполезный вопрос. Вы получили ответ XOR, а теперь вы комментируете, что этого недостаточно. Конечно, это не так, это тупой трюк с XOR. Используйте временную переменную и покончите с этим. Если у вас есть реальная проблема или реальный вопрос, задайте их. Голосование закрыто.   -  person GManNickG    schedule 05.09.2010
comment
Чтобы было до боли ясно, что говорят R и Майкл: приведенный выше код вызывает неопределенное поведение, потому что нет точки последовательности между двумя использованиями b. Нельзя полагаться на то, как компилятор отображает эту строку (если он вообще ее отображает).   -  person dmckee --- ex-moderator kitten    schedule 06.09.2010
comment
В дополнение к наблюдениям выше; временное (a + b) оказывается безымянным.   -  person MSalters    schedule 06.09.2010
comment
Вот оно [введите здесь описание ссылки] [1] [1]: stackoverflow.com/a/17297099/1079945   -  person DareDevil    schedule 25.06.2013
comment
@SteveJessop; Да, ты прав. Код вызовет неопределенное поведение.   -  person haccks    schedule 27.12.2013
comment
Я бы предпочел временную переменную для обмена вместо того, чтобы использовать замену XOR или арифметические операции. В основном, если я имею дело с беззнаковым байтом, вероятность получения неправильных ответов высока. Так что лучше избегайте таких методов ...   -  person Sudhakar Chavali    schedule 04.08.2014


Ответы (9)


a=a+b;
b=a-b;
a=a-b;

Это просто, но эффективно ....

person sreejith k s    schedule 23.08.2011
comment
Похоже, что он содержит несколько арифметических операторов. - person Bo Persson; 24.08.2011
comment
Это также подвержено переполнению, в отличие от примера xor. - person Mikhail; 14.11.2012
comment
@Mikhail: Перелив будет в a=a+b. Затем произойдет два недополнения в b=a-b и a=a-b, в результате чего обе переменные правильно меняют местами значения. - person displayName; 03.11.2015
comment
@displayName правильно? Я бы не стал использовать это слово после трех последовательных неопределенных поведений. - person alx; 07.04.2019

В C это должно работать:

a = a^b;
b = a^b;
a = a^b;

ИЛИ круче / увлекательнее на вид:

a^=b;
b^=a;
a^=b;

Дополнительные сведения см. В этом. XOR - очень мощная операция, у которой то тут, то там возникает много интересных применений.

person BiGYaN    schedule 05.09.2010
comment
спасибо за XOR .. bt XOR операция очень медленнее любого другого метода решения этой проблемы? - person Vishwanath Dalvi; 05.09.2010
comment
@mr_eclair: На любой типичной платформе вы не добьетесь большего, чем предложение КенниTM наверху. - person Oliver Charlesworth; 05.09.2010
comment
Чтобы сделать его еще менее читаемым, вы можете использовать оператор запятой: a ^ = b, b ^ = a, a ^ = b; - person Martin York; 05.09.2010
comment
@mr_eclair: Что значит медленнее? Метод - использовать чертову временную переменную. (Используйте std::swap.) Вот и все. В чем смысл этого вопроса? У вас есть актуальная проблема? - person GManNickG; 05.09.2010
comment
Чтобы сделать это еще хуже: a ^= b ^= a ^= b (или, что еще хуже, a^=b^(b=a)) - person Tim; 19.10.2015
comment
@mr_eclair фактически на аппаратном уровне суммирование намного медленнее, чем xor - person Slava; 21.03.2017
comment
На аппаратном уровне операция xor проще, чем сложение или вычитание. Есть веские основания полагать, что это могло быть быстрее. Это было быстрее на старом оборудовании, но производительность на современном оборудовании может быть неотличимой. - person Jeff Grigg; 25.05.2018

Почему бы не использовать стандартные библиотеки?

std::swap(a,b);
person Martin York    schedule 05.09.2010
comment
Я думаю, что спрашивающий хочет найти здесь решение, включающее детали более низкого уровня, а не просто вызов готовой функции / библиотеки. - person Sandeepan Nath; 05.09.2010
comment
@sandeepan: Это не мне или вам интерпретировать суть вопроса. Мы должны ответить на поставленный вопрос, предложив наилучшее решение проблемы (если это не то, что требует OP, мы не получим галочку). Но я считаю, что это лучшее решение задаваемого вопроса (даже OP на самом деле играет с глупыми вопросами типа интервью). - person Martin York; 05.09.2010
comment
@Martin Я не согласен, мы должны хорошо интерпретировать и понимать, прежде чем отвечать, и постоянно думать, правильно ли мы интерпретировали. Этот вопрос не об оптимизированном / лучшем решении, это просто другое решение. - person Sandeepan Nath; 06.09.2010
comment
Что ж, очевидной причиной не использовать эту библиотечную функцию было бы то, что она не работает на 2/3 запрашиваемых языков. - person Chuck; 06.09.2010
comment
@Chunk: 1/3 (он должен работать на объекте C (ну, на объекте C ++)) - person Martin York; 06.09.2010
comment
@sandeeoan: Дело в том, что я понимаю вопрос и то, что он хочет. Но я дал ему то, что ему нужно, а не то, что он хочет, и поэтому он должен стать лучшим разработчиком для этого. - person Martin York; 06.09.2010
comment
@Foo Bah: Верно. Но вопрос помечен как C ++, поэтому они должны ожидать ответов C ++. - person Martin York; 25.08.2011
comment
@Foo Bah: Да. Но что с того. Он помечен как C ++, поэтому вы должны ожидать ответа C ++. Для нескольких языков оптимальным вариантом будет отсутствие ответа. Замена XOR (забавно, как тривиальный глупый пример), но глупо для C / C ++ и объекта C. Конечно, вы можете написать свой собственный swap_tmp (), это нормально для C / objective-C, но глупо для C ++ (поскольку он уже определен с помощью более оптимальная версия на C ++) - person Martin York; 25.08.2011
comment
@Martin, в вашем ответе нет указаний на то, что это специфично для C ++. Например, своп XOR работает везде. Но в стандартной библиотеке C нет эквивалента свопу C ++. - person Foo Bah; 25.08.2011
comment
@Foo Bah: Пример XOR - это бессмысленный пример игрушки, который никогда не будет использоваться в реальной жизни (на любом языке (если вы думаете, что вы можете задать его как вопрос о SO)). Мой ответ - это практическое решение вопроса, который является идеальным ответом для C ++. Нет смысла прямо заявлять, что это C ++, что очевидно из контекста. - person Martin York; 25.08.2011
comment
@Martin не очевидно из контекста, если вы не знаете C и цель C, что решение применимо только к C ++. Было бы полезно, если бы вы написали это прямо. - person Foo Bah; 25.08.2011
comment
@Foo Bah: Если вы не знаете C / Objective-C или C ++, вы не собираетесь читать этот вопрос! - person Martin York; 25.08.2011

Лучший способ поменять местами два числа без использования какого-либо временного хранилища или арифметических операций - это загрузить обе переменные в регистры, а затем использовать регистры наоборот!

Вы не можете сделать это непосредственно из C, но компилятор, вероятно, вполне способен решить это за вас (по крайней мере, если включена оптимизация) - если вы напишете простой, очевидный код, такой как тот, который Кенни предложил в своем комментарии .

e.g.

void swap_tmp(unsigned int *p)
{
  unsigned int tmp;

  tmp = p[0];
  p[0] = p[1];
  p[1] = tmp;
}

скомпилированный с помощью gcc 4.3.2 с флагом оптимизации -O2 дает:

swap_tmp:
        pushl   %ebp               ;  (prologue)
        movl    %esp, %ebp         ;  (prologue)
        movl    8(%ebp), %eax      ; EAX = p
        movl    (%eax), %ecx       ; ECX = p[0]
        movl    4(%eax), %edx      ; EDX = p[1]
        movl    %ecx, 4(%eax)      ; p[1] = ECX
        movl    %edx, (%eax)       ; p[0] = EDX
        popl    %ebp               ;  (epilogue)
        ret                        ;  (epilogue)
person Matthew Slattery    schedule 05.09.2010
comment
Я не думаю, что переменная должна находиться в стеке, чтобы считаться временной переменной. - person SMBiggs; 05.10.2018

Используя XOR,

void swap(int &a, int &b)
{
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

Один лайнер с XOR,

void swap(int &a, int &b)
{
    a ^= b ^= a ^= b;
}

Эти методы кажутся чистыми, потому что они не терпят неудачу ни в одном тестовом примере, но, опять же, поскольку (как и в методе 2) значение переменной изменяется дважды в одной и той же точке последовательности, говорят, что оно имеет неопределенное поведение, объявленное ANSI C.

person Shubham A.    schedule 08.02.2015

Я раньше не видел этого решения C, но уверен, что кто-то об этом подумал. И, возможно, у меня было больше самоконтроля, чем у меня.

fprintf(fopen("temp.txt", "w"), "%d", a);
a = b;
fscanf(fopen("temp.txt", "r"), "%d", &b);

Никаких лишних переменных!

У меня это работает, но в зависимости от реализации stdio вам, возможно, придется что-то делать с буферизацией вывода.

person Thomas Padron-McCarthy    schedule 09.01.2015
comment
не закрывать файл после записи в него работает не во всех системах. И это перебор! - person Jean-François Fabre; 06.04.2020

C++11 позволяет:

person k06a    schedule 29.06.2019

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

a = a+b;
b=b-(-a);
a=b-a;
b=-(b);
person Katie    schedule 03.08.2015

Также можно использовать умножение и деление.

 int x = 10, y = 5;

 // Code to swap 'x' and 'y'
 x = x * y;  // x now becomes 50
 y = x / y;  // y becomes 10
 x = x / y;  // x becomes 5
person Moiz Sajid    schedule 20.08.2015