Distribute an integer amount by a set of slots as evenly as possible

前端 未结 5 564
逝去的感伤
逝去的感伤 2021-02-02 09:47

I am trying to figure an elegant way of implementing the distribution of an amount into a given set of slots in python.

For example:

7 oranges distributed onto 4

5条回答
  •  耶瑟儿~
    2021-02-02 10:08

    Mad Physicist answer is perfect. But if you want to distribute the oranges uniformley on the plates (eg. 2 3 2 3 vs 2 2 3 3 in the 7 oranges and 4 plates example), here's an simple idea.

    Easy case

    Take an example with 31 oranges and 7 plates for example.

    Step 1: You begin like Mad Physicist with an euclidian division: 31 = 4*7 + 3. Put 4 oranges in each plate and keep the remaining 3.

    [4, 4, 4, 4, 4, 4, 4]
    

    Step 2: Now, you have more plates than oranges, and that's quite different: you have to distribute plates among oranges. You have 7 plates and 3 oranges left: 7 = 2*3 + 1. You will have 2 plates per orange (you have a plate left, but it doesn't matter). Let's call this 2 the leap. Start at leap/2 will be pretty :

    [4, 5, 4, 5, 4, 5, 4]
    

    Not so easy case

    That was the easy case. What happens with 34 oranges and 7 plates?

    Step 1: You still begin like Mad Physicist with an euclidian division: 34 = 4*7 + 6. Put 4 oranges in each plate and keep the remaining 6.

    [4, 4, 4, 4, 4, 4, 4]
    

    Step 2: Now, you have 7 plates and 6 oranges left: 7 = 1*6 + 1. You will have one plate per orange. But wait.. I don't have 7 oranges! Don't be afraid, I lend you an apple:

    [5, 5, 5, 5, 5, 5, 4+apple]
    

    But if you want some uniformity, you have to place that apple elsewhere! Why not try distribute apples like oranges in the first case? 7 plates, 1 apple : 7 = 1*7 + 0. The leap is 7, start at leap/2, that is 3:

    [5, 5, 5, 4+apple, 5, 5, 5]
    

    Step 3. You owe me an apple. Please give me back my apple :

    [5, 5, 5, 4, 5, 5, 5]
    

    To summarize : if you have few oranges left, you distribute the peaks, else you distribute the valleys. (Disclaimer: I'm the author of this "algorithm" and I hope it is correct, but please correct me if I'm wrong !)

    The code

    Enough talk, the code:

    def distribute(oranges, plates):
        base, extra = divmod(oranges, plates) # extra < plates
        if extra == 0:
            L = [base for _ in range(plates)]
        elif extra <= plates//2:
            leap = plates // extra
            L = [base + (i%leap == leap//2) for i in range(plates)]
        else: # plates/2 < extra < plates
            leap = plates // (plates-extra) # plates - extra is the number of apples I lent you
            L = [base + (1 - (i%leap == leap//2)) for i in range(plates)]
        return L
    

    Some tests:

    >>> distribute(oranges=28, plates=7)
    [4, 4, 4, 4, 4, 4, 4]
    >>> distribute(oranges=29, plates=7)
    [4, 4, 4, 5, 4, 4, 4]
    >>> distribute(oranges=30, plates=7)
    [4, 5, 4, 4, 5, 4, 4]
    >>> distribute(oranges=31, plates=7)
    [4, 5, 4, 5, 4, 5, 4]
    >>> distribute(oranges=32, plates=7)
    [5, 4, 5, 4, 5, 4, 5]
    >>> distribute(oranges=33, plates=7)
    [5, 4, 5, 5, 4, 5, 5]
    >>> distribute(oranges=34, plates=7)
    [5, 5, 5, 4, 5, 5, 5]
    >>> distribute(oranges=35, plates=7)
    [5, 5, 5, 5, 5, 5, 5]
    

提交回复
热议问题