使用最少的比较对数组进行排序

时间:2009-12-20 08:01:47

标签: algorithm sorting

我的CS作业需要一些帮助。我需要编写一个排序例程,在最坏的情况下使用7次比较对长度为5的数组进行排序(我已经证明,由于决策树的高度,需要7个。)

我考虑使用决策树“硬编码”,但这意味着算法非常复杂,并且我的导师暗示这不是它应该完成的方式。

我检查了快速排序,合并排序,堆排序,d-ary堆排序,插入排序,选择排序,都没有回答要求,这让我相信需要一个长度数组的特定算法5。

真的希望得到一些正确方向的提示。

6 个答案:

答案 0 :(得分:19)

Donald Knuth的计算机编程艺术,第3卷有一个关​​于这个主题的部分。我没有这里的书,但我很确定Knuth提出了5个元素的算法。正如您所怀疑的那样,没有一种通用算法能够为许多大小提供最少量的比较,但是在这种算法中使用了许多常用技巧。

从模糊的回忆中,我重构了5个元素的算法,可以在7个比较中完成。首先,取两个单独的对,在其中进行比较,并比较每对中较小的一对。然后,将剩余的一个与其中较大的一个进行比较。现在根据剩余元素是小还是大来分为两种情况,但在所有情况下都可以在仍然可用的三种比较中完成。

我建议您绘制图片来帮助您。 Knuth的照片是这样的:

   o---o
  /
 o---o

显示了前三次比较后的结果(据我记得,这种图片出现在许多最小比较分类中)。一条线连接我们知道的两个元素。拥有这样的图片可以帮助您确定要比较的元素,因为您希望进行比较以获得最大量的信息。

附录:由于实际代码有一个可接受的答案,我想完成这些图表没有任何害处,它们可能是答案的有用补充。因此,从上面的一个开始,将缺少的元素与左上角的元素进行比较。如果它更大,这将导致

    /--o
   o
  / \--o
 o
  \--o

现在,比较右上角的两个大元素,最后得到

   o---o---o
  /
 o---o

现在,通过首先将右下角元素与顶部的中间元素进行比较,然后将其与所属的一侧进行比较,我们使用剩余的两个比较正确地放置它。

如果初始比较导致剩余元素变小,则图表变为

 o---o---o
    /
   o---o

现在,比较两个还没有比它们小的东西。一个选项是上面的最后一个图,它可以通过剩余的两个比较来解决。另一种情况是

       o---o
      /
 o---o---o

再次,与其他人不一致的那个可以通过两次比较正确放置。

答案 1 :(得分:13)

是的,这是在Knuth第3卷第185页(第5.3.1节)中。这是首次在博士论文中记录,所以你的教授对你很难!没有真正简单优雅的方法;你必须将它作为一个部分有序的树来跟踪。

这是在lisp中。测试好了(Linux上的SBCL)

(defun small-sort (a)  
  "Sort a vector A of length 5"  
  (if (> (aref a 0) (aref a 1))  
      (rotatef (aref a 0) (aref a 1)))  
  (if (> (aref a 2) (aref a 3))  
      (rotatef (aref a 2) (aref a 3)))  
  (if (> (aref a 0) (aref a 2))  
      (progn  
        (rotatef (aref a 0) (aref a 2))  
        (rotatef (aref a 1) (aref a 3))))  
  (if (> (aref a 4) (aref a 2))  
      (if (> (aref a 4) (aref a 3))  
          (progn)  
          (rotatef (aref a 3) (aref a 4)))  
      (if (> (aref a 4) (aref a 0))  
          (rotatef (aref a 2) (aref a 4) (aref a 3))  
          (rotatef (aref a 0) (aref a 4) (aref a 3) (aref a 2))))  
  (if (> (aref a 1) (aref a 3))  
      (if (> (aref a 1) (aref a 4))  
          (rotatef (aref a 1) (aref a 2) (aref a 3) (aref a 4))  
          (rotatef (aref a 1) (aref a 2) (aref a 3)))  
      (if (> (aref a 1) (aref a 2))  
          (rotatef (aref a 1) (aref a 2))  
          (progn)))  
  a)  

(defun check-sorted (a)  
  (do ((i 0 (1+ i)))  
      ((>= i (1- (array-dimension a 0))))  
    ;;(format t "~S ~S~%" (aref a i) (aref a (+ 1 i)))  
    (assert (<= (aref a i) (aref a (+ 1 i))))))  

(defun rr ()  
  (dotimes (i 100000)  
    (let ((a (make-array 5 :initial-contents (list (random 1.0) (random 1.0) (random 1.0) (random 1.0) (random 1.0) ))))  
      ;;(format t "A=~S~%" a)  
      (let ((res (small-sort a)))  
        (check-sorted res)  
        ;;(format t "Res=~S~%" res)  
        ))))  

答案 2 :(得分:0)

看一下桶排序。

答案 3 :(得分:0)

7听起来也可能shell排序。

答案 4 :(得分:0)

我认为硬编码解决方案不需要那么复杂:

  1. 比较(元素)2&amp; 3,并根据需要进行交换
  2. 比较3&amp; 4,并根据需要进行交换
  3. 比较1&amp; 3,如果1较小,则比较1&amp; 2,否则比较1&amp; 4.将1放入正确的插槽中。
  4. 重复步骤3,除了元素3&amp; 5。
  5. 这将始终使用7次比较。

    编辑:

    我不认为这会起作用:第4步被打破,可能需要进行第8次比较。考虑:

    Index | 1 | 2 | 3 | 4 | 5 |
    Value | 2 | 3 | 4 | 5 | 1 |
    

    第4步:

    1. 比较3&amp; 5 == 4 vs 1 ==元素5小于元素3
    2. 比较2&amp; 5 == 3 vs 1 ==元素5小于元素2
    3. ???需要比较1&amp; 5知道在哪里放置元素5.

答案 5 :(得分:0)

存储桶排序可以实现为比较算法,如下所示:

拿一个元素。

将它与所有存储桶进行比较。

将其放入匹配的存储桶中。 &lt; - 需要比较。

如果找不到存储桶,请创建一个新存储桶。

因此,它是我刚刚描述的动态桶排序算法。

我过去已经在新闻组上发明过/描述了这个。