Distributing integers using weights? How to calculate?

前端 未结 4 385
鱼传尺愫
鱼传尺愫 2020-12-10 09:03

I need to distribute a value based on some weights. For example, if my weights are 1 and 2, then I would expect the column weighted as 2 to have twice the value as the colum

相关标签:
4条回答
  • 2020-12-10 09:33

    The easiest approach is to calculate the normalization scale, which is the factor by which the sum of the weights exceeds the total you are aiming for, then divide each item in your weights by that scale.

    def distribute(total, weights):
        scale = float(sum(weights))/total
        return [x/scale for x in weights]
    
    0 讨论(0)
  • 2020-12-10 09:50

    Distribute the first share as expected. Now you have a simpler problem, with one fewer participants, and a reduced amount available for distribution. Repeat until there are no more participants.

    >>> 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
    ...
    >>>
    
    0 讨论(0)
  • 2020-12-10 09:50

    You have to distribute the rounding errors somehow:

    Actual:
    | |   |     |
    
    Pixel grid:
    |   |   |   |
    

    The simplest would be to round each true value to the nearest pixel, for both the start and end position. So, when you round up block A 0.5 to 1, you also change the start position of the block B from 0.5 to 1. This decreases the size of B by 0.5 (in essence, "stealing" the size from it). Of course, this leads you to having B steal size from C, ultimately resulting in having:

    |   |   |   |
    

    but how else did you expect to divide 3 into 3 integral parts?

    0 讨论(0)
  • 2020-12-10 09:56

    If you expect distributing 3 with weights of (1,2,3) to be equal to (0.5, 1, 1.5), then the rounding is your problem:

    weighted_value = round(p*total)
    

    You want:

    weighted_value = p*total
    

    EDIT: Solution to return integer distribution

    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
    
    0 讨论(0)
提交回复
热议问题