【问题标题】:Number of Comparisons using merge sort使用归并排序的比较次数
【发布时间】:2010-12-03 18:46:25
【问题描述】:

如果您有 5 个不同的数字,您最多需要多少次比较才能使用归并排序对其进行排序?

【问题讨论】:

  • 嗯,我是一名学生,但这不是作业问题。只是好奇。 O(nlogn) 是归并排序的最坏情况。这涉及 5*2.3 = 11 次比较,但是当我在纸上进行比较时,我得到了更好的结果,所以我很好奇。在最坏的情况下,我们需要进行多少次比较才能对它进行排序?
  • “最坏”的情况是将每个数字与其他每个数字(即 10)进行比较。
  • 与冒泡排序、插入排序或选择排序相同。你能给出最坏情况下 5 个数字的序列吗?
  • 写一个归并排序,生成所有排列,就大功告成了。
  • 大 O 表示法描述了增长,它没有说明您最终将完成的操作数量,也没有说明它们需要多长时间。因此,如果 1 表示“1 周”而另一个 O(nlogn) 算法只需要几分钟,那么 O(nlogn) 可能会很好地胜过 O(1)。

标签: algorithm mergesort


【解决方案1】:

是什么阻止了您编写归并排序、保留一个计数器以记录其中的比较次数并尝试对 [0,1,2,3,4] 的所有排列进行测试?

【讨论】:

  • 我喜欢你的回答,我只是没有太多时间来编写它。我看了这些排序小程序,有些是错的,有些只是图片。
  • 合并排序实际上并不像您想象的那样花费很长时间来编写代码。它在 Python 中相当短(我认为在许多函数式语言中甚至更短),基本的 C/C++/Java 解决方案也不应该太长。
【解决方案2】:

我觉得这个问题很有趣,所以我决定彻底探索它(在 Python 中进行一些实验)。

我从here 下载了mergesort.py,并对其进行了修改,为比较器函数添加了cmp 参数。那么:

import collections
import itertools
import mergesort
import sys

class CountingComparator(object):
  def __init__(self):
    self.count = 0
  def __call__(self, a, b):
    self.count += 1
    return cmp(a, b)

ms_histo = collections.defaultdict(int)

for perm in itertools.permutations(range(int(sys.argv[1]))):
  cc = CountingComparator()
  lperm = list(perm)
  mergesort.mergesort(lperm, cmp=cc)
  ms_histo[cc.count] += 1

for c in sorted(ms_histo):
  print "%d %2d" % (c, ms_histo[c])

生成的简单直方图(从长度 4 开始,正如我在开发和调试时所做的那样)是:

4  8
5 16

对于发布的问题,长度为 5 而不是 4,我得到:

5  4
6 20
7 48
8 48

长度为 6(和更宽的格式;-):

7    8
8   56
9  176
10 288
11 192

最后,长度为 7(甚至更宽的格式;-):

 9   16
10  128
11  480
12 1216
13 1920
14 1280

这里肯定潜伏着一些完全规则的组合公式,但我发现很难判断它可能是什么,无论是通过分析还是通过仔细研究数字。有人有建议吗?

【讨论】:

  • 干得好。我非常感谢您对该主题的好奇心和兴趣。通过查看结果,可以看到比较次数多于 n 且少于 2n。 Wiki 建议:在最坏的情况下,归并排序的比较量等于或略小于 (n ⌈lg n⌉ - 2⌈lg n⌉ + 1),介于 (n lg n - n + 1) 和(n lg n + n + O(lg n))。 [1]
【解决方案3】:

当对两个长度为 L1 和 L2 的列表进行合并排序时,我认为最坏情况的比较次数是 L1+L2-1。

  • 最初您有五个 1 长的列表。
  • 您可以使用 2 次比较 合并两对列表,从而生成长度为 2,2 和 1 的列表。
  • 然后,您可以将 2 和 1 长列表与最多另外 1+2-1 = 2 次比较合并,产生 2 和 3 长列表。
  • 最后,您将这些列表与最多 2+3-1 = 4 次比较合并。

所以我猜答案是 8。

这个数字序列导致上述结果: [2], [4], [1], [3], [5] -> [2,4], [1,3], [5] -> [2,4], [1,3,5 ] -> [1,2,3,4,5]

编辑:

这是一个简单的 Erlang 实现。基于此,对于 1..5 的排列,比较次数为 5、6、7 或 8。

-module(mergesort).

-compile(export_all).


test() ->
  lists:sort([{sort(L),L} || L <- permutations()]).

sort([]) -> {0, []};
sort([_] = L) -> {0, L};
sort(L) -> 
  {L1, L2} = lists:split(length(L) div 2, L),
  {C1, SL1} = sort(L1), {C2, SL2} = sort(L2),
  {C3, RL} = merge(SL1, SL2, [], 0),
  {C1+C2+C3, RL}.

merge([], L2, Merged, Comps) -> {Comps, Merged ++ L2};
merge(L1, [], Merged, Comps) -> {Comps, Merged ++ L1};
merge([H1|T1], [H2|_] = L2, Merged, Comps) when H1 < H2 -> merge(T1, L2, Merged ++[H1], Comps + 1);
merge(L1, [H2|T2], Merged, Comps) -> merge(L1, T2, Merged ++[H2], Comps + 1).


permutations() ->
  L = lists:seq(1,5),
  [[A,B,C,D,E] || A <- L, B <- L, C <- L, D <- L, E <- L, A =/= B, A =/= C, A =/= D, A =/= E, B =/= C, B =/= D, B =/= E, C =/= D, C =/= E, D =/= E].

【讨论】:

    【解决方案4】:
    【解决方案5】:

    根据Wikipedia在最坏的情况下,归并排序的比较量等于或略小于 (n ⌈lg n⌉ - 2^⌈lg n⌉ + 1)

    【讨论】:

    • 我读到了,我只是想看看数字。那么我可以说:5*3 - 2*3 +1 = 10。我也对这样的数字感到好奇。
    • 因为 ⌈lg 5⌉ 是 2,所以答案是 5*2-2^2+1 = 7。如果您遵循文章中描述的算法,这是有道理的。如果初始序列是 2,4,1,3,5,则比较将按出现顺序为: (2,4) (2,1) (3,5) (1,3) (2,3) (4,3) (4,5)
    • 公式建议的是天花板,而不是地板。因此 log5 = 3
    • 实际上,我犯的错误是假设 lg 指的是自然对数。 ln(5)=1.609... 当然,以 2 为底的对数在这种情况下更有意义。
    • stackoverflow.com/questions/12346054/… 询问这个公式,以及它的来源。 My answer there 有证据。
    【解决方案6】:

    对于仅要排序的五个不同数字,您可以进行的最大比较次数为 8 次,最小比较次数为 7 次。原因如下:-

    假设数组是a,b,c,d,e

    递归除法:a,b,c 和 d,e

    递归划分:a,b&c 和 d&e

    递归除法:a&b & c 和 d&e

    现在,合并需要比较-

    a & b : 一个比较形成 a,b

    a,b & c : 两个比较形成 a,b,c

    d & e : 与 d,e 形式的比较

    a,b,c and d,e : 最坏情况四比较或三比较 id d 是数组中最大的元素组成a,b,c,d,e

    因此,最坏情况下的比较总数为 8,最好情况下为 7。

    【讨论】:

      猜你喜欢
      • 2016-10-02
      • 2021-11-03
      • 1970-01-01
      • 2012-06-01
      • 2021-09-02
      • 1970-01-01
      • 1970-01-01
      • 2015-07-09
      • 2012-01-22
      相关资源
      最近更新 更多