问题描述
我想知道是否有可能对数组中的项目进行“排序”以将它们以“相等”的间距放置。
一个例子是数百个单词,所以:
Apple - 1
Banana - 2
Pineapple - 3
Orange - 4
这是一个数组:
[ 'Apple','Apple','Banana','Pineapple','Orange' ]
[ 1,1,2,3,4 ]
我要实现的目标与此类似:
[ 'Apple','Orange','Pineapple' ]
[ 1,4,3 ]
通过这种转换,Pineapple
在其他'Pineapple'之间有1个项目偏移,并且Apple
被放置在[0]和[3]位置。
在开始实施之前,我正在寻找一个已经发明的解决方案-它应该与标准偏差有关吗?
解决方法
首先根据出现的次数对单词进行排序。然后遍历它们,首先填充所有偶数索引,然后填充所有奇数索引。
第一个单词最多可以填充所有偶数索引。在大多数现代阵列中,始终应至少有偶数个索引的插槽与奇数个索引的插槽一样多。如果您的语言不符合此条件(即基于一个的数组),请根据可用插槽的数量选择偶数或奇数。
第二个最常见的单词最多只能出现一次,最多出现一次,因此,不可能以这种方式在两个相邻的插槽中出现相同的单词。
一个简单的python实现看起来像这样:
import math
def spaced_ordering(words):
words = sorted(words,key=words.count,reverse=True)
output = [None] * len(words)
for i in range(0,math.ceil(len(words) / 2)):
output[i * 2] = words[i]
for i in range(0,math.floor(len(words) / 2)):
output[i * 2 + 1] = words[math.ceil(len(words) / 2) + i]
return output
注意:上面的实现既不完全有效,也不完全不花哨,也不包括检查有效输入(例如,一个单词出现超过math.ceil(len(words) / 2)
次时会发生什么)。它只是用来说明基本原理。
您要寻找的算法类别称为多路复用。多路复用器接收几个输入流,并创建一个输出流,一次从输入中选择一项。有许多不同的复用策略。我将描述一种易于实现且性能良好的产品。
通常的想法是,每个项目都有一个名称,费率和累加器,并且其累加器中值最大的项目接下来选择。在问题给出的示例中,费率是Apple的2,香蕉的1,菠萝的3和橙的1。比率的总和是周期,即7。
算法操作如下:
initialize all accumulators to 0
for each slot in one period:
choose the item with the largest accumulator,and add it to the output
update each accumulator by adding the rate to the accumulator
subtract the period from the accumulator of the chosen item
下表显示了算法如何进行。插槽标记为S1至S7。对于每个插槽,都有两列数字,每个项目的累加器值,以及对累加器的调整。
在插槽1中,选择了橙色,因此对累加器的调整为+1 -7 = -6
(加上速率,减去周期)。对于其他所有项目,调整均等于费率。请注意,所有累加器均从0开始,并在第七个插槽后返回0。因此,该算法可以在任意数量的插槽中运行,并且只需重复相同的模式即可。
Name Rate __S1__ __S2__ __S3__ __S4__ __S5__ __S6__ __S7__
Orange 1/7 0 -6 -6 +1 -5 +1 -4 +1 -3 +1 -2 +1 -1 +1 0
Banana 1/7 0 +1 1 +1 2 +1 3 -6 -3 +1 -2 +1 -1 +1 0
Apple 2/7 0 +2 2 +2 4 -5 -1 +2 1 +2 3 -5 -2 +2 0
Pineapple 3/7 0 +3 3 -4 -1 +3 2 +3 5 -4 1 +3 4 -4 0
Selected item: Orange Pine Apple Banana Pine Apple Pine
这是Python的实现:
items = ['Apple','Apple','Banana','Pineapple','Orange']
# Convert the list of items into a list that contains the [name,rate,accumulator]
# for each item. The initial value for the accumulator is 0
counts = {}
for item in items:
counts[item] = counts.get(item,0) + 1
rates = counts.items()
rates = [[name,0] for (name,rate) in rates]
rates.sort(key=lambda x:x[1])
# Run the multiplexer,which
# adds the item with the largest accumulator to the output
# updates all the accumulators by adding the rate to the accumulator
# subtracts the period from the chosen accumlator
output = []
period = len(items)
for i in range(period):
best = 0
for j in range(len(rates)):
if rates[j][2] > rates[best][2]: # compare accumulators
best = j
rates[j][2] += rates[j][1] # update accumulator
rates[best][2] -= period
output.append(rates[best][0]) # add an item to the output
print output # ['Orange','Pineapple']