Search code examples
pythonpython-3.xlistmathmathematical-optimization

How to divide a number value in N unevenly parts using Python & following a set of min- & max requirement?


I have a fairly basic math problem in Python & I dont know where to look.

Explanation:

  • I have 100 dollars.
  • I have 10 friends.
  • I want to unevenly distribute my money across my friends.
  • I want to this over & over again using a algorithm.

My only requirements are the following:

  • The smallest amount any friend can receive should be > $5.
  • The biggest amount any friend can receive should be < $40.

from random import randint, shuffle

def divide_number(number, parts_number, allow_zero = False ):

    if (parts_number > number):
        raise ValueError("Number of parts can't be higher than the number");

    parts = {currency: []}
    number_rest = number

    for i in range(1, parts_number + 1):
        if (i == parts_number):
            parts[currency].append(number_rest)
            break
        else:
            new_number = randint(0, number_rest) if allow_zero else randint(1, (number_rest - 
(parts_number - i)) // 2)

        number_rest -= new_number
        parts[currency].append(new_number)

    return parts

Running the function:

divide_number(100, 10)

Output

[2, 37, 8, 10, 2, 4, 4, 4, 26, 3]

This piece of code I've found online seems to work just great but it doesn't meet my requirements. How do I alter it so it does meet my requirements regarding min and max values?

So basicly, I want to go from unfair distribution to fair distribution.

enter image description here


Solution

  • Is is not easy to make rather fair distribution with limits. For reasonable sum and parts (p) values we can make p cells (friend pockets) and randomly put coin by coin into them (if possible)

    import random
    
    def randparts(summ, p, minn, maxx):
        maxx = maxx - minn
        summ -= p * minn
        if summ < 0:
            return None
        if p * maxx  >=  summ * 2:
            lst = [0] * p
            while summ > 0:
                r = random.randrange(p)
                if lst[r] < maxx:
                    summ -= 1
                    lst[r] += 1
        else:
            lst = [maxx] * p
            summ = maxx * p - summ
            while summ > 0:
                r = random.randrange(p)
                if lst[r] > 0:
                    summ -= 1
                    lst[r] -= 1
        for i in range(len(lst)):
            lst[i] += minn
        return lst
    
    print(randparts(100, 10, 5, 40))
    
    >>>[7, 17, 8, 10, 8, 8, 9, 12, 10, 11]
    

    More concise and pythonic version from the comment of Olvin Roght (also removed the second branch for optimization for large summ values)

    def randparts(number, divider, min_value, max_value):
        sum_min = divider * min_value
        if sum_min > number:
            return
    
        number -= sum_min
        result = [min_value] * divider
        while number:
            pocket = randrange(divider)
            if result[pocket] <= max_value:
                result[pocket] += 1
                number -= 1
    
        return result