Как отсортировать три переменные, используя не более двух свопов?
Следующий алгоритм может сортировать три переменные x
, y
и z
типа K
, которые сопоставимы с помощью operator<
:
void sort2(K& x, K& y) {
if(y < x)
swap(x, y);
}
void sort3(K& x, K& y, K& z) {
sort2(x, y);
sort2(y, z);
sort2(x, y);
}
Это требует трех свопов в "худшем случае". Однако основная математика говорит нам, что упорядочение трех значений может быть выполнено с использованием только двух свопов.
Пример: значения (c, b, a) будут отсортированы с использованием трех свопов: (c, b, a) → (b, c, a) → (b, a, c) → (a,До нашей эры). Однако одного свопа было бы достаточно: (c, b, a) → (a, b, c).
Какими будут самые простые алгоритмы, которые сортируют три переменные с не более чем двумя свопами во всех случаях?
Ответы
Ответ 1
Найти наименьшее, это займет 2 сравнения и поменять его на первую позицию.
Затем сравните оставшиеся 2 и при необходимости замените.
if (x < y) {
if (z < x) swap(x,z);
} else {
if (y < z) swap(x,y);
else swap(x,z);
}
if(z<y) swap(y,z);
Это занимает 3 сравнения, но только два свопа.
Ответ 2
void sort(int& a, int& b, int& c)
{
swap(a, min(a, min(b, c)));
swap(b, min(b, c));
}
2 свопа, 3 сравнения.
Ответ 3
Найдите минимальное значение и замените его первым значением. Найдите второй минимум и замените его вторым значением. Два свопа не более.
В основном это сортировка выбора, которая будет выполнять не более n - 1
свопов.
Ответ 4
От 2 до 3 сравнений, от 0 до ~ 1.7 свопов
Старый вопрос, новый ответ... Следующий алгоритм сортирует x
, y
и z
с 2 по 3 сравнения в зависимости от их значений и от 0 до ~ 1.7 операций свопинга.
void sort3(K& x, K& y, K& z)
{
if (y < x) {
if (z < x) {
if (z < y) {
swap(x, z);
} else {
K tmp = std::move(x);
x = std::move(y);
y = std::move(z);
z = std::move(tmp);
}
} else {
swap(x, y);
}
} else {
if (z < y) {
if (z < x) {
K tmp = std::move(z);
z = std::move(y);
y = std::move(x);
x = std::move(tmp);
} else {
swap(y, z);
}
}
}
}
Итак, как это работает? Он basiccaly разворачивается сортировка вставки: если значения уже отсортированы (для проверки это требуется 2 сравнения), то алгоритм ничего не меняет. В противном случае он выполняет 1 или 2 операции свопинга. Однако, когда требуются 2 операции свопинга, алгоритм "вращает" значения вместо этого, так что выполняется 4 хоста вместо 6 (операция свопинга должна стоить 3 ходов, если оптимизировано).
Существует только 6 возможных перестановок из 3 значений. Этот алгоритм выполняет сравнения, чтобы знать, какую перестановку мы рассматриваем. Затем он обменивается и уходит. Следовательно, алгоритм имеет 6 возможных путей (включая тот, где он ничего не делает, потому что массив уже отсортирован). Хотя он все еще доступен для человека, эквивалентный оптимальный алгоритм для сортировки значений 4 будет иметь 24 разных пути и будет намного сложнее читать (для n значений возможны n! Возможные перестановки).
Поскольку мы уже в 2015 году, и вы, похоже, используете С++, я воспользовался свободой std::move
, чтобы убедиться что swap-rotate thingy будет достаточно эффективным и будет работать даже для подвижных, но не копируемых типов.
Ответ 5
Если вы не сделаете это на месте, вы можете выполнить его без каких-либо свопов.
Ответ 6
Кодировать сортировочную сеть в таблице. Статья, связанная с Википедии, должна помочь вам со ссылками, если вам нужно выяснить, что положить в таблицу в других случаях (т.е. Больше массивов).
Ответ 7
Я думаю, что вы хотите найти оптимальный своп на каждом шаге, а не только действительный своп. Для этого просто найдите наибольшую разницу между элементом и элементом позже в списке и замените их. В 3-кортеже есть три возможных свопа: 1-3, 1-2 и 2-3. На каждом шаге найдите максимальное различие между этими тремя свопами и сделайте это. Довольно уверен, что дает два свопа в худшем случае для 3 элементов. Только действительно имеет смысл, если обмен происходит относительно дорого по сравнению с сравнением элементов, иначе, вероятно, не стоит дополнительного анализа авансом.
Ответ 8
Холодный вопрос:)
Если сборка доступна для вас и значения, вписываемые в регистр, то вы, вероятно, можете сделать это очень быстро, просто загрузив их в регистры и сделав несколько сравнений, перепрыгнув в нужный сценарий, чтобы вернуть значения. Возможно, ваш компилятор уже делает эту оптимизацию.
В любом случае, если производительность является вашей целью, взгляните на сгенерированный машинный код и оптимизируйте его. Для такого небольшого алгоритма, где вы можете выжать производительность из.
Ответ 9
Недавно мне пришлось решить подобную проблему - эффективно сортировать три значения. Вы концентрируетесь на своп-операциях в своем вопросе. Если производительность - это то, что вы ищете, сосредоточьтесь на операциях сравнения и ветких! При сортировке такого "крошечного" массива с тремя значениями хорошей идеей является рассмотрение использования дополнительного хранилища, которое подходит для стольких значений. Я придумал нечто вроде специализированного "сортировки слияния" (см. Код ниже).
Точно так же, как tenfour, я посмотрел на сборку, а приведенный ниже код скомпилирован до компактного встроенного набора операций с регистром CPU и очень быстро. Дополнительная переменная "arr12" также сохраняется в регистрах CPU. Для сортировки требуются две или три операции сравнения. Функция может быть легко преобразована в шаблон (здесь не приводится для ясности).
inline void sort3_descending( double * arr )
{
double arr12[ 2 ];
// sort first two values
if( arr[ 0 ] > arr[ 1 ] )
{
arr12[ 0 ] = arr[ 0 ];
arr12[ 1 ] = arr[ 1 ];
} // if
else
{
arr12[ 0 ] = arr[ 1 ];
arr12[ 1 ] = arr[ 0 ];
} // else
// decide where to put arr12 and the third original value arr[ 3 ]
if( arr12[ 1 ] > arr[ 2 ] )
{
arr[ 0 ] = arr12[ 0 ];
arr[ 1 ] = arr12[ 1 ];
} // if
else if( arr[ 2 ] > arr12[ 0 ] )
{
arr[ 0 ] = arr [ 2 ];
arr[ 1 ] = arr12[ 0 ];
arr[ 2 ] = arr12[ 1 ];
} // if
else
{
arr[ 0 ] = arr12[ 0 ];
arr[ 1 ] = arr [ 2 ];
arr[ 2 ] = arr12[ 1 ];
} // else
}
Ответ 10
Это может быть проиллюстрировано таблицей истинности, относящейся ко всей возможной комбинации сравнений, чтобы увидеть, как мы можем оптимально оптимизировать своп, который вы упомянули здесь.
Значения | x < y | y < z | x < г
x, y, z | y | y | у
x, z, y | y | n | у
y, x, z | n | y | у
y, z, x | n | y | п
z, x, y | y | n | п
z, y, x | n | n | п
Обращая вопрос таким образом, мы можем легко увидеть, что, сначала проверяя и заменяя 1-й и 3-й элементы, наименьшее значение, которое мы можем иметь в первом элементе после свопа, может быть либо x, либо y. Это упрощает проверку if, чтобы мы могли либо поменять 1-й и 2-й элементы при x > y, либо заменить 2-й и 3-й элементы при y > z.
if (x > z) {
swap(x,z);
}
if (x > y) {
swap(x,y);
} else if (y > z) {
swap(y,z);
}
Не требуется никаких вложенных условных выражений. Всего 2-3 простых сравнения для 2 свопов при макс.