讨论
鉴于你可以只使用key参数,我暂时忽略它。
给定序列的算法如下所示:
def middle_sort_flip_OP(seq, key=None):
result = []
length = len(seq)
seq.sort(key=key)
result = seq[:length // 2]
seq.sort(key=key, reverse=True)
result.extend(seq[:length // 2])
return result
print(middle_sort_flip_OP([1, 5, 3, 7, 3, 2, 9, 8]))
# [1, 2, 3, 3, 9, 8, 7, 5]
print(middle_sort_flip_OP([1, 5, 3, 7, 3, 2, 8]))
# [1, 2, 3, 8, 7, 5]
第二个排序步骤是完全没有必要的(但计算复杂度与在 Python 中实现的 Timsort 算法的简单反转相同),因为您可以简单地将排序后的序列向后切片(确保计算正确“中间”元素的偏移量):
def middle_sort_flip(seq, key=None):
length = len(seq)
offset = length % 2
seq.sort(key=key)
return seq[:length // 2] + seq[:length // 2 - 1 + offset:-1]
print(middle_sort_flip([1, 5, 3, 7, 3, 2, 9, 8]))
# [1, 2, 3, 3, 9, 8, 7, 5]
print(middle_sort_flip([1, 5, 3, 7, 3, 2, 8]))
# [1, 2, 3, 8, 7, 5]
另一种理论上更有效的方法是分别对序列的左侧和右侧进行排序。这更有效,因为每个排序步骤都是O(N/2 log N/2),并且当组合时会给出O(N log N/2)(而不是O(N + N log N)):
def middle_sort_half(seq, key=None):
length = len(seq)
return \
sorted(seq[:length // 2], key=key) \
+ sorted(seq[length // 2:], key=key, reverse=True)
但是,这些方法要么在整个右侧大于左侧 (middle_sort_flip()) 时给出很大程度上不平衡的结果,要么具有取决于输入的初始顺序的平衡 (middle_sort_half())。
通过提取和重新组合奇偶子序列可以获得更平衡的结果。由于切片操作,这在 Python 中非常简单,并且具有与 middle_sort_flip() 相同的渐近复杂度,但具有更好的平衡属性:
def middle_sort_mix(seq, key=None):
length = len(seq)
offset = length % 2
seq.sort(key=key)
result = [None] * length
result[:length // 2] = seq[::2]
result[length // 2 + offset:] = seq[-1 - offset::-2]
return result
print(middle_sort_mix([1, 5, 3, 7, 3, 2, 9, 8]))
# [1, 3, 5, 8, 9, 7, 3, 2]
print(middle_sort_mix([1, 5, 3, 7, 3, 2, 8]))
# [1, 3, 5, 8, 7, 3, 2]
基准测试
在不使用key参数时,它们都非常相似,因为执行时间主要由复制来控制:
import random
nums = [10 ** i for i in range(1, 7)]
funcs = middle_sort_flip_OP, middle_sort_flip, middle_sort_half, middle_sort_mix
print(nums)
# [10, 100, 1000, 10000, 100000, 1000000]
def gen_input(num):
return list(range(num))
for num in nums:
print(f"N = {num}")
for func in funcs:
seq = gen_input(num)
random.shuffle(seq)
print(f"{func.__name__:>24s}", end=" ")
%timeit func(seq.copy())
print()
...
N = 1000000
middle_sort_flip_OP 542 ms ± 54.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_flip 510 ms ± 49 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_half 546 ms ± 4.28 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_mix 539 ms ± 63 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
另一方面,当key 参数不平凡时,您的方法与其他两个方法相比,函数调用量要大得多,这可能会导致middle_sort_OP() 的执行时间显着增加:
def gen_input(num):
return list(range(num))
def key(x):
return x ** 2
for num in nums:
print(f"N = {num}")
for func in funcs:
seq = gen_input(num)
random.shuffle(seq)
print(f"{func.__name__:>24s}", end=" ")
%timeit func(seq.copy(), key=key)
print()
...
N = 1000000
middle_sort_flip_OP 1.33 s ± 16.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_flip 1.09 s ± 23.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_half 1.1 s ± 27.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_mix 1.11 s ± 8.88 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
或者,更接近您的用例:
class Container():
def __init__(self, x):
self.x = x
def get_x(self):
return self.x
def gen_input(num):
return [Container(x) for x in range(num)]
def key(c):
return c.get_x()
for num in nums:
print(f"N = {num}")
for func in funcs:
seq = gen_input(num)
random.shuffle(seq)
print(f"{func.__name__:>24s}", end=" ")
%timeit func(seq.copy(), key=key)
print()
...
N = 1000000
middle_sort_flip_OP 1.27 s ± 4.44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_flip 1.13 s ± 13.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_half 1.24 s ± 12.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
middle_sort_mix 1.16 s ± 8.07 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
这似乎没有那么戏剧化。