roundingfloorceil

How to select from a vector of fractions and respect the number of elements?


I have a vector of fractions a = [0.04352034965262487, 0.480655360084497, 0.02655889003246612, 0.449265400230412, 0.0] of type float64 such that of course sum(a)=1. These fractions represent persentages of some number K=128. So, multiplying a by K we obtain the number of elements in each fraction a*K =[5.570604755535983, 61.523886090815616, 3.3995379241556636, 57.50597122949274, 0.0].

I want to transform this vector a*K into an int vector and respect the sum of elements of a*K to be K. What I am doing is floor but this will give 126 and not 128. When I do round I obtain 129 and when I do ceil I obtain 130.

I thought of applying ceil and then remove randomly the 2 elements that exceeds 128. Is there a better way?


Solution

  • This can be accomplished by first applying floor to a*K then pick n items that had the largest fractional parts and add one to them, where n is K - sum(floor(a*K))

    In Python:

    from math import floor
    
    aK = [5.570604755535983, 61.523886090815616, 3.3995379241556636, 57.50597122949274, 0.0]
    K = 128
    
    # Sum of floored is less than K.
    floored = [floor(x) for x in aK]
    print(floored, sum(floored))
    
    # Add 1 to each items with largest fractional part
    frac = [x - y for x, y in zip(aK,floored)]
    frac_rank = sorted(range(len(frac)), key=frac.__getitem__, reverse=True)
    for i in frac_rank[:K - sum(floored)]:
        floored[i] += 1
    
    # Now floored adds up to K
    print(floored, sum(floored))