合并两组不均匀的项目

Merge two uneven sets of items

我正在尝试将一组项目相当均匀地分配到另一组中,并且正在寻找可以提供帮助的算法。

例如,A 组有 42 个项目,B 组有 16 个项目。我想将两个组混合在一起,以便 B 在 A 中相当均匀地分布。因此,合并后的组看起来像:{AA B AAA B AA B AA B AAA.....} 当然,这很容易,如果 A 是 B 的倍数,但我的需要通常不是这种情况。

您可以从获取一组中另一组项目之间的项目数开始:

float number_between = bigger_set.size() / smaller_set.size();

遍历较大的集合,从累加器(用 number_between 初始化)中的每个循环减去 1,每当累加器低于 0 时从较小的集合中插入一个项目,并用 [= 刷新它13=]:

float accumulator = number_between;
foreach(item : bigger_set) {
  result.add(item);
  accumulator = accumulator - 1;
  if (accumulator < 0) {
      result.add(next from smaller_set);
      accumulator = accumulator + number_between;
  } 
} 

编辑

更改为:

float number_between = (bigger_set.size() +1) / smaller_set.size();

如果您想确保更大的列表既是结果列表的开始又是结果列表的结束。

编辑 2

注意使用浮点运算可能会引入舍入和下溢错误。

例如,如果您使用的是 IEEE 单精度(尾数为 24 位 ~ 7 位十进制数字)并且较大的列表比较小的列表大 10^7 或更多,行 accumulator = accumulator - 1 会下溢(你会得到一个完全由较大集合和较小集合的 none 组成的结果)。

此外,四舍五入可能会导致在用尽时尝试从较小的列表中提取更多项目。

1) 您可以连接这两个组并从组合组中进行简单采样,例如通过打乱元素并遍历打乱后的组合集。

2) 如果你更愿意按顺序进行,你可以从每个组中抽样,概率为 size(A) / (size(A) + size(B))size(B) / (size(A) + size(B)),其中 size(A)size(B) 是当前的A 组和 B 组中尚未采样的元素数。换句话说,如果 U 是来自 Uniform(0,1) 随机数生成器的抽取:

if U <= size(A) / (size(A) + size(B))
   randomly draw next observation from A
else
   randomly draw next observation from B

在这两种方法中,AB 最终均匀分布在整个范围内,这是 "fairly even distribution".[=21= 的统计描述]

您没有指定语言,所以这里是 Ruby 中两种方法的具体实现。我已将设置大小减半以保持合理的输出长度,显然由于使用随机性,它们每次 运行 时都会产生不同的结果。

第一种方法:

a = ['A'] * 21
b = ['B'] * 8
c = (a + b).shuffle
puts c.join(',')

例如,产生了以下输出:

A,A,A,A,A,B,A,A,A,A,A,B,B,B,A,B,A,A,A,A,A,A,A,A,A,B,B,A,B

第二种方法:

a = ['A'] * 21
b = ['B'] * 8
c = []    
while a.length > 0 || b.length > 0
  c << (rand <= (a.length / (a.length + b.length).to_f) ? a.shift : b.shift)
end    
puts c.join(',')

例如,产生了以下输出:

A,A,B,A,A,A,B,B,A,A,A,A,A,A,A,B,B,B,A,A,A,B,A,A,A,B,A,A,A

好吧,我一直在研究这个问题,并提出了一个适合我的目的的解决方案。我基本上将较大的项目混合到较小的项目中,然后循环返回,直到我 运行 出较大的项目。

For Each item In smallerList
  mergedList.add(smallerID)
Next

itemsRemaining = biggerList.Count

While itemsRemaining > 0
  index = 0

  For i = 1 To smallerList.Count
    If index >= mergedList.Count or itemsRemaining = 0 Then Continue While

    mergedList.Insert(index , largerID)
    index += 2 + loopCount
    itemsRemaining -= 1
  Next

  loopCount += 1
End While

然后我可以用两个列表中的实际项目替换 ID。

因此,对于我的原始示例(第 1 组有 42 个项目,第 2 组有 16 个项目),我最终得到:

111 2 111 2 111 2 111 2 111 2 111 2 111 2 111 2 111 2 111 2 11 2 11 2 11 2 11 2 11 2 11 2

它有点提前加载,但就我的目的而言,这会很好。