我需要帮助完成计算机科学作业。我需要编写一个排序函数,用于对长度为5的数组进行排序,在最坏情况下使用7次比较(因为决策树的高度已经被证明需要7次比较)。
我考虑过使用'硬编码'的决策树,但这意味着算法非常复杂,我的导师暗示这不是正确的做法。
我已经检查了快速排序、归并排序、堆排序、d-ary堆排序、插入排序、选择排序,都不能满足要求,这让我相信需要一个特定的算法来处理长度为5的数组。
真的想得到一些指向正确方向的提示。
我需要帮助完成计算机科学作业。我需要编写一个排序函数,用于对长度为5的数组进行排序,在最坏情况下使用7次比较(因为决策树的高度已经被证明需要7次比较)。
我考虑过使用'硬编码'的决策树,但这意味着算法非常复杂,我的导师暗示这不是正确的做法。
我已经检查了快速排序、归并排序、堆排序、d-ary堆排序、插入排序、选择排序,都不能满足要求,这让我相信需要一个特定的算法来处理长度为5的数组。
真的想得到一些指向正确方向的提示。
唐纳德·克努斯的The Art of Computer Programming,第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
这里再次,还没有与其他人序列化的那个可以通过两次比较正确地放置。
是的,它在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)
))))
[0, 1, 2, 3, 4]
的每个排列进行排序需要840次比较,而使用Ford-Johnson算法仅需要832次比较。我担心您的实现与Knuth所描述的不完全相同。 - Morwenn[0,3,1,2,4], [0,3,2,1,4], [1,2,0,3,4], [1,2,3,0,4], [2,1,0,3,4], [2,1,3,0,4], [3,0,1,2,4], [3,0,2,1,4]
[1,4,2,3,0], [1,4,3,2,0], [2,3,1,4,0], [2,3,4,1,0], [3,2,1,4,0], [3,2,4,1,0], [4,1,2,3,0], [4,1,3,2,0]
5! * 7 = 340 - 8 = 832
。 - Christopher Galpin我认为硬编码的解决方案不需要太复杂:
这将始终使用7次比较。
编辑:
我认为这不会起作用:步骤4是错误的,可能需要第8次比较。考虑:
Index | 1 | 2 | 3 | 4 | 5 |
Value | 2 | 3 | 4 | 5 | 1 |
步骤4:
看一下桶排序。
桶排序可以实现为以下比较算法:
取一个元素。
将其与所有桶进行比较。
将其放入匹配的桶中。 <- 需要比较。
如果找不到桶,则创建一个新桶。
因此,这是一种动态桶排序算法,我刚刚描述了它。
我以前在新闻组中已经发明/描述了这个算法。