spassador
spassador

Reputation: 413

Distribute elements based on percentages

Let's say that I want to distribute a number of items (n) into an array of fixed size (x). The difficult part is that I have to distribute the items using a flexibility array.

Assuming that x = 4, n = 11 and flexibility = [20, 20, 30, 30] with len(flexibility) == x.

My question is: How can I distribute the n elements in an array of length equal to x using the percentage defined in f?

What I want at the end is something like:

n = 11
x = 4
flexibility = [20, 20, 30, 30]
distributed = distribute_elements_in_slots(n, x, flexibility)
print(distributed)
# distributed = [2, 2, 3, 4]

In the case of equal flexibility values, the final result will depend on the rule that we decide to apply to use all the item. In the previous case, the final result will be good with [2, 2, 3, 4] and with [2, 2, 4, 3].

Edit: An example of the method that I want to have is as follows:

def distribute_elements_in_slots(n, x, flexibility=[25,25,25,25]):
    element_in_slots = []

    element_per_percentage = x / 100

    for i in range(x):
        element_in_slots.append(round(slots_per_point_percentage * flexibility[i])

Edit 2: One of the solutions that I found is the following:

def distribute_elements_in_slots(n, x, flexibility=[25,25,25,25]):
    element_in_slots = [f * n / 100 for f in flexibility]

    carry = 0
    for i in range(len(element_in_slots)):
        element = element_in_slots[i] + carry
        element_in_slot[i] = floor(element)
        carry = element- floor(element)

    if np.sum(element_in_slots) < n:
        # Here the carry is almost 1
        max_index = element_in_slots.index(max(flexibiliyt))
        appointments_per_slot[max_index] = appointments_per_slot[max_index] + 1

This will distribute almost evenly the slots based on the flexibility array.

Upvotes: 1

Views: 1368

Answers (3)

skualos
skualos

Reputation: 1

This is an apportionment problem, which has different ways of being solved. See Mathematics of apportionment.

@prune answer implements the Largest Remainder or Hamilton Method.

There are other methods, and some simple examples provided by the US Census Bureau can be found here.

There are Python libraries available at pypi.org that implement different methods:

Upvotes: 0

Prune
Prune

Reputation: 77885

As Albin Paul did, we need to allocate the whole-number amount for each slot's percentage. The leftovers need to be allocated, largest first.

def distribute_elements_in_slots(total, slots, pct):
    # Compute proportional distribution by given percentages.
    distr = [total * pct[i] / 100 for i in range(slots)]
    # Truncate each position and store the difference in a new list.
    solid = [int(elem) for elem in distr]
    short = [distr[i] - solid[i] for i in range(slots)]
    print(distr)
    print(solid)
    print(short)

    # allocate leftovers
    leftover = int(round(sum(short)))
    print(leftover)
    # For each unallocated item,
    #   find the neediest slot, and put an extra there.
    for i in range(leftover):
        shortest = short.index(max(short))
        solid[shortest] += 1
        short[shortest] = 0
        print("Added 1 to slot", shortest)

    return solid


n = 11
x = 4
flexibility = [20, 20, 30, 30]
distributed = distribute_elements_in_slots(n, x, flexibility)
print(distributed)
# distributed = [2, 2, 3, 4]

Output:

[2.2, 2.2, 3.3, 3.3]
[2, 2, 3, 3]
[0.2, 0.2, 0.3, 0.3]
1
Added 1 to slot 2
[2, 2, 4, 3]

Upvotes: 2

Albin Paul
Albin Paul

Reputation: 3419

what you need to do is split the number 11 according to certain percents given in the array so initially it becomes percentage * number(11). Then we get remainder and put assign it somewhere which in your case is the last element.

In [10]: [i*n/100 for i in f]
Out[10]: [2.2, 2.2, 3.3, 3.3]

In [11]: b=[i*n/100 for i in f]

In [12]: rem = sum(b) - sum(map(int,b))


In [13]: rem
Out[13]: 1.0

In [24]: b= list(map(int,b))

In [26]: b[-1] +=rem

In [27]: b
Out[27]: [2, 2, 3, 4.0]

Hope it helps. :)

Upvotes: 1

Related Questions