【问题标题】:Distributing integers using weights? How to calculate?使用权重分配整数?如何计算?
【发布时间】:2012-01-31 23:06:50
【问题描述】:

我需要根据一些权重分配一个值。例如,如果我的权重是 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 所需的输出是什么?
  • 你想要浮点数还是整数值作为返回值?这里的期望值是多少? (1,1,1) 或 (0,1,2) ?
  • 鉴于您的规格不完整,最简单的方法是:删除“圆形”。如果您需要整数结果,则在许多情况下没有精确的解决方案。在这些情况下你想要什么样的结果?
  • @Patrick:分配的金额必须是整数(美分、苹果、王国等),否则没有问题。主要标准是每个份额都应该足够接近“浮动”答案,没有参与者有投诉的理由。
  • 我认为this questionthis one 可能与您的情况相关。特别是,这些相关问题中的第二个似乎在问几乎同样的事情。

标签: python algorithm


【解决方案1】:

按预期分配第一个份额。现在你有一个更简单的问题,参与者减少了,可分配的数量也减少了。重复直到没有更多的参与者。

>>> 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 以确保总数正确。
【解决方案2】:

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

Actual:
| |   |     |

Pixel grid:
|   |   |   |

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

|   |   |   |

但是您还期望如何将 3 分成 3 个组成部分?

【讨论】:

    【解决方案3】:

    最简单的方法是计算归一化比例,这是权重总和超过目标总和的因素,然后将权重中的每个项目除以该比例。

    def distribute(total, weights):
        scale = float(sum(weights))/total
        return [x/scale for x in weights]
    

    【讨论】:

    • PS - 如果您不熟悉它,最后一行使用list comprehension,这只是将列表制作for 循环放在一行中的一种奇特方式。
    • ... 然后你的权重就不再是整数了。 round 电话显然是想要的。
    【解决方案4】:

    如果您期望以 (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
    

    【讨论】:

    • 我希望分布只包含整数值。我没有在我的原始问题中指定这一点,但它暗示在我的代码中。
    • 更新了答案以包含返回整数分布的解决方案
    • -1 它不起作用。例如,sum(distribute(19.0, 10*[1.0])) 产生 18.0;应该是19.0
    • 啊,是的,舍入错误越来越多……现在应该更好
    猜你喜欢
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    • 2018-10-13
    • 2021-10-07
    • 2020-04-08
    • 2017-06-17
    • 2016-01-24
    • 1970-01-01
    相关资源
    最近更新 更多