大小不同于 2^n 的最佳 Batcher 奇偶合并网络
Posted
技术标签:
【中文标题】大小不同于 2^n 的最佳 Batcher 奇偶合并网络【英文标题】:Optimal Batcher odd-even merge networks for sizes different than 2^n 【发布时间】:2016-01-24 01:21:59 【问题描述】:这些天来,我一直在尝试使用最少数量的比较交换单元(size 最佳,而不是 depth)实现最大 32 大小的排序网络.到目前为止,我已经能够使用以下资源来生成我的网络:
对 0 到 16 的网络进行排序:Perl 的 Algorithm::Networksort
module 使用“最佳”算法。不幸的是,它只提供最知名的网络,直到大小为 16。
排序网络 17 到 23:Using Symmetry and Evolutionary Search to Minimize Sorting Networks by Valsalam 和 Miikkulainen。
Baddar 的论文 Finding Better Sorting Networks 给出了已知对 0 到 32 网络进行排序所需的最小比较交换单元数量(不是最新的,因为 Valsalam 和 Miikkulainen 为大小为 17、18、 19、20、21 和 22)以及用于查找它们的方法:基本上,必须将数组分成两部分排序,然后使用最知名的排序网络对两半进行排序,然后使用奇偶合并它们合并网络(对应Batcher's odd-even mergesort的合并步骤)。
Wikipedia 页面为 Batcher 的奇偶合并排序提供了以下 Python 实现:
def oddeven_merge(lo, hi, r):
step = r * 2
if step < hi - lo:
yield from oddeven_merge(lo, hi, step)
yield from oddeven_merge(lo + r, hi, step)
yield from [(i, i + r) for i in range(lo + r, hi - r, step)]
else:
yield (lo, lo + r)
def oddeven_merge_sort_range(lo, hi):
""" sort the part of x with indices between lo and hi.
Note: endpoints (lo and hi) are included.
"""
if (hi - lo) >= 1:
# if there is more than one element, split the input
# down the middle and first sort the first and second
# half, followed by merging them.
mid = lo + ((hi - lo) // 2)
yield from oddeven_merge_sort_range(lo, mid)
yield from oddeven_merge_sort_range(mid + 1, hi)
yield from oddeven_merge(lo, hi, 1)
def oddeven_merge_sort(length):
""" "length" is the length of the list to be sorted.
Returns a list of pairs of indices starting with 0 """
yield from oddeven_merge_sort_range(0, length - 1)
oddeven_merge
步骤已被隔离,因此很容易单独使用它来生成合并原始数组的两个已排序部分所需的索引对。但是,此实现仅在数组大小为 2 的幂时才有效。因此,它只允许我找到大小为 32 的排序网络所需的最小已知比较交换单元数。使用最高索引让我能够找到大小为 31 的等效排序网络,但删除更多对并没有为小于 31 的大小产生最知名的结果。
Perl 的Algorithm::Networksort
模块提供了另一种 Batcher 的奇偶合并排序实现,它适用于任何大小的数组,不仅是 2 的幂。因此,我决定看看它是否可以提取合并步骤从算法。这是 Python 的等价物(它也对应于 Knuth 在计算机编程的艺术 vol. 3 中描述的算法):
def oddeven_merge_sort(length):
t = math.ceil(math.log2(length))
p = 2 ** (t - 1)
while p > 0:
q = 2 ** (t - 1)
r = 0
d = p
while d > 0:
for i in range(length - d):
if i & p == r:
yield (i, i + d)
d = q - p
q //= 2
r = p
p //= 2
不幸的是,这个算法在我看来有点神秘,我根本无法从中提取合并部分。我设法推导出一个合并网络,它为我提供了 24 大小的排序网络所需的最小已知比较交换单元数量,但我使用的技巧并没有扩展到任何其他大小(据我的理解,这绝对不是奇偶合并)。
我尝试了更多的方法来调整 Batcher 奇偶合并排序中的合并步骤,以适应大小不是 2 的幂的数组,但我无法找到最知名的大小为 25 的排序网络, 26, 27, 28, 29 和 30。我如何推导出这个合并步骤来找到拼图的缺失部分?
【问题讨论】:
【参考方案1】:Perl 算法mentions in a comment 是 Knuth 的搜索和排序中的算法 5.2.2M。
反过来,Knuth 提到它在 p = 1 时将已排序的序列合并在一起。因此,要生成合并任何 N 的序列的对,您只需使用 p = 1 运行算法:
def oddeven_merge_step(length):
t = math.ceil(math.log2(length))
q = 2 ** (t - 1)
r = 0
d = 1
while d > 0:
for i in range(length - d):
if i & 1 == r:
yield (i, i + d)
d = q - 1
q //= 2
r = 1
请注意,Batcher 的奇偶合并步骤需要排序序列交错(偶数、奇数、偶数、...),但会生成一个连续的排序序列。
例如对于 N = 25,它会生成以下网络:
【讨论】:
以上是关于大小不同于 2^n 的最佳 Batcher 奇偶合并网络的主要内容,如果未能解决你的问题,请参考以下文章