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

Algorithm 如何生成统一的随机整数分区?,algorithm,random,Algorithm,Random,谷歌搜索揭示了大量关于将整数n的所有可能分区生成m个部分的信息,但我没有发现任何关于将n的均匀分布随机分区采样为m个部分的信息。在谷歌搜索之后,我在《应用算法手册》中找到了一种算法。第31页第1.12.2节给出了该算法。以下是一些实现该算法的代码。这是您第一次调用它时的O(n2),但它会构建一个缓存,以便后续调用为O(n) 随机导入 缓存={} def计数分区(n,限制): 如果n==0: 返回1 如果缓存中有(n,限制): 返回缓存[n,限制] x=缓存[n,限制]=总和(在范围(1,min(

谷歌搜索揭示了大量关于将整数n的所有可能分区生成m个部分的信息,但我没有发现任何关于将n的均匀分布随机分区采样为m个部分的信息。

在谷歌搜索之后,我在《应用算法手册》中找到了一种算法。第31页第1.12.2节给出了该算法。

以下是一些实现该算法的代码。这是您第一次调用它时的O(n2),但它会构建一个缓存,以便后续调用为O(n)

随机导入
缓存={}
def计数分区(n,限制):
如果n==0:
返回1
如果缓存中有(n,限制):
返回缓存[n,限制]
x=缓存[n,限制]=总和(在范围(1,min(限制,n)+1)内为k计算分区(n-k,k))
返回x
def随机分区(n):
a=[]
极限=n
总计=分区数(n,限制)
其中=random.randrange(总计)
而n:
对于范围(1,min(极限,n)+1)内的k:
count=count\u分区(n-k,k)
如果哪个<计数:
打破
哪个-=计数
a、 附加(k)
极限=k
n-=k
归还
工作原理:我们可以计算一个整数n在O(n2)时间内有多少个分区。作为一个副作用,这将生成一个大小为O(n2)的表,然后我们可以使用该表在O(n)时间内为任意整数k生成n的第k个分区

所以让total=分区的数量。从0到总数-1中选择一个随机数k。生成第k个分区。

在c#中再生成一个版本


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

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

我最近得出了一个绝对公正、非常简单、非常快速的方法(使用记忆法)来解决您的确切问题

它基于对n的词汇有序分区(有m个部分)的了解,并使用了与公认的算法(如Nijenhuis和Wilf 1978)类似的方法来发现n的随机分区,概念上与上述类似


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

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

其中n:=要分区的整数,r:=切片数: 该算法是简单地随机插入分区的简单方法的修补版本。这种方法的问题,正如我在查看其输出时所看到的那样,是在同一地点进行分离的情况不太可能发生。只有一种方法可以得到{1,1,1},而只有3种!获得{2,4,9},{4,2,9},{2,4,9},{9,4,2}中任何一个的方法。。。排序时将导致相同的分区位置。这一点已通过提供额外的明确重复机会进行了修改。对于每个分型插入,分型的位置可能不是随机的,而是作为先前选定值的重复选择的。这就平衡了朴素方法的不均匀概率分布

我已经用穷举证明了,对于r=3,n=2,每个分区的可能性完全相等。我认为cbf证明了它的更高价值,但有爱心的企业这样做只发现了有希望的迹象。我还对随机输入进行了测试,发现我尝试的每一个值至少大致相等(但可能完全相等)

这里是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;
}
#包括
#包括
#包括
#包括
模板
vector partitionGen(unsigned nPart,unsigned bandw,Seed Seed){//nPart是部分数,即比输出向量中列出的除法器数大一个。bandw是要分区的整数。
断言(npart>0);
矢量输出(nparts-1);
srand(种子);
无符号genRange=bandw;

对于(auto i=out.begin();i这篇文章的标题有点误导。随机整数分区在默认情况下是不受限制的,这意味着它可以有任意大小的任意多个部分。所问的具体问题是关于将n个部分划分为m个部分,这是一种受限制的整数分区

对于生成无限制整数分区,Fristedt提出了一种非常快速和简单的算法,该算法在一篇名为《大整数随机分区的结构》(1993)的论文中给出。该算法如下:

  • 设置x=exp(-pi/sqrt(6n))
  • 生成独立的随机变量Z(1),Z(2),…,Z(n),其中Z(i)与参数1-x^i呈几何分布
  • 如果总和i*Z(i)=n,其中总和超过所有i=1,2,…,n,则停止。
    否则,重复2
  • 一旦算法停止,那么Z(1)是在一个均匀随机选择的分区中1的数量,Z(2)是2的数量,等等。接受随机选择的一组Z的概率是渐进的1/(94n^3)^(1/4),这意味着在接受单个样本之前,人们希望将此算法运行O(n^(3/4))次

    我之所以花时间解释这个算法,是因为它直接适用于将n划分为m个部分的问题

    n的分区数正好等于n的分区数,其中最大部分等于m

    然后w
    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,
    
    #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;
    }