下面的程序是一个低成本的启发式程序。它所做的是将值分布在“桶”中,通过在一轮中从排序列表的一端选择值,在下一轮从另一端选择值,将大值与小值一起放置。以循环方式进行分配可确保满足有关每个桶的元素数量的规则。它是一种启发式而不是算法,因为它往往会产生好的解决方案,但不能保证不存在更好的解决方案。
理论上,如果有足够的值,并且它们是均匀分布或正态分布的,那么将值随机放置在桶中可能会导致桶的均值相同。假设数据集很小,这种启发式方法提高了一个好的解决方案的机会。更多地了解数据集的大小和统计分布将有助于设计更好的启发式或算法。
from random import randint, seed
from itertools import cycle,chain
def chunks(q, n):
q = list(q)
for i in range(0, len(q), n):
yield q[i:i+n]
def shuffle(q, n):
q = list(q)
m = len(q)//2
left = list(chunks(q[:m],n))
right = list(chunks(reversed(q[m:]),n)) + [[]]
return chain(*(a+b for a,b in zip(left, right)))
def listarray(n):
return [list() for _ in range(n)]
def mean(q):
return sum(q)/len(q)
def report(q):
for x in q:
print mean(x), len(x), x
SIZE = 5
COUNT= 37
#seed(SIZE)
data = [randint(1,1000) for _ in range(COUNT)]
data = sorted(data)
NBUCKETS = (COUNT+SIZE-1) // SIZE
order = shuffle(range(COUNT), NBUCKETS)
posts = cycle(range(NBUCKETS))
buckets = listarray(NBUCKETS)
for o in order:
i = posts.next()
buckets[i].append(data[o])
report(buckets)
print mean(data)
由于排序步骤,复杂性是对数的。这些是样本结果:
439 5 [15, 988, 238, 624, 332]
447 5 [58, 961, 269, 616, 335]
467 5 [60, 894, 276, 613, 495]
442 5 [83, 857, 278, 570, 425]
422 5 [95, 821, 287, 560, 347]
442 4 [133, 802, 294, 542]
440 4 [170, 766, 301, 524]
418 4 [184, 652, 326, 512]
440
请注意,对桶大小的要求占主导地位,这意味着如果原始数据中的方差很大,则均值不会接近。您可以尝试使用此数据集:
data = sorted(data) + [100000]
包含的桶100000
将至少获得另外 3 个基准。
我想出了这个启发式的想法,如果一群孩子递给一包不同面额的钞票,并告诉他们按照这个游戏的规则分享它们,这就是他们会做的事情。它在统计上是合理的,并且 O(log(N))。