10

I have a situation where I have invoice spreadsheets incoming with single rows that span multiple months with a quantity column containing the summation of the quantity for all months spanned.

In order to run month-by-month analytics, we need to split the total quantity into equal(ish) quantities across n rows where n is the number of months spanned.

These numbers can be off by one or two, but the smaller the difference between each element the better.

I have a rough mockup I did in python but I feel there's a better way to do this somehow. Note: Please excuse... everything:

from __future__ import division
import math
def evenDivide(num, div):
    splits = []
    sNum = str(num/div)
    remainder = float(sNum[sNum.index('.'):])
    #print "Remainder is " + str(remainder)
    integer = math.floor(num/div)
    #print "Integer is " + str(integer)
    totRemainder = round(remainder * div, 2)
    #print "Total Remainder is " + str(totRemainder)
    for index in range(div):
        if (totRemainder > 0):
            totRemainder -= 1 if (index%2 == 0) else 0
            if (index % 2 == 0):
                splits.append(int(integer + 1)) 
            else:
                splits.append(int(integer))
        else:
            splits.append(int(integer))
    for index in range(div):
        if(totRemainder > 0):
            if (index % 2 == 1):
                splits[index] += 1
                totRemainder -= 1

    return splits

def EvalSolution(splits):
    total = 0
    for index in range(len(splits)):
        total += splits[index]
    return total

def testEvenDivide():
    for index in range(20000):
        for jndex in range(3, 200):
            if (EvalSolution(evenDivide(index, jndex)) != index):
                print "Error for " + str(index) + ", " + str(jndex)
Sed
  • 133
  • 1
  • 1
  • 9

4 Answers4

16

If space is an issue, this one-liner may help:

num, div = 15, 4
print ([num // div + (1 if x < num % div else 0)  for x in range (div)])
# result: [4, 4, 4, 3]
Poe Dator
  • 4,535
  • 2
  • 14
  • 35
  • 3
    1) **[num // div for x in range (div)]** creates a list of div numbers all equal to num/div, i.e. [3,3,3,3]; 2) **(1 if x < num % div else 0)** adds 1 only to the first num%div elements i.e. [1,1,1,0] the combination gives the answer [4,4,4,3] – Poe Dator Nov 03 '18 at 12:48
6

I assume both num and div are integers (you should mention it in your question).

You can use the modulo operator to find the remainder of the division:

remainder=num%div   # i.e. 124/12 will give you 4

Integer division will give you the integer part of the result without using math.floor

integer = num/div    # i.e. 124/12 will give you 10

I would return now the (integer,remainder) tuple, but if you really need all the splits in a list, you can do:

splits=[]
for i in range(div):
   splits.append(integer)
for i in range(remainder):
   splits[i]+=1
leeladam
  • 1,748
  • 10
  • 15
  • 1
    BTW, Python has a built-in named [divmod()](http://docs.python.org/2/library/functions.html?highlight=divmod#divmod) which would be good for doing the first part. – martineau Dec 03 '13 at 11:51
2

This one liner can help:

def get_evenly_divided_values(value_to_be_distributed, times):
    return [value_to_be_distributed // times + int(x < value_to_be_distributed % times) for x in range(times)]
1

Here's a variation on Poe Dator's answer that avoids doing a modulus for every returned number:

def evenDivide(num, div):
    groupSize, remainder = divmod(num, div)
    return [groupSize + (1 if x < remainder else 0) for x in range(div)]
oobug
  • 941
  • 1
  • 8
  • 13