A Google search reveals plenty about generating all possible partitions of an integer n into m parts, but I haven't found anything about sampling a uniformly distributed random partition of n into m parts.
A:
After some googling I found an algorithm for this in the "Handbook of Applied Algorithms," which Google Books has indexed. The algorithm is given in section 1.12.2, on page 31.
PeterAllenWebb
2010-01-29 17:19:17
Yeah, I came across that, too. It doesn't generate partitions with exactly m parts, and it assumes that RP(n,m) (equivalent to Jason's count_partitions(n, limit), with limit = m) has already been computed. I'm thinking less work needs to be done to compute the number of partitions of n into m parts.
opencover
2010-01-30 16:45:18
+1
A:
Here is some code that does it. This is O(n2) the first time you call it, but it builds a cache so that subsequent calls are O(n).
import random
cache = {}
def count_partitions(n, limit):
if n == 0:
return 1
if (n, limit) in cache:
return cache[n, limit]
x = cache[n, limit] = sum(count_partitions(n-k, k) for k in range(1, min(limit, n) + 1))
return x
def random_partition(n):
a = []
limit = n
total = count_partitions(n, limit)
which = random.randrange(total)
while n:
for k in range(1, min(limit, n) + 1):
count = count_partitions(n-k, k)
if which < count:
break
which -= count
a.append(k)
limit = k
n -= k
return a
How this works: We can calculate how many partitions of an integer n there are in O(n2) time. As a side effect, this produces a table of size O(n2) which we can then use to generate the kth partition of n, for any integer k, in O(n) time.
So let total = the number of partitions. Pick a random number k from 0 to total - 1. Generate the kth partition.
Jason Orendorff
2010-01-29 17:24:50
So count_partitions(n, limit) counts the number of partitions of n into parts less than or equal to limit. Okay, I see how you're counting that. And then, given a bijection between the integers 1,...,count_partitions(n,n) and the partitions of n, random_partition chooses one of those integers and constructs the corresponding partition. Of course, this solution doesn't quite address the question, which asked for a random partition of n *into exactly m parts*. I suppose I should have made that clear in the title. However, I can definitely come up with what I need based on this.
opencover
2010-01-30 16:17:45
Oh, I'm sorry. I misread the question! Anyway, all I did was take some code I already had for generating all partitions, make two copies, and turn one copy into the counting function and the other copy into the kth-partition-constructing function. The exact same approach should work for your problem.
Jason Orendorff
2010-01-30 22:36:03
A:
Just one more version in c#.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication6
{
class Program
{
static Random random = new Random();
static void Main(string[] args)
{
PrintPartition(GetUniformPartition(24, 5));
PrintPartition(GetUniformPartition(24, 5));
PrintPartition(GetUniformPartition(24, 5));
PrintPartition(GetUniformPartition(24, 5));
PrintPartition(GetUniformPartition(24, 5));
Console.ReadKey();
}
static int[] GetUniformPartition(int input, int parts)
{
if(input<= 0 || parts <= 0)
throw new ArgumentException("invalid input or parts");
if (input < MinUniformPartition(parts))
throw new ArgumentException("input is to small");
int[] partition = new int[parts];
int sum = 0;
for (int i = 0; i < parts-1; i++)
{
int max = input - MinUniformPartition(parts - i - 1) - sum;
partition[i] = random.Next(parts - i, max);
sum += partition[i];
}
partition[parts - 1] = input - sum; // last
return partition;
}
// sum of 1,2,3,4,..,n
static int MinUniformPartition(int n)
{
return n * n - 1;
}
static void PrintPartition(int[] p)
{
for (int i = 0; i < p.Length; i++)
{
Console.Write("{0},", p[i]);
}
Console.WriteLine();
}
}
}
This code will produce next output:
5,8,7,2,2,
6,6,7,2,3,
5,7,6,2,4,
6,4,3,2,9,
7,8,4,4,1,
volody
2010-06-26 03:45:55