Java:具有非均匀分布的随机整数

时间:2011-05-11 19:20:49

标签: java random non-uniform-distribution

如何在Java中创建随机整数n,在1k之间使用“线性降序分布”,即1最有可能,{{ 1}}不太可能,2不太可能,......,3最不可能,并且概率线性下降,如下所示:

enter image description here

我知道已经有关于这个主题的线程,我为制作新主题道歉,但我似乎无法从他们那里创造我需要的东西。我知道使用k代码

import java.util.*;

Random r=new Random(); int n=r.nextInt(k)+1; 1之间创建一个随机整数,均匀分布。

GENERALIZATION:任何有关创建任意分布整数的提示,即kf(n)=some function),也会受到赞赏,例如: enter image description here

11 个答案:

答案 0 :(得分:18)

这应该可以满足您的需求:

public static int getLinnearRandomNumber(int maxSize){
    //Get a linearly multiplied random number
    int randomMultiplier = maxSize * (maxSize + 1) / 2;
    Random r=new Random();
    int randomInt = r.nextInt(randomMultiplier);

    //Linearly iterate through the possible values to find the correct one
    int linearRandomNumber = 0;
    for(int i=maxSize; randomInt >= 0; i--){
        randomInt -= i;
        linearRandomNumber++;
    }

    return linearRandomNumber;
}

此外,这是从起始索引到stopIndex的范围内POSITIVE函数(负函数没有意义)的一般解决方案:

public static int getYourPositiveFunctionRandomNumber(int startIndex, int stopIndex) {
    //Generate a random number whose value ranges from 0.0 to the sum of the values of yourFunction for all the possible integer return values from startIndex to stopIndex.
    double randomMultiplier = 0;
    for (int i = startIndex; i <= stopIndex; i++) {
        randomMultiplier += yourFunction(i);//yourFunction(startIndex) + yourFunction(startIndex + 1) + .. yourFunction(stopIndex -1) + yourFunction(stopIndex)
    }
    Random r = new Random();
    double randomDouble = r.nextDouble() * randomMultiplier;

    //For each possible integer return value, subtract yourFunction value for that possible return value till you get below 0.  Once you get below 0, return the current value.  
    int yourFunctionRandomNumber = startIndex;
    randomDouble = randomDouble - yourFunction(yourFunctionRandomNumber);
    while (randomDouble >= 0) {
        yourFunctionRandomNumber++;
        randomDouble = randomDouble - yourFunction(yourFunctionRandomNumber);
    }

    return yourFunctionRandomNumber;
}

注意:对于可能返回负值的函数,一种方法可以是取该函数的绝对值,并将其应用于每个yourFunction调用的上述解决方案。

答案 1 :(得分:7)

所以我们需要以下分布,最不可能的是:

*
**
***
****
*****

让我们尝试将均匀分布的整数随机变量映射到该分布:

1
2  3
4  5  6
7  8  9  10
11 12 13 14 15

这样,如果我们生成一个均匀分布的随机整数,从1到15,在这种情况下为K = 5,我们只需要找出它适合它的桶。棘手的部分是如何做到这一点。

请注意,右侧的数字是三角形数字!这意味着,对于从X1的随机生成的T_n,我们只需找到N T_(n-1) < X <= T_n。幸运的是有一个well-defined formula to find the 'triangular root' of a given number,我们可以将它作为从均匀分布到桶的映射的核心:

// Assume k is given, via parameter or otherwise
int k;

// Assume also that r has already been initialized as a valid Random instance
Random r = new Random();

// First, generate a number from 1 to T_k
int triangularK = k * (k + 1) / 2;

int x = r.nextInt(triangularK) + 1;

// Next, figure out which bucket x fits into, bounded by
// triangular numbers by taking the triangular root    
// We're dealing strictly with positive integers, so we can
// safely ignore the - part of the +/- in the triangular root equation
double triangularRoot = (Math.sqrt(8 * x + 1) - 1) / 2;

int bucket = (int) Math.ceil(triangularRoot);

// Buckets start at 1 as the least likely; we want k to be the least likely
int n = k - bucket + 1;

n现在应具有指定的分布。

答案 2 :(得分:6)

有很多方法可以做到这一点,但最简单的方法就是生成 两个随机整数,一个在0k之间,称之为x,一个在0h之间,称之为{ {1}}。如果yy > mx + bm选择正确...),则b,否则 k-x

修改:回复此处的评论,以便我可以腾出更多空间。

基本上我的解决方案利用原始分布中的对称性,其中xp(x)的线性函数。我在编辑关于泛化之前做出了回应,并且这个解决方案在一般情况下不起作用(因为在一般情况下没有这样的对称性)。

我想象了这样的问题:

  1. 你有两个直角三角形,每个x,有一个共同的斜边。复合形状是k x h矩形。
  2. 以相同的概率生成落在矩形内每个点上的随机点。
  3. 一半时间落在一个三角形中,一半落在另一个三角形中。
  4. 假设该点落在下三角形中。
    • 三角形基本上描述了P.M.F.,并且每个x值上的三角形的“高度”描述了该点具有这样的x值的概率。 (请记住,我们只处理下三角形中的点。)因此,通过产生x值。
  5. 假设该点落在上三角形中。
    • 反转坐标并按上面的三角形处理它。
  6. 你也必须照顾边缘情况(我没有打扰)。例如。我现在看到你的发行从1开始,而不是0,所以那里有一个一个一个,但它很容易修复。

答案 3 :(得分:5)

让我尝试另一个答案,受到rlibby的启发。这个特殊的分布也是从相同范围内均匀和随机选择的两个值的较小的分布。

答案 4 :(得分:4)

如果您的分布是可以计算其累积分布函数(cdf),则无需使用数组等模拟此类型。上面你有一个概率分布函数(pdf)。 h实际上是确定的,因为曲线下面积必须为1.为了简化数学运算,我还假设你在[0,k)中选择一个数字。

这里的pdf是f(x)=(2 / k)*(1 - x / k),如果我读了你的话。 cdf只是pdf的组成部分。这里,那是F(x)=(2 / k)*(x - x ^ 2 / 2k)。 (如果任何pdf函数是可积的,你可以重复这个逻辑。)

然后你需要计算cdf函数的倒数,F ^ -1(x),如果我不懒,我会为你做。

但好消息是:一旦你有了F ^ -1(x),你所做的就是在[0,1]中统一地将它应用于随机值分布并将函数应用于它。 java.util.Random可以提供一些小心。这是您从发行版中随机抽样的值。

答案 5 :(得分:3)

This is called a triangular distribution,虽然你的是一个退化的情况,模式等于最小值。维基百科有关于如何在给定均匀分布的(0,1)变量的情况下创建一个等式的方程式。

答案 6 :(得分:2)

首先想到的解决方案是使用阻塞数组。每个索引都会指定一系列值,具体取决于您希望它的“可能”程度。在这种情况下,您可以使用更宽的范围1,宽度更宽2,依此类推,直到达到k的小值(假设为1)。

int [] indexBound = new int[k];
int prevBound =0;
for(int i=0;i<k;i++){
    indexBound[i] = prevBound+prob(i);
    prevBound=indexBound[i];
}
int r = new Random().nextInt(prevBound);
for(int i=0;i<k;i++){
    if(r > indexBound[i];
        return i;
}

现在问题只是找到一个随机数,然后将该数字映射到其存储桶。 您可以为任何分布执行此操作,前提是您可以离散每个间隔的宽度。 如果我在解释算法或其正确性时遗漏了某些内容,请告诉我。毋庸置疑,这需要进行优化。

答案 7 :(得分:2)

像这样......

class DiscreteDistribution
{
    // cumulative distribution
    final private double[] cdf;
    final private int k;

    public DiscreteDistribution(Function<Integer, Double> pdf, int k)
    {
        this.k = k;
        this.cdf = new double[k];
        double S = 0;
        for (int i = 0; i < k; ++i)
        {
            double p = pdf.apply(i+1);         
            S += p;
            this.cdf[i] = S;
        }
        for (int i = 0; i < k; ++i)
        {
            this.cdf[i] /= S;
        }
    }
    /**
     * transform a cumulative distribution between 0 (inclusive) and 1 (exclusive)
     * to an integer between 1 and k.
     */
    public int transform(double q)
    {
        // exercise for the reader:
        // binary search on cdf for the lowest index i where q < cdf[i]
        // return this number + 1 (to get into a 1-based index.
        // If q >= 1, return k.
    }
}

答案 8 :(得分:2)

对于模式(最高加权概率)为1的三角分布x^2,累积分布函数为[0,1],如here所示。

因此,我们需要做的就是将均匀分布(例如Java的Random::nextDouble)转换为方便的三角分布,加权为1:简单地取平方根Math.sqrt(rand.nextDouble()),然后可以乘以任何期望的范围。

对于你的例子:

int a = 1; // lower bound, inclusive
int b = k; // upper bound, exclusive
double weightedRand = Math.sqrt(rand.nextDouble()); // use triangular distribution
weightedRand = 1.0 - weightedRand; // invert the distribution (greater density at bottom)
int result = (int) Math.floor((b-a) * weightedRand);
result += a; // offset by lower bound
if(result >= b) result = a; // handle the edge case 

答案 9 :(得分:1)

最简单的方法是生成权重中所有可能值的列表或数组。

int k = /* possible values */
int[] results = new int[k*(k+1)/2];
for(int i=1,r=0;i<=k;i++)
   for(int j=0;j<=k-i;j++)
       results[r++] = i;
// k=4 => { 1,1,1,1,2,2,2,3,3,4 }

// to get a value with a given distribution.
int n = results[random.nextInt(results.length)];

这最适用于相对较小的k值。 k&lt; 1000.;)

对于较大的数字,您可以使用存储桶方法

int k = 
int[] buckets = new int[k+1];
for(int i=1;i<k;i++)
   buckets[i] = buckets[i-1] + k - i + 1;

int r = random.nextInt(buckets[buckets.length-1]);
int n = Arrays.binarySearch(buckets, r);
n = n < 0 ? -n : n + 1;

二进制搜索的成本相当小,但不如直接查找(对于小数组)有效


对于仲裁分配,您可以使用double[]进行累积分布,并使用二元搜索来查找值。

答案 10 :(得分:0)

有很多方法可以生成具有自定义分布(也称为离散分布)的随机整数。选择取决于许多因素,包括要选择的整数数量,分布的形状以及分布是否会随时间变化。

使用自定义权重函数f(x)选择整数的最简单方法之一是拒绝采样方法。以下假设f的最大可能值为max。平均而言,拒绝采样的时间复杂度是恒定的,但在很大程度上取决于分布的形状,并且永远无法运行。要使用拒绝采样在[1,k]中选择一个整数:

  1. 在[1,i]中选择一个统一的随机整数k
  2. 以概率f(i)/max,返回i。否则,请转到步骤1。

其他算法的平均采样时间并不非常依赖于分布(通常是常数或对数),但是通常需要您在设置步骤中预先计算权重并将其存储在数据结构中。就它们平均使用的随机比特数而言,其中一些也是经济的。这些算法包括别名方法Fast Loaded Dice Roller,Knuth-Yao算法,MVN数据结构等。请参阅我的“ A Note on Weighted Choice Algorithms”部分进行调查。