Google搜索显示了很多关于将整数n的所有可能分区生成为m个部分的内容,但我还没有找到任何关于将n均匀分布的随机分区采样为m个部分的内容。
答案 0 :(得分:13)
这篇文章的标题有点误导。随机整数分区默认为 unrestricted ,这意味着它可以包含任意大小的任意数量的部分。提出的具体问题是将n分区为m个部分,这是一种受限制的整数分区。
为了生成不受限制的整数分区,在一篇名为大整数随机分区的结构(1993)的论文中,一种非常快速和简单的算法归功于Fristedt。算法如下:
一旦算法停止,则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的一个且仅一个分区。我希望这会有所帮助。