以增量生成随机数

时间:2011-07-26 19:16:12

标签: java c++ c random

我需要在na之间生成b个随机数,但任何两个数字的差异都不能小于c。除n之外的所有变量都是浮点数(n是一个int)。

在java中首选解决方案,但C / C ++也可以。

这是我到目前为止的代码。:

static float getRandomNumberInRange(float min, float max) {
    return (float) (min + (Math.random() * (max - min)));
}

static float[] randomNums(float a, float b, float c, int n) {
    float minDistance = c;
    float maxDistance = (b - a) - (n - 1) * c;
    float[] randomNumArray = new float[n];
    float random = getRandomNumberInRange(minDistance, maxDistance);
    randomNumArray[0] = a + random;
    for (int x = 1; x < n; x++) {
        maxDistance = (b - a) - (randomNumArray[x - 1]) - (n - x - 1) * c;
        random = getRandomNumberInRange(minDistance, maxDistance);
        randomNumArray[x] = randomNumArray[x - 1] + random;
    }
    return randomNumArray;
}

如果我这样运行这个功能(10次),我得到以下输出:

输入:randomNums(-1f, 1f, 0.1f, 10)

[-0.88, 0.85, 1.23, 1.3784, 1.49, 1.59, 1.69, 1.79, 1.89, 1.99]

[-0.73, -0.40, 0.17, 0.98, 1.47, 1.58, 1.69, 1.79, 1.89, 1.99]

[-0.49, 0.29, 0.54, 0.77, 1.09, 1.56, 1.69, 1.79, 1.89, 1.99]

9 个答案:

答案 0 :(得分:8)

我认为合理的方法如下:

schema

  1. 总“空格”为(b - a)
  2. 删除所需的最小空间(n-1)*c以获取剩余空间
  3. 在0和1之间拍摄(n-1)个随机数并对其进行缩放,以便总和就是这个“可选空间”。它们中的每一个都将是一个“切片”的空间。
  4. 第一个数字是a
  5. 对于每个其他号码,将c和下一个“切片”添加到上一个号码。最后一个号码为b
  6. 如果您不希望第一个和最后一个完全匹配ab,那么只需创建n+1个切片而不是n-1,然后从a+slice[0]开始而不是a

    主要思想是,一旦删除了点之间所需的间距(总计(n-1)*c),问题就在于找到n-1值,以便总和是规定的“可选空间”。要使用均匀分布执行此操作,只需拍摄n-1个数字,计算总和并均匀地缩放这些数字,以便通过将每个数乘以常数因子k = wanted_sum / current_sum来得到总和。

    要获得最终结果,您只需将值与前一个结果用作必需部分c和其中一个随机采样变量部分的总和。

    Python中用于计算所需代码的示例如下

    space = b - a
    slack = space - (n - 1)*c
    slice = [random.random() for i in xrange(n-1)]  # Pick (n-1) random numbers 0..1
    k = slack / sum(slice)                          # Compute needed scaling
    slice = [x*k for x in slice]                    # Scale to get slice sizes
    result = [a]
    for i in xrange(n-1):
        result.append(result[-1] + slice[i] + c)
    

答案 1 :(得分:2)

如果您有随机数X并且您想要另一个随机数Y,其中X的最小值为A,最大值为X,那么为什么不在代码中写出?

float nextRandom(float base, float minDist, float maxDist) {
  return base + minDist + (((float)Math.random()) * (maxDist - minDist));
}

通过尝试将基数保留在下一个数字例程之外,会给算法增加很多复杂性。

答案 2 :(得分:1)

虽然这并不能完全按照您的需要进行操作,并且没有包含此线程中描述的技术,但我相信这段代码将证明是有用的,因为它会像您想要的那样。

static float getRandomNumberInRange(float min, float max)
{
    return (float) (min + (Math.random() * ((max - min))));
}
 static float[] randomNums(float a, float b, float c, int n) 
{
    float averageDifference=(b-a)/n; 
    float[] randomNumArray = new float[n];
    int random;
    randomNumArray[0]=a+averageDifference/2;
    for (int x = 1; x < n; x++)
        randomNumArray[x]=randomNumArray[x-1]+averageDifference;
    for (int x = 0; x < n; x++)
    {
        random = getRandomNumberInRange(-averageDifference/2, averageDifference/2);
        randomNumArray[x]+=random;
    }
    return randomNumArray;
}

答案 3 :(得分:1)

  

我需要在a和b之间生成n个随机数,但任何两个数字的差值都不能小于c。除n之外的所有变量都是浮点数(n是int)。

     

在java中首选解决方案,但C / C ++也可以。

首先,分发?我将假设一个统一的分布,但有一点需要注意“任何两个数字不能有小于c 的差异”。你想要什么被称为“拒绝抽样”。有关于这个主题的维基百科文章,以及关于'网络和书籍(例如http://www.columbia.edu/~ks20/4703-Sigman/4703-07-Notes-ARM.pdf)的大量其他参考文献。伪代码,使用一些函数random_uniform()返回从U [0,1]中提取的随机数,并假设一个基于1的数组(许多语言使用基于0的数组):

function generate_numbers (a, b, c, n, result)
  result[1] = a + (b-a)*random_uniform()
  for index from 2 to n
    rejected = true
    while (rejected)
      result[index] = a + (b-a)*random_uniform()
      rejected = abs (result[index] < result[index-1]) < c
    end
  end

答案 4 :(得分:0)

我会通过在a和b之间生成n个随机数来实现这一点。然后我会对它们进行排序并得到第一个顺序差异,踢出任何产生差异小于c的数字,留下m个数字。如果m < n,我会再次这样做,这次是n-m数字,将这些数字添加到我的原始结果中,再次排序,生成差异......依此类推,直到我有n个数字。

注意,一阶差分意味着x [1] - x [0],x [2] - x [1],依此类推。

我没有时间用C写出来,但在R中,这很容易:

getRands<-function(n,a,b,c){
   r<-c()
   while(length(r) < n){
      r<-sort(c(r,runif(n,a,b)))
      r<-r[-(which(diff(r) <= c) + 1 )]
   }
   r

}

注意,如果你对c相对于a和b过于强烈,那么这种解决方案可能需要很长时间才能收敛,或者如果n * C&gt;那么根本不收敛。 b -a

另请注意,我并不是说这个R代码是完全形成的,生产准备好的代码片段,只是算法的例证(对于那些可以跟随R的人)。

答案 5 :(得分:0)

你的解决方案几乎是正确的,这是修复:

maxDistance = b - (randomNumArray[x - 1]) - (n - x - 1) * c;

答案 6 :(得分:0)

在生成数字时如何使用移位范围以确保它们看起来不太近?

static float[] randomNums(float min, float max, float separation, int n) {
    float rangePerNumber = (max - min) / n;

    // Check separation and range are consistent.
    assert (rangePerNumber >= separation) : "You have a problem.";

    float[] randomNumArray = new float[n];

    // Set range for first random number
    float lo = min;
    float hi = lo + rangePerNumber;

    for (int i = 0; i < n; ++i) {
        float random = getRandomNumberInRange(lo, hi);

        // Shift range for next random number.
        lo = random + separation;
        hi = lo + rangePerNumber;

        randomNumArray[i] = random;
    }
    return randomNumArray;
}

答案 7 :(得分:0)

我知道你已经接受了答案,但我喜欢这个问题。我希望它是独一无二的,我还没有详细介绍每个人的答案,我需要运行,所以我会发布这个并希望它有所帮助。

按照这种方式思考:一旦你选择了第一个号码,就会有一个你不能再选择的大块+/- c。

所以你的第一个号码是

range1=b-a
x=Random()*range1+a

此时,x介于a和b之间(假设Random()在0到1之间返回)。现在,我们标出了我们无法再选择的空间

excludedMin=x-c
excludedMax=x+c 

如果x接近任何一端,那么很容易,我们只需选择剩余的空间

if (excludedMin<=a)
{
  range2=b-excludedMax
  y=Random()*range2+excludedMax
}

这里,x非常靠近a,你不会在a和x之间得到y,所以你只需要在x + c和b之间选择。同样地:

else if (excludedMax>=b)
{
   range2=excludedMin-a
   y=Random()*range2+a
}

现在如果x在中间的某个地方,我们必须做一点魔术

else
{
  range2=b-a-2*c
  y=Random()*range2+a
  if (y>excludedMin) y+=2*c
}

这里发生了什么?好吧,我们知道范围y可以位于,比整个空间小2 * c,所以我们在那个较小的空间中选择一个数字。现在,如果y小于excludedMin,我们知道x“在左边”是x-c,我们都没问题。但是,如果y&gt;排除min,我们会向它添加2 * c(总排除空间),以确保它大于x + c,但它仍然小于b,因为我们的范围减小了。

现在,很容易扩展n个数字,每次你只是通过任何其他点之间的排除空间缩小范围。继续,直到排除的空间等于原始范围(b-a)。

答案 8 :(得分:0)

我知道做第二个答案是不好的形式,但我只想到一个......使用递归搜索空间:

假设全球点数列表:points

FillRandom(a,b,c)
{
  range=b-a;
  if (range>0)
  {
    x=Random()*range+a
    points.Append(x)
    FillRandom(a,x-c,c)
    FillRandom(x+c,b,c)
  }
}

我会让你跟踪递归,但最后,你会在points中找到一个用密度1 / c填充空间的列表