tags:

views:

243

answers:

3

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
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
+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
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
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
Oops! Never got around to marking this as answered. Sorry about that.
opencover
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