使用权重分配整数?如何计算?

6

我需要根据一些权重来分配值。例如,如果我的权重是1和2,则我希望以2为权重的列具有与以权重1加权的列相比两倍的值。

我有一些Python代码来演示我想要做的事情,以及问题:

def distribute(total, distribution):
    distributed_total = []
    for weight in distribution:
        weight = float(weight)
        p = weight/sum(distribution)
        weighted_value = round(p*total)
        distributed_total.append(weighted_value)
    return distributed_total

for x in xrange(100):
    d = distribute(x, (1,2,3))
    if x != sum(d):
        print x, sum(d), d

上面的代码展示了许多情况,其中分配一个值导致分配的总和与原始值不同。例如,使用权重为(1,2,3)分配3会得到(1,1,2),总计为4。
最简单的修复分配算法的方法是什么?
更新:
我希望分配的值是整数值。它们分配成整数并不重要,只要它们总和正确,并且它们“尽可能接近”正确的分配即可。
(通过正确的分配,我指的是非整数分配,并且我还没有完全定义“尽可能接近”的含义。也许有几个有效的输出,只要它们总和原始值即可。)

那么,使用权重(1,2,3)分配3的期望输出是什么? - Avaris
你想要返回浮点数还是整数?这里期望的值是什么?(1,1,1) 还是 (0,1,2)? - ElKamina
根据您的不完整规格,最简单的方法是删除“round”。如果您需要整数结果,在许多情况下没有精确解决方案。在这些情况下,您想要什么样的结果? - Patrick
1
@Patrick:分配的金额必须是整数(以分、苹果、王国或其他单位计算),否则就会出问题。主要标准是每个份额应该足够接近“浮点”答案,以便没有参与者有理由抱怨。 - John Machin
@JohnMachin 谢谢,这是我正在寻找的好定义。并且+1指出如果我们不使用整数值就没有问题。 - Buttons840
1
我认为这个问题这个问题可能与你的情况相关。特别是第二个链接的问题几乎在问同样的事情。 - David Z
4个回答

9
按照预期分配第一份股份。现在你有一个更简单的问题,参与者减少了一个,可分配金额也减少了。重复操作直到没有更多的参与者。
>>> def distribute2(available, weights):
...     distributed_amounts = []
...     total_weights = sum(weights)
...     for weight in weights:
...         weight = float(weight)
...         p = weight / total_weights
...         distributed_amount = round(p * available)
...         distributed_amounts.append(distributed_amount)
...         total_weights -= weight
...         available -= distributed_amount
...     return distributed_amounts
...
>>> for x in xrange(100):
...     d = distribute2(x, (1,2,3))
...     if x != sum(d):
...         print x, sum(d), d
...
>>>

这个解决方案很好,因为它不需要检查在 for 循环中分配给前一个“桶”的值。它基本上对最后一个桶进行 +1 或 -1 操作,以确保总数正确。 - Buttons840
甚至适用于非整数权重。 - Jairo Andres Velasco Romero

2

你必须以某种方式分配舍入误差:

Actual:
| |   |     |

Pixel grid:
|   |   |   |

最简单的方法是将每个真实值四舍五入到最近的像素,对于起始位置和结束位置都是如此。因此,当您将块A 0.5四舍五入到1时,您还将块B的起始位置从0.5更改为1。这会使B的大小减小0.5(实质上是从中“窃取”大小)。当然,这会导致B从C中窃取大小,最终导致:

|   |   |   |

但是你还有什么其他方法可以将3分成3个整数部分呢?


1
如果你期望使用权重(1,2,3)将3分配成(0.5,1,1.5)的话,那么舍入就是你的问题了。
weighted_value = round(p*total)

你想要:

weighted_value = p*total

编辑:返回整数分布的解决方案

def distribute(total, distribution):
  leftover = 0.0
  distributed_total = []
  distribution_sum = sum(distribution)
  for weight in distribution:
    weight = float(weight)
    leftover, weighted_value = modf(weight*total/distribution_sum + leftover)
    distributed_total.append(weighted_value)
  distributed_total[-1] = round(distributed_total[-1]+leftover) #mitigate round off errors
  return distributed_total

我期望这个分布只包含整数值。虽然我在原问题中没有明确说明,但是在我的代码中已经暗示了这一点。 - Buttons840
更新了答案以包含返回整数分布的解决方案 - Greg Ra
-1 它不起作用。例如,sum(distribute(19.0, 10*[1.0])) 产生的结果是 18.0,但应该是 19.0 - John Machin
啊,是的,舍入误差悄悄地出现了...现在应该好多了。 - Greg Ra

1
最简单的方法是计算归一化比例,即权重总和超过您的目标总和的因子,然后将权重中的每个项目除以该比例。
def distribute(total, weights):
    scale = float(sum(weights))/total
    return [x/scale for x in weights]

PS - 如果您不熟悉它,最后一行使用的是列表推导式,这只是一种将列表生成的“for”循环放在一行中的花哨方式。 - cheeken
1
...然后你的权重就不再是整数了。这显然是从round调用中想要的。 - derobert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接