【问题标题】:Shellsort, 2.48^(k-1) vs Tokuda's sequenceShellsort, 2.48^(k-1) vs Tokuda 的序列
【发布时间】:2014-02-25 19:57:22
【问题描述】:

简介

Shellsort 是我不久前遇到的一个有趣的排序算法。最令人惊奇的部分是不同的间隙序列可以显着提高算法的速度。我做了一些阅读(不是广泛阅读),似乎建议将 Tokuda 的序列用于实际应用。

另一个有趣的部分是比率 2.20~2.25 的序列往往更有效。所以我做了一个小的搜索,考虑了从 2.20 到 2.50 的比率序列,并试图搜索哪个比率可以表现平均好。我遇到了这个比率:2.48,这似乎在许多不同的试验中平均表现良好。

然后,我想出了序列生成器:2.48k-1(我们称之为 248 序列)并尝试将其与德田的序列进行比较。事实证明,它们的速度平均相等。 248 序列倾向于使用更多的比较次数。

基准方法

  • 我没有使用毫秒作为度量,而是使用比较次数和交换次数。
  • 我对以下数组大小(50,000;100,000;200,000;300,000;500,000;1,000,000)分别进行了 100 次试验,并跟踪它们的比较次数和交换次数。
  • 以下是我的数据 (here in CSV format)。
  • 完整代码:http://pastebin.com/pm7Akpqh

问题

我知道我可能错了,这就是为什么我来这里寻求更多经验丰富的程序员的意见。如果你没有得到这个问题,这里是我的简短问题:

  • 2.48k-1 和德田的序列一样好吗?
  • 如果它和德田的序列一样好,那么使用它会不会更实用,因为 2.48k-1 序列比德田的序列更容易生成。

248序列: 综述(2.48(k-1)) 例如:1、3、7、16、38、94、233、577、1431、3549、8801、21826、... 德田的序列 ROUNDUP ( (9k - 4k) / (5 * 4k - 1) ) 例如:1、4、9、20、46、103、233、525、1182、2660、5985、13467、...

正如@woolstar 建议的那样,我也可以使用反转和排序等边缘情况进行测试。正如预期的那样,248 序列在边缘情况下更快,因为 248 序列间隙更大,因此逆向移动得更快。


Shellsort 实施

public static int compare = 0;
public static int swap = 0;

public static bool greaterthan(int a, int b) {
    compare++;
    return a > b;
}

public static int shellsort(int[] a, int[] gaps) {
    // For keeping track of number of swap and comparison
    compare = 0;
    swap = 0;

    int temp, gap, i, j;

    // Finding a gap that is smaller than the length of the array
    int gap_index = gaps.Length - 1;
    while (gaps[gap_index] > a.Length) gap_index--;

    while (gap_index >= 0) {

        // h-sorting
        gap = gaps[gap_index];
        for (i = gap; i < a.Length; i++) {
            temp = a[i];
            for(j = i; (j >= gap) && (greaterthan(a[j - gap], temp)); j -= gap) {
                a[j] = a[j - gap];
            }

            // swapping
            a[j] = temp;
            swap++;
        }

        gap_index--;
    }

    return compare;
}

【问题讨论】:

  • 你能在这里引用德田的序列吗?我不知道。
  • 除了测试随机排序的数据外,测试您的序列如何处理边缘情况,例如完全排序、几乎排序和反转。
  • @JanDvorak,我更新了德田的序列公式。
  • @woolstar,感谢您的想法。我会尝试边缘情况。
  • @woolstar,正如我所测试的,在边缘情况下,248 序列运行得更快,因为差距大于德田的序列。

标签: c# algorithm sorting shellsort


【解决方案1】:

根据this reserach(Ciura, Marcin (2001)“Shellsort 平均情况的最佳增量”。在 Freiwalds,Rusins。第 13 届计算理论基础国际研讨会论文集。伦敦:Springer -Verlag. pp. 106–117) 对于小于 108 个元素的数组,shell 排序中的主要操作应该是比较操作,而不是交换:

Knuth 的讨论假设运行时间可以近似为 9×移动次数, 而图 3 和图 4 表明,对于每个序列,关键比较的次数比移动次数更能衡量运行时间。对于 N ≤ 108 而言,每次移动 9 个周期的渐近比并不太精确,并且,如果某个假设序列进行 Θ(NlogN) 移动,则永远无法实现。其他计算机体系结构的类似图将得出相同的结论。

将移动视为主导操作会导致错误的结论 关于最优序列。

在这种情况下,您的问题的答案是否定的:248 序列更糟糕,因为它使用了更多的比较。您也可以考虑将您的序列与本文中介绍的 Ciura 序列进行比较,因为这项研究似乎证明它比 Tokuda 的序列更好。

【讨论】:

  • 很难与 Ciura 的序列进行比较,因为 701 之后没有已知值。使用(Ciura 的序列)1、4、10、23、57、132、301、701 对大小数组进行排序即使我扩展 h(i) = 2.25 * h(i-1),1,000,000 也会比 Tokuda 慢。
  • Tokuda 倾向于使用比 248 序列少 1% 的比较。我用 Knuth 的序列和 Sedgewick 的序列对其进行了测试,248 序列使用较少的比较。
  • @invisal 哦,对了,没有注意到 Ciura 的序列没有一般形式。好吧,看来你可能会在 Springer 上发表文章 :)
  • 好吧,我只是使用随机数组进行基准测试。我没有任何数学模型可以证明所有情况。很遗憾。
  • @invisal 良好而彻底的实践实验也具有科学价值 :) 你仍然可以尝试添加一些小的理论分析。
猜你喜欢
  • 1970-01-01
  • 2021-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多