Allocate an array of integers proportionally compensating for rounding errors

后端 未结 5 688
暖寄归人
暖寄归人 2021-02-04 12:29

I have an array of non-negative values. I want to build an array of values who\'s sum is 20 so that they are proportional to the first array.

This would be an easy probl

5条回答
  •  时光说笑
    2021-02-04 13:25

    Your problem is similar to a proportional representation where you want to share N seats (in your case 20) among parties proportionnaly to the votes they obtain, in your case [3, 3, 3, 3, 3, 3, 18]

    There are several methods used in different countries to handle the rounding problem. My code below uses the Hagenbach-Bischoff quota method used in Switzerland, which basically allocates the seats remaining after an integer division by (N+1) to parties which have the highest remainder:

    def proportional(nseats,votes):
        """assign n seats proportionaly to votes using Hagenbach-Bischoff quota
        :param nseats: int number of seats to assign
        :param votes: iterable of int or float weighting each party
        :result: list of ints seats allocated to each party
        """
        quota=sum(votes)/(1.+nseats) #force float
        frac=[vote/quota for vote in votes]
        res=[int(f) for f in frac]
        n=nseats-sum(res) #number of seats remaining to allocate
        if n==0: return res #done
        if n<0: return [min(x,nseats) for x in res] # see siamii's comment
        #give the remaining seats to the n parties with the largest remainder
        remainders=[ai-bi for ai,bi in zip(frac,res)]
        limit=sorted(remainders,reverse=True)[n-1]
        #n parties with remainter larger than limit get an extra seat
        for i,r in enumerate(remainders):
            if r>=limit:
                res[i]+=1
                n-=1 # attempt to handle perfect equality
                if n==0: return res #done
        raise #should never happen
    

    However this method doesn't always give the same number of seats to parties with perfect equality as in your case:

    proportional(20,[3, 3, 3, 3, 3, 3, 18])
    [2,2,2,2,1,1,10]
    

提交回复
热议问题