来源:H.W. Lang Hochschule Flensburg lang@hs-flensburg.de Impressum Datenschutz © Created: 29.01.1998 Updated: 04.06.2018
(今天我看了一下,发现这个文章的链接已经失效了,因为这个好像是这位教授的上课教案,而不是他的个人博客。我今天点进去发现这个网站是个大学课程地址,然后这篇文章已经没有了。2022.4.13)
希尔排序是最古老的排序算法之一,以其发明者D.L.Shell(1959年)命名[She 59]。虽然该算法速度快,易于理解,易于实现。然而,其复杂性分析却要复杂的多。 (୨୧•͈ᴗ•͈)◞︎♡粉色的字是我写的注释嗷。
算法思想
希尔排序的算法思想如下如下:
- 将数据序列存放在二维数组中
- 对数组的列进行排序
其结果是对数据进行了部分排序。重复上述过程,但是每次都使用较窄的数组,即列数较少。在最后一步,数组仅有一列。在每一步中,需要进行排序的序列都会增加(列数逐渐变少,每列包含的数组逐渐增多),直到最后一步完全排序为止。但是,由于在前面的步骤中获得的序列已经基本有序,因此在每个步骤中进行的排序操作是有有限。
例如:
对3 7 9 0 5 1 6 8 4 2 0 6 1 5 7 3 4 9 8 2
进行排序。
首先,将其排列成具有7列的数组(左),然后对这些列进行排序(右):
3 7 9 0 5 1 6 3 3 2 0 5 1 5
8 4 2 0 6 1 5 → 7 4 4 0 6 1 6
7 3 4 9 8 2 8 7 9 9 8 2
依次排序之后较大的数据元素8
和9
已经到了所在列的末尾,但是一个较小的数字(如倒数第二列的2
)也在该列末尾。 下一步将该序列分为3列,并再次对其进行排序:
3 3 2 0 0 1
0 5 1 1 2 2
5 7 4 3 3 4
4 0 6 → 4 5 6
1 6 8 5 6 8
7 9 9 7 7 9
8 2 8 9
现在,序列已经基本有序。在最后一步将其排列成一列时,只需将6、8和9移到正确位置即可。
算法实现
实际上,数据序列不是存储在二维数组中,而是保存在一维数组中。 例如,位置0、5、10、15等处的数据元素想象为具有5列的数组的第一列。通过这种方式建立索引的“列”,对每列进行插入排序。这种方法具有良好的性能。
以下程序从索引位置0
到n-1
对数组a进行排序,数据存放在数组cols
中。 因此,在第一步中将数据排列在1391376列中,在最后一步中将数据排列在一列中。 (请注意,如果列数h大于待排序数据元素的总数,则基本上不执行任何操作。也就是说,一开始第一步,数据最大值是多大就给他们分配多少列是没意义的,不进行任何操作)对每列插入排序。
void shellsort (int[] a, int n)
{
int i, j, k, h, v;
int[] cols = {1391376, 463792, 198768, 86961, 33936, 13776, 4592,
1968, 861, 336, 112, 48, 21, 7, 3, 1}
for (k=0; k<16; k++)
{
h=cols[k];
for (i=h; i<n; i++)
{
v=a[i];
j=i;
while (j>=h && a[j-h]>v)
{
a[j]=a[j-h];
j=j-h;
}
a[j]=v;
}
}
}
算法分析
该算法的正确性在于最后一步(h = 1)中,对整个数组进行普通插入排序。但是由于数据是通过前面的步骤(h = 3、7、21,…)进行预排序,因此最后一步只有很少的插入排序步骤即可。 上面的h序列(在下文中称为h序列)只是一种取增量的方法,实际上希尔排序的性能取决于增量到底怎么取。
背景
寄一封信需要16法郎,一张明信片需要11法郎。但是只能买到3法郎和7法郎的邮票。可以把信和明信片寄出去吗?(兄弟们寄过明信片吗?寄之前邮局已经按照距离规定好了邮费的,寄的时候需要贴上相应价钱的邮票才可以寄出,当没有相应面额的邮票的时候,你就要贴好几枚凑够价钱。比如我们邮政寄一封信1.2元,你可以贴3枚4毛钱的邮票。)
显然,问题是将数字 16 和 11 表示为线性组合
k
⋅
3
+
l
⋅
7
k·3 + l·7
k⋅3+l⋅7,非负整数系数k
和 l
。哪些自然数能由3和7的整数倍组合而成的?哪些不能?
定义:设
g
,
h
∈
I
N
g,h \in IN
g,h∈IN。如果f
可以表示为系数k
的线性组合
f
=
k
×
g
+
l
×
h
f=k \times g+l \times h
f=k×g+l×h,我们则称f
为g
和h
的线性组合,系数
k
,
l
∈
I
N
0
k,l \in IN_0
k,l∈IN0。
因为16是3和7的线性组合,即
16
=
3
×
3
+
1
×
7
16=3 \times 3+1 \times 7
16=3×3+1×7,所以可以准确将信封寄出去。
定义:令
g
,
h
∈
I
N
g,h \in IN
g,h∈IN互为质数。 用
N
(
g
,
h
)
N(g,h)
N(g,h)表示不是g
和h
的组合的所有自然数的(有限)集合,而
γ
(
g
,
h
)
γ(g,h)
γ(g,h)表示这些数中最大的自然数:
N
(
g
,
h
)
=
{
f
元
素
自
然
数
∣
¬
∃
k
,
l
∈
I
N
0
:
f
=
k
g
+
l
h
}
γ
(
g
,
h
)
=
M
a
x
(
N
(
g
,
h
)
)
N(g,h)=\{f元素自然数 | ¬ \exists k,l \in IN_0:f = kg + lh \} \\ γ(g,h)=Max(N(g,h))
N(g,h)={f元素自然数 ∣ ¬∃k,l∈IN0:f=kg+lh}γ(g,h)=Max(N(g,h))
示例:令
g
=
3
,
h
=
7
g = 3,h = 7
g=3,h=7
N
(
g
,
h
)
=
{
1
,
2
,
4
,
5
,
8
,
11
}
,
γ
(
g
,
h
)
=
11
N(g,h) = \{1,2,4,5,8,11 \},γ(g,h) = 11
N(g,h)={1,2,4,5,8,11},γ(g,h)=11
由于11不是3和7的组合,因此无法寄出明信片。
命题:令
g
,
h
∈
I
N
g,h \in IN
g,h∈IN互为质数。 然后
γ
(
g
,
h
)
=
(
g
−
1
)
⋅
(
h
−
1
)
–
1
γ(g, h) = (g-1)·(h-1) – 1
γ(g,h)=(g−1)⋅(h−1)–1
即,每个大于或等于
(
g
−
1
)
⋅
(
h
−
1
)
(g-1)·(h-1)
(g−1)⋅(h−1)的数f
都是g
和h
的组合。
证明:自己做。
h-排序
定义:
h
∈
I
N
0
h \in IN_0
h∈IN0。如果序列
a
1
,
.
.
.
,
a
n
a_1,...,a_n
a1,...,an对于所有
i
∈
{
1
,
.
.
.
,
n
−
h
}
i \in \{1,...,n-h \}
i∈{1,...,n−h}都满足
a
i
≤
a
i
+
h
a_i \leq a_{i+h}
ai≤ai+h被称为h-排序(以增量h
对数组进行排序)。
数据存放在具有h列的数组中,然后进行列排序,可以获得h-排序的序列。1个排序的序列被排序。
命题:
g
,
h
∈
I
N
g,h \in IN
g,h∈IN。一个g-排序的序列在h排序后仍然保持g-排序。
证明:详细见参考文献[Knu 73]
定义:经过g-排序和h-排序的序列称为g,h-排序。
命题:一个g,h排序的序列是g + h-排序的。
证明:对于所有
i
∈
1
,
.
.
.
,
n
−
(
g
+
h
)
i \in{1,...,n-(g + h)}
i∈1,...,n−(g+h),都满足
a
i
≤
a
i
+
g
+
h
a_i \leq a_{i+g+h}
ai≤ai+g+h,因为
a
i
≤
a
i
+
g
a_i \leq a_{i + g}
ai≤ai+g,序列是按g-排序的;而
a
i
+
g
≤
a
i
+
g
+
h
a_{i + g} \leq a_{i + g + h}
ai+g≤ai+g+h序列是h-排序的。因此我们得出以下结论:
命题:如果一个序列是g,h-排序的,那么对于所有
k
,
l
∈
I
N
0
k,l \in IN_0
k,l∈IN0,它都是kg + lh-排序的,即该序列是以f
为增量的有序序列,f
为g,h
的线性组合。
命题:设a为g,h-排序的序列,其中g
和h
是互为质数。 对于所有元素
a
i
a_i
ai和
a
j
a_j
aj均成立:
j
–
i
≥
(
g
−
1
)
⋅
(
h
−
1
)
⇒
a
i
≤
a
j
j – i \geq (g-1)·(h-1) \Rightarrow a_i \leq a_j
j–i≥(g−1)⋅(h−1)⇒ai≤aj,即任意
a
i
a_i
ai的右侧,仅有下
(
g
−
1
)
⋅
(
h
−
1
)
–
1
(g-1)·(h-1) – 1
(g−1)⋅(h−1)–1 个元素比它小。
证明:令
j
–
i
=
f
≥
(
g
−
1
)
⋅
(
h
−
1
)
j – i = f \geq (g-1)·(h-1)
j–i=f≥(g−1)⋅(h−1),则f
是g
和h
的组合。 因此,该序列是f
排序的,
a
i
≤
a
i
+
f
=
a
j
a_i \leq a_{i+f} = a_j
ai≤ai+f=aj
命题:设a为g,h-排序的序列,其中g
和h
是互为质数,而d
为变量。 如果g和h都在
O
(
d
)
O(d)
O(d)中,则
O
(
n
⋅
d
)
O(n·d)
O(n⋅d)步排序就可以对序列进行d-排序。
证明:在
a
i
a_i
ai的右侧最多
(
g
−
1
)
⋅
(
h
−
1
)
–
1
(g-1)·(h-1) – 1
(g−1)⋅(h−1)–1 个元素比它小。 对序列进行d-排序意味着将其排列为有d列的二维数组,然后进行列排序。
在一列中
a
i
a_i
ai下的列中,较小元素的每隔d
个可能出现一次。 这意味着
a
i
a_i
ai最多只能与
(
(
g
−
1
)
⋅
(
h
−
1
)
–
1
)
/
d
((g-1)·(h-1) – 1) / d
((g−1)⋅(h−1)–1)/d 个元素交换,或者由于g
和h
在
O
(
d
)
O(d)
O(d)中,因此必须与
O
(
d
)
O(d)
O(d)个元素交换。
由于这适用于所有
a
i
(
i
=
1
,
.
.
.
,
n
)
a_i(i = 1,...,n)
ai(i=1,...,n),因此需要进行
O
(
n
⋅
d
)
O(n·d)
O(n⋅d)个排序步骤才能对序列进行d-排序。
从这个命题可以得出希尔排序复杂度的上限。
上限
定理1:对于序列h:1,3,7,15,31,63,127,...,2k–1,...
希尔排序对n个数据排序需要
O
(
n
⋅
n
)
O(n· \sqrt n)
O(n⋅n)个步骤
[
P
S
65
]
^{[PS 65]}
[PS65]。(序列h
是增量取法)
证明:令序列h中,
h
t
h_t
ht的值最接
n
\sqrt n
n。我们分别对
k
≤
t
k \leq t
k≤t和
k
>
t
k > t
k>t两种情况进行分析。
- 当 k ≤ t k \leq t k≤t时,因为 h k = 2 k – 1 h_k = 2^k – 1 hk=2k–1,因此可知 h k + 1 h_k + 1 hk+1和 h k + 2 h_k + 2 hk+2是互为质数,且数量级为 O ( h k ) O(h_k) O(hk)。 因此, O ( n ⋅ h k ) O(n·h_k) O(n⋅hk)步排序足以对数据进行排序。 由于 h k h_k hk是一个几何级数,当 k = 1 , . . . , t k = 1, ..., t k=1,...,t, O ( h t ) = O ( n ) O(h_t) = O(\sqrt n) O(ht)=O(n)时候,所有 h k h_k hk的总和为 O ( n ⋅ n ) O(n· \sqrt n) O(n⋅n)。 因此,当 k ≤ t k \leq t k≤t时,数量级为 O ( n ⋅ n ) O(n· \sqrt n) O(n⋅n)。
- 当 k > t k > t k>t时,当序列放置在包含 h k h_k hk 列的二维数组时,每列中都有 n / h k n/hk n/hk 元素。由于插入排序具有二次复杂性,因此需要 O ( ( n / h k ) 2 ) O((n/h_k)^2) O((n/hk)2) 步对每列进行排序。有 h k h_k hk 列,由于二维数组有 h k h_k hk列,因此对整个数据序列进行 h k h_k hk-排序 需要 O ( ( n / h k ) 2 ⋅ h k ) = O ( n ⋅ n / h k ) O((n/h_k)^2·h_k) = O(n·n/h_k) O((n/hk)2⋅hk)=O(n⋅n/hk) 步。同样, n / h k n/h_k n/hk也是一个几何级数, O ( n / h t ) = O ( n ) O(n/h_t) = O(\sqrt n) O(n/ht)=O(n)。因此,当 k > t k > t k>t时,同样数量级为 O ( n ⋅ n ) O(n· \sqrt n) O(n⋅n)。
可以证明,对于这个h序列,上界是比较稳定的。但是还有另外的h序列可以使希尔排序更加高效。
定理2:对于h序列:1,2,3,4,6,8,9,12,16,...,2p3q,...
,希尔排序对n个数据排序需要
O
(
n
⋅
l
o
g
n
2
)
O(n·logn^2)
O(n⋅logn2)个步骤。
[
P
r
a
79
]
^{[Pra 79]}
[Pra79](序列h
是增量取法)
证明:如果
g
=
2
g = 2
g=2且
h
=
3
h = 3
h=3,则
γ
(
g
,
h
)
=
(
g
−
1
)
⋅
(
h
−
1
)
–
1
=
1
γ(g,h)=(g-1)·(h-1)– 1 = 1
γ(g,h)=(g−1)⋅(h−1)–1=1,即在一个2,3-排序的序列中,每个元素右侧只有下一个元素可以更小。
O
(
n
)
O(n)
O(n)个步骤足以用插入排序对序列进行排序。分别考虑索引为奇数和偶数的元素,很明显
O
(
n
)
O(n)
O(n)个步骤足以使4,6-排序的序列2-排序。同理
O
(
n
)
O(n)
O(n)个步骤足以使6,9-排序的序列3-排序,以此类推。
上面的h序列具有以下属性:每个
h
k
h_k
hk也会出现
2
h
k
2h_k
2hk和
3
h
k
3h_k
3hk的性质,因此
O
(
n
)
O(n)
O(n)个步骤足以满足每个
h
k
h_k
hk。h序列中总共有
l
o
g
n
2
logn^2
logn2个元素;因此,这种h序列的复杂度为
O
(
n
⋅
l
o
g
n
2
)
O(n·logn^2)
O(n⋅logn2)。
这个h序列在渐近性方面表现最佳,特别是,如果数据序列是基本有序的,则待排序序列中元素较少时该序列效果更好,因为即使只有几个排序步骤,也必须(通过for-i循环)对每个
h
k
h_k
hk数据进行扫描。
通过将这两个定理的结合,可以得到在实际中性能更好的h序列,例如Sedgewick的h序列 [ S e d 96 ] ^{[Sed 96]} [Sed96]。但不幸的是,似乎没有 h 序列能够让希尔排序在最差情况下表现达到 O ( n ⋅ l o g n ) O(n·logn) O(n⋅logn) [ S e d 96 ] ^{[Sed 96]} [Sed96]。 平均复杂度是否能达到 O ( n ⋅ l o g n ) O(n·logn) O(n⋅logn)也是一个尚未解决的问题。
排序网络
如果数据的插入排序替换为冒泡排序,则可以将希尔排序实现为排序网络。使用 2p3q 的h 序列时,排序网络由
O
(
n
⋅
l
o
g
n
2
)
O(n·logn^2)
O(n⋅logn2)个比较器组成。这与双调排序的比较器个数相同。
下图显示了n=8对应的排序网络。
模拟
(用于模拟希尔排序的Java小程序)
参考文献
- Shellsort was originally published by D.L. Shell [She 59].
- [Knu 73] D.E. Knuth: The Art of Computer Programming, Vol. 3 - Sorting and Searching. Addison-Wesley (1973)
- [Pra 79] V. Pratt: Shellsort and Sorting Networks. Garland, New York (1979)
- [PS 65] A. Papernov, G. Stasevic: A Method of Information Sorting in Computer Memories. Problems of Information Transmission 1, 63-75 (1965)
- [Sed 88] R. Sedgewick: Algorithms. 2nd edition, Addison-Wesley (1988)
- [Sed 96] R. Sedgewick: Analysis of Shellsort and Related Algorithms. In: Josep Díaz,
- Maria Serna (Eds.): Algorithms - ESA '96, Fourth Annual European Symposium, Barcelona, Lecture Notes in Computer Science, Vol. 1136, Springer, 1-11 (1996)
- [She 59] D.L. Shell: A High-Speed Sorting Procedure. Communications of the ACM, 2, 7, 30-32 (1959)
原作者信息
汉斯·沃纳·朗
- 1980-1990年 基尔大学基督教-阿尔布雷希特斯大学计算机科学研究所 研究助理
- 1990-1994年 ISATEC 软硬件有限公司 董事兼总经理
- 1994-2017年 弗伦斯堡应用科学大学计算机科学教授
- 2012-2014年 弗伦斯堡应用科学大学信息与通信学院 系主任
- 2014-2016年 弗伦斯堡应用科学大学信息与通信学院 教务长
碎碎念
昨晚,啊应该是今天凌晨00:00的时候我给教授发了个邮件,想翻译这个文章,咱们凌晨2:30人家就回复我了。
不论是发邮件的时候还是今天翻译的时候,我深刻感受到了自己是个咸鱼。写完邮件我就想哭,给德国教授写邮件用英语,说不定还有语法错误……然后今天翻译文章时候看到一些名词也是头大。好在我谷歌+必应+百度+查词典最后还是顽强地把它翻译完了。十分欢迎各位大佬批评指正,算了,( ๑ˊ•̥▵•)੭₎₎指正就好了,批评太多我会哭的。
我是安安,萝莉永不服输 (ง •̀o•́)ง。