25

I'm trying to find or develop Integer Partitioning code for Python.

FYI, Integer Partitioning is representing a given integer n as a sum of integers smaller than n. For example, an integer 5 can be expressed as 4 +たす 1 = 3 +たす 2 = 3 +たす 1 +たす 1 = 2 +たす 2 +たす 1 = 2 +たす 1 +たす 1 +たす 1 = 1 +たす 1 +たす 1 +たす 1 +たす 1

I've found a number of solutions for this. http://homepages.ed.ac.uk/jkellehe/partitions.php and http://code.activestate.com/recipes/218332-generator-for-integer-partitions/

However, what I really want is to restrict the number of partitions.

Say, # of partition k = 2, a program only need to show 5 = 4 +たす 1 = 3 +たす 2,

if k = 3, 5 = 3 +たす 1 +たす 1 = 2 +たす 2 +たす 1

Bergi
669k161 gold badges1k silver badges1.5k bronze badges
asked Aug 29, 2013 at 5:41
5
  • You only want a certain number of partitions? Commented Aug 29, 2013 at 5:44
  • Yes, that's right. Say partitionfunc(n, k) would give list of partitions of integer n whose length is k Commented Aug 29, 2013 at 5:47
  • Wait, do you want fixed-length partitions, or do you want to only generate a certain number of partitions? Commented Aug 29, 2013 at 5:48
  • length of partitions k will be given by user's input, as well as n Commented Aug 29, 2013 at 5:49
  • @DavidEisenstat This is a distinct problem from the linked question, which is about doubly restricted integer partitioning (though the title of the linked question is misleading.) Commented Oct 14, 2018 at 18:50

4 Answers 4

26

I've written a generator solution

def partitionfunc(n,k,l=1):
 '''n is the integer to partition, k is the length of partitions, l is the min partition element size'''
 if k < 1:
 raise StopIteration
 if k == 1:
 if n >= l:
 yield (n,)
 raise StopIteration
 for i in range(l,n+1):
 for result in partitionfunc(n-i,k-1,i):
 yield (i,)+result

This generates all the partitions of n with length k with each one being in order of least to greatest.

Just a quick note: Via cProfile, it appears that using the generator method is much faster than using falsetru's direct method, using the test function lambda x,y: list(partitionfunc(x,y)). On a test run of n=50,k-5, my code ran in .019 seconds vs the 2.612 seconds of the direct method.

answered Aug 29, 2013 at 6:05
5
  • 6
    You can use return instead of raise StopIteration. Commented Aug 29, 2013 at 6:35
  • I updated my list concatenation version. Still slower than your code, but improved. :) Commented Aug 29, 2013 at 6:40
  • You can change for i in range(l, n+1) to for i in range(l, n//k+1) since you can't have k parts bigger than n//k. Commented Sep 12, 2016 at 21:47
  • I found a non-recursive solution: stackoverflow.com/questions/60639740/… Commented Mar 12, 2020 at 12:28
  • raise StopIteration is deprecated now as shown here ; raise StopIteration should be replace with return statement. Commented Dec 5, 2020 at 3:36
8
def part(n, k):
 def _part(n, k, pre):
 if n <= 0:
 return []
 if k == 1:
 if n <= pre:
 return [[n]]
 return []
 ret = []
 for i in range(min(pre, n), 0, -1):
 ret += [[i] + sub for sub in _part(n-i, k-1, i)]
 return ret
 return _part(n, k, n)

Example:

>>> part(5, 1)
[[5]]
>>> part(5, 2)
[[4, 1], [3, 2]]
>>> part(5, 3)
[[3, 1, 1], [2, 2, 1]]
>>> part(5, 4)
[[2, 1, 1, 1]]
>>> part(5, 5)
[[1, 1, 1, 1, 1]]
>>> part(6, 3)
[[4, 1, 1], [3, 2, 1], [2, 2, 2]]

UPDATE

Using memoization:

def part(n, k):
 def memoize(f):
 cache = [[[None] * n for j in xrange(k)] for i in xrange(n)]
 def wrapper(n, k, pre):
 if cache[n-1][k-1][pre-1] is None:
 cache[n-1][k-1][pre-1] = f(n, k, pre)
 return cache[n-1][k-1][pre-1]
 return wrapper
 @memoize
 def _part(n, k, pre):
 if n <= 0:
 return []
 if k == 1:
 if n <= pre:
 return [(n,)]
 return []
 ret = []
 for i in xrange(min(pre, n), 0, -1):
 ret += [(i,) + sub for sub in _part(n-i, k-1, i)]
 return ret
 return _part(n, k, n)
answered Aug 29, 2013 at 6:03
5

First I want to thanks everyone for their contribution. I arrived here needing an algorithm for generating integer partitions with the following details :

Generate partitions of a number into EXACTLY k parts but also having MINIMUM and MAXIMUM constraints.

Therefore, I modified the code of "Snakes and Coffee" to accommodate these new requirements:

def partition_min_max(n, k, l, m):
 ''' n is the integer to partition, k is the length of partitions, 
 l is the min partition element size, m is the max partition element size '''
 if k < 1:
 raise StopIteration
 if k == 1:
 if n <= m and n>=l :
 yield (n,)
 raise StopIteration
 for i in range(l,m+1):
 for result in partition_min_max(n-i, k-1, i, m):
 yield result+(i,)
>>> x = list(partition_min_max(20 ,3, 3, 10 ))
>>> print(x)
>>> [(10, 7, 3), (9, 8, 3), (10, 6, 4), (9, 7, 4), (8, 8, 4), (10, 5, 5), (9, 6, 5), (8, 7, 5), (8, 6, 6), (7, 7, 6)]
answered Mar 25, 2017 at 10:40
0

Building upon previous answer with maximum and minimum constraints, we can optimize it be a little better . For eg with k = 16 , n = 2048 and m = 128 , there is only one such partition which satisfy the constraints(128+128+...+128). But the code searches unnecessary branches for an answer which can be pruned.

def partition_min_max(n,k,l,m):
#n is the integer to partition, k is the length of partitions, 
#l is the min partition element size, m is the max partition element size
 if k < 1:
 return
 if k == 1:
 if n <= m and n>=l :
 yield (n,)
 return
 if (k*128) < n: #If the current sum is too small to reach n
 return
 if k*1 > n:#If current sum is too big to reach n
 return
 for i in range(l,m+1):
 for result in partition_min_max(n-i,k-1,i,m): 
 yield result+(i,)
answered Feb 26, 2021 at 16:17

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.