Rounding floats while maintaining total sum equal

2019-07-29 05:06发布

I have a list of floats that add up to an integer. For circumstantial reasons, I have to iterate using a for loop x times, x being each float in the list, but since the argument for the range() function must be an integer, each float must be rounded. However, I want the total number of loops to remain equal to the sum of the original floats, which doesn't usually add up to the sum of the rounded numbers. How would you solve this problem? Thank you.

标签: python
2条回答
来,给爷笑一个
2楼-- · 2019-07-29 05:38

I recently had to solve a similar problem and decided it was something common enough to various projects of mine to generalize a python solution and package it. Check out iteround.

查看更多
爷的心禁止访问
3楼-- · 2019-07-29 05:53

Ok, this is going to be a bit mathematical:

You have a series of real numbers Xi Their sum equals N sum(Xi) = N

Let's break each real number to its floor integer and residual real part (between 0 and 1): Xi = Ri + fi

Now, you need a series of integers Yi that are as close to Xi, but are integers and also sum to N. We can break them like this: Yi = Ri + Fi (where Fi is an integer either 0 or 1).

Now we need that: sum(Yi) = sum(Xi) = N

If you break that, you'll get this equation as a requirement for the solution: sum(Fi) = sum(fi) = N - sum(Ri)

Let's denote: K = N - sum(Ri)

Now the solution is simple, choose the K elements which have the largest fi values, and assign their corresponding Fi to 1; assign the other Fi to 0.

Now you have your values for Yi which in your case are the loop sizes

Here's the code for it:

def round_series_retain_integer_sum(xs):
    N = sum(xs)
    Rs = [round(x) for x in xs]
    K = N - sum(Rs)
    assert K == round(K)
    fs = [x - round(x) for x in xs]
    indices = [i for order, (e, i) in enumerate(reversed(sorted((e,i) for i,e in enumerate(fs)))) if order < K]
    ys = [R + 1 if i in indices else R for i,R in enumerate(Rs)]
    return ys


xs = [5.2, 3.4, 2.1, 7.3, 3.25, 6.25, 8.2, 9.1, 10.1, 55.1]
ys = round_series_retain_integer_sum(xs)

print xs, sum(xs)
print ys, sum(ys)

I think I don't have any bugs, I hope you get the idea even if so

查看更多
登录 后发表回答