如何生成统一的随机整数分区?

时间:2010-01-29 10:57:17

标签: algorithm

Google搜索显示了很多关于将整数n的所有可能分区生成为m个部分的内容,但我还没有找到任何关于将n均匀分布的随机分区采样为m个部分的内容。

6 个答案:

答案 0 :(得分:13)

这篇文章的标题有点误导。随机整数分区默认为 unrestricted ,这意味着它可以包含任意大小的任意数量的部分。提出的具体问题是将n分区为m个部分,这是一种受限制的整数分区。

为了生成不受限制的整数分区,在一篇名为大整数随机分区的结构(1993)的论文中,一种非常快速和简单的算法归功于Fristedt。算法如下:

  1. 设置x = exp(-pi / sqrt(6n))。
  2. 生成独立随机变量Z(1),Z(2),...,Z(n),其中Z(i)几何分布为参数1-x ^ i。
  3. IF sum i * Z(i)= n,其中总和取代所有i = 1,2,...,n,然后停止。
    ELSE,重复2次。
  4. 一旦算法停止,则Z(1)是的1s ,Z(2)是的2s 等数,在选择的分区中均匀随意。接受随机选择的Z集合的概率渐近1 /(94n ^ 3)^(1/4),这意味着在接受单个算法之前,人们期望运行该算法O(n ^(3/4))次。样品

    我花时间解释这个算法的原因是因为它将直接应用于将n的分区生成为m个部分的问题。首先,观察

    n到m个部分的分区数等于n的分区数,最大部分等于m。

    然后我们可以直接应用Fristedt算法,但不是生成Z(1),Z(2),...,Z(n),我们可以生成Z(1),Z(2),... ,Z(m-1),Z(m)+1(这里的+1确保最大部分正好是m,并且1 + Z(m)在Z(m)条件下的分布等于Z(m) > = 1)并设置所有其他Z(m + 1),Z(m + 2),...等于0.然后,一旦我们在步骤3中获得目标总和,我们也保证具有无偏的样本。要获得n到m个部分的分区,只需获取生成的分区的共轭。

    这对Nijenhuis和Wilf的递归方法的优势在于除了存储随机变量Z(1),Z(2)等之外没有内存要求。此外,x的值可以是任何值在0和1之间,这个算法仍然没有偏见!然而,选择一个好的x值可以使算法更快,尽管步骤1中的选择对于不受限制的整数分区几乎是最佳的。

    如果n非常大并且Fristedt的算法花费的时间太长(并且表格方法是不可能的),那么还有其他选择,但它们有点复杂;有关概率分而治之及其应用的更多信息,请参阅我的论文https://sites.google.com/site/stephendesalvo/home/papers

答案 1 :(得分:10)

这是一些代码。这是第一次调用时的O( n 2 ),但它会构建一个缓存,以便后续调用为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

这是如何工作的:我们可以计算出O( n 2 2 时间。作为副作用,这将生成一个大小为O( n 2 )的表,然后我们可以使用它来生成 k 分区< em> n ,对于任何整数 k ,在O( n )时间内。

所以让总计 =分区数。从0到总计选择随机数 k - 1.生成 k 分区。

答案 2 :(得分:1)

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();
        }
    }
}

此代码将生成下一个输出:

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,

答案 3 :(得分:1)

我有一个均匀分布的分区生成器。

其中n:=要分区的整数,r:=切片数: 该算法是简单地随机插入分类的简单方法的修补版本。当我看到它的输出时,这种方法的问题在于,分配放置在同一位置的情况不太可能发生。获得{1,1,1}的方法只有一种,而有3种!获得{2,4,9},{4,2,9},{2,4,9},{9,4,2} ......中的任何一个的方式将在排序时导致相同的分区放置。通过为重复提供额外的明确机会,对此进行了修订。对于每次分型插入,分离的位置可能不是随机的,而是将被选择为先前选择的值的重复。这平衡了天真方法的不均匀概率分布。

我已经筋疲力尽地证明了每个分区对于r = 3,n = 2完全相同。我证明了它的价值更高,但是这样做的经验丰富的企业只发现了有希望的迹象。我还在随机输入上对它进行了测试,发现它至少对我尝试的每个值都是大致均匀[但可能完全均匀]。

这是在C ++ 11中:[输出格式与您期望的不同,它是分区的位置而不是它们之间的空间大小。转换很容易,但是

#include <vector>
#include <algorithm>
#include <random>
#include <cassert>
template <typename Parting, typename Seed>
vector<Parting> partitionGen(unsigned nparts, unsigned bandw, Seed seed){//nparts is the number of parts, that is, one greater than the number of dividers listed in the output vector. Bandw is the integer being partitioned.
    assert(nparts > 0);
    vector<Parting> out(nparts-1);
    srand(seed);
    unsigned genRange = bandw;
    for(auto i=out.begin(); i<out.end(); ++i, ++genRange){
        unsigned gen = rand()%genRange;
        *i = ((gen<bandw)?
            gen:
            *(i-(gen-bandw+1)));
    }
    sort(out.begin(), out.end(), less<Parting>());
    return out;
}

我不喜欢我必须对它进行排序的事实。如果Vlody的版本具有均匀分布,那么它似乎会更好。

答案 4 :(得分:0)

经过一些谷歌搜索后,我在“应用算法手册”which Google Books has indexed中找到了一个算法。该算法在第31页的1.12.2节中给出。

答案 5 :(得分:0)

我已经实现了上述解决方案,并发现如果想要计算n的整数分区而不是m的整数分区,它的效果非常好。如果使用大n,递归限制和调用堆栈可能需要增加很多。

但是,您不需要第一个函数,因为count_partitions(n,limit)实际上等于'n + limit'的分区数和'limit'部分数。一些数学软件具有非常快的功能,用于查找n到m个部分的分区数。

我最近推出了一种绝对无偏见,非常简单且非常快速的方法(使用记忆)来解决您的确切问题An algorithm for randomly generating integer partitions of a particular length, in Python?

它基于对具有m个部分的n的词汇有序分区的了解并且使用类似于广泛接受的算法(例如Nijenhuis和Wilf 1978)的方法来找到n的随机分区,并且在概念上类似于上述。

简而言之,如果有n个m分区的m个分区,那么我们选择1和x之间的随机数。该随机数将编码满足n和m的一个且仅一个分区。我希望这会有所帮助。