如何使用最多两次掉期对三个变量进行排序?

时间:2010-07-27 12:21:53

标签: algorithm

以下算法可以对x类型yzK的三个变量进行排序,使用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) - &gt; (b,c,a) - &gt; (b,a,c) - &gt; (A,B,C)。然而,一次交换就足够了:(c,b,a) - &gt; (A,B,C)。

在所有情况下,最简单的两个掉期排序三个变量的最简单算法是什么?

10 个答案:

答案 0 :(得分:34)

找到最小的,这需要进行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次比较,但只有两次交换。

答案 1 :(得分:11)

void sort(int& a, int& b, int& c)
{
   swap(a, min(a, min(b, c)));
   swap(b, min(b, c));
}

2次交换,3次比较。

答案 2 :(得分:8)

2到3个比较,0到~1.7交换

旧问题,新答案......以下算法对xyz进行排序,并根据其值和0到~1.7交换操作进行2到3次比较。< / p>

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次移动(交换操作应该花费3次移动,unless optimized)。

3个值只有6种可能的排列。该算法进行比较以了解我们正在处理哪种排列。然后它进行交换和离开。因此,该算法有6条可能的路径(包括它没有做任何路径的路径,因为数组已经被排序)。虽然它仍然是人类可读的,但是对4个值进行排序的等效最佳算法将具有24个不同的路径并且将更难以读取(对于n个值,存在n!可能的排列)。

由于我们已经在2015年并且你似乎正在使用C ++,我冒昧地使用std::move以确保swap-rotate thingy足够高效并且甚至可以用于可移动但非 - 可复制的类型。

答案 3 :(得分:7)

找到最小值并将其与第一个值交换。找到第二个最小值并将其与第二个值交换。最多两次交换。

这基本上是selection sort,最多会执行n - 1次互换。

答案 4 :(得分:2)

如果您不这样做,您可以在没有任何交换的情况下执行此操作。

答案 5 :(得分:1)

在表格中对sorting network进行编码。我链接的维基百科文章可以帮助你提供参考,以防你需要弄清楚在其他情况下放在表格中的内容(即更大的数组)。

答案 6 :(得分:1)

我认为您想要的是在每个步骤中找到最佳交换,而不仅仅是有效交换。要做到这一点,只需在列表中稍后找到元素和元素之间的最大差异并交换它们。在3元组中,有三种可能的交换,1-3,1-2和2-3。在每个步骤中找到这三个交换之间的最大差异并执行此操作。非常确定在最坏的情况下为3个元素提供两次交换。只有与比较元素相比,交换相对昂贵才真正有意义,否则可能不值得进行额外的分析。

答案 7 :(得分:1)

很酷的问题:)

如果你可以使用汇编,并且这些值适合寄存器,那么你可以通过将它们加载到寄存器中并进行一些比较来非常快速地完成它,跳转到正确的场景以将值放回去。也许您的编译器已经进行了这种优化。

无论哪种方式,如果性能是您的目标,请查看生成的机器代码并在那里进行优化。对于这样一个小算法,你可以用它来挤出性能。

答案 8 :(得分:1)

我最近不得不解决类似的问题 - 有效地排序三个值。你专注于你的问题中的交换操作。如果您正在寻找性能,请专注于比较操作和分支机构!当只用三个值对这样的“微小”数组进行排序时,一个好主意是考虑使用额外的存储,这适用于很少的值。我提出了类似专门的“合并排序”(见下面的代码)。

正如 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
}

答案 9 :(得分:0)

这可以用真值表来说明每个可能的比较组合,看看我们如何最好地优化你在这里提到的交换。

值| x&lt; y | y&lt; z | x&lt; ž

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 | Ñ

通过这种方式构建问题,我们可以很容易地看到,通过最初检查和交换第一个和第三个元素,交换后第一个元素中的最低值可以是x或y。这简化了之后的if检查,以便当x> 1时我们可以交换第1和第2个元素。 y或y>当交换第2和第3个元素时ž。

if (x > z) {
    swap(x,z);
}

if (x > y) {
    swap(x,y);
} else if (y > z) {
    swap(y,z);
}

不需要任何嵌套的if条件。只需2-3次简单比较,最多可进行2次掉期