【问题标题】:Generate random numbers in increments以增量方式生成随机数
【发布时间】:2011-07-26 19:16:12
【问题描述】:

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

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]

【问题讨论】:

  • 你有什么问题吗?另外,你没有问任何问题。
  • 你的代码有什么问题,你对它不满意?
  • @retrodone 它实际上是在我的游戏中随机生成平台,因为两个平台不能靠得太近,不是功课。
  • 已编辑问题以显示不正确的输出。
  • 正如你所说的那样,你真的是在自找麻烦。您是否需要生成一个具有最少平台数量的解决方案,尽管如果选择不当,它们可能不会都适合 a 和 b?我只是放置一个平台,走一个大于 c 的随机距离,然后放置另一个平台。

标签: java c++ c random


【解决方案1】:

我认为一个合理的方法可以是:

  1. 总“空间”为(b - a)
  2. 删除所需的最小空间(n-1)*c以获取剩余空间
  3. 拍摄(n-1) 0 和1 之间的随机数并缩放它们,使总和是刚刚计算的“可选空间”。它们中的每一个都是要使用的空间“切片”。
  4. 第一个号码是a
  5. 对于每个其他号码,将c 和下一个“切片”添加到前一个号码。最后一个号码是b

如果您不希望 first 和 last 完全匹配 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)

【讨论】:

  • 这就是我的想法,除了一点点变化:删除(n-1)切片的长度,生成n(不同的)数字,并在数字之间插入切片(第一个和最后一个号码不会在ab,而是随机的)。
  • 仍然是早期选择改变后期选择分布的牺牲品。选择过程开始时的几个大切片可以保证您永远不会选择另一个同样大的切片。实际上,后一种选择表明方差减小。
  • @Edwin 我不这么认为。如果您采用n-1 随机数,对它们进行排序并缩放它们的范围,那么选择过程的“开始”和“结束”没有任何意义。但我怀疑仍然存在问题,因为 2 个异常值对其他数字的影响很大。
  • @Edwin:请仔细阅读。通过选择 N-1 个随机数并对其进行缩放,使总和恰好是分布的空间,分布将是均匀的(在最小值之上的部分)。随机挑选和排序不会给出松弛空间切片的均匀分布。出于这个原因,我没有使用 sort 。我将添加确切的切片向量计算代码以使其更清晰。
  • @6502 是的,请添加一些代码(甚至是伪代码),因为我在理解算法时遇到了一些麻烦。
【解决方案2】:

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

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

通过尝试将基数排除在下一个数字例程之外,您会为您的算法增加很多复杂性。

【讨论】:

  • 如果你很贪心并且几乎占据了第一个号码的所有剩余空间,那么 minDists 将没有空间用于其余的。
  • 目前的算法也不能保证这也不会发生。事实上,不可能根据算法生成真正的随机数并始终满足标准。如果随机性真的很重要,最好生成一组十个数字并检查它是否符合标准,丢弃不需要的集合。
  • 原始算法确实保证它可以生成 n 个数字,只是有一个小错误,请参阅我的答案。它确实创建均匀分布虽然
【解决方案3】:

虽然这并不能完全满足您的需求,也没有包含此线程中描述的技术,但我相信此代码将证明是有用的,因为它会按照您的意愿执行。

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

【讨论】:

  • 我会测试这个和今晚发布的其他人。
  • 你会得到一个太好的发行版,每个 bin 都有一个平台。但是,对于所考虑的应用程序来说,这可能不是一件坏事。此外,除非有很多空白空间 ((b - a) > 2 * n * c),否则您的算法会失败,但对于原始帖子中给出的数字示例来说,这也是可以的。
【解决方案4】:

我需要在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

【讨论】:

  • 我猜您还假设您在添加元素时正在对result 进行排序。您还应该通过查看左右邻居来检查它是否被拒绝。
  • 不,我不假设任何此类。 OP没有指定“任何两个数字的差不能小于c”的含义。我假设任何两个相邻的数字。如果 OP 意味着需要不同的澄清。
  • 那你不应该检查所有对abs (result[i] - result[j]) &lt; c吗?当您生成一个新结果时,它可能位于先前结果的左侧,因为它们都是使用a + (b-a)*random_uniform() 生成的。
  • @David 对不起,我的意思是生成的两个数字不能小于c
  • @Amandeep:你想要完成什么?你想解决什么问题?如果基础过程是离散的,请使用离散分布。如果是底层过程,那么据我所知,没有任何物理过程会产生接近这种分布的任何东西。
【解决方案5】:

您的解决方案几乎是正确的,这是解决方法:

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

【讨论】:

  • 我将两个 maxDistance 行更改为:float maxDistance = b - (n - 1) * c;maxDistance = b - (randomNumArray[x - 1]) - (n - x - 1) * c;。此处输出:pastie.org/pastes/2275548/text?key=vlh81hmpi1eqb91w5agiq 综上所述,第一个数字总是-0.9,没有足够的负数来随机化。
  • nonono,第一个 maxDistance 很好! 只需修正我所说的
  • 同样,如果我不触摸我的第一个 maxDistance,我会得到这个输出(没有足够的负数):pastie.org/pastes/2275614/text
  • right..:/ 它修复了错误,但主要概念有缺陷,它不会生成均匀分布
  • 是的,我并没有说我的算法在正确的轨道上,只是我能想到的最好的。
【解决方案6】:

我会通过在 a 和 b 之间生成 n 个随机数来做到这一点。然后我会对它们进行排序并获得一阶差异,排除任何产生小于 c 的差异的数字,留下 m 个数字。如果 m

注意,一阶差分表示 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 > b -a 则根本不收敛

另外请注意,我并不是说这个 R 代码是一个完整的、生产就绪的代码,只是算法的一个说明(对于那些可以遵循 R 的人)。

【讨论】:

  • 这会产生平均分配吗?如果 (n_1)*c 接近 b-a,它会完成吗?我对此表示怀疑。
  • 我也觉得有无限循环的可能。如果 a=0, b=1, c=0.1, and n=10,生成的数字是 0, 1.5, 3, 4.5, 6, 7.5, 9。现在你只有 7 个数字,但不能在任何地方插入另一个数字。
  • @Amandeep,这是指定问题方式的根本缺陷。
  • @yi_H 可以均匀分布。但是我想您需要添加一个条件,即当间隔太短时,您以 50-50 的概率拒绝间隔开始或结束时的值。即使那样,我也不确定这是否足以实现统一。仅生成 n 值可能更简单,如果存在冲突,则比使用 n 新值重新开始。但这需要更长的时间才能找到解决方案。
【解决方案7】:

在生成数字时使用移动范围如何确保它们不会显得太接近?

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

【讨论】:

  • 您的分布将严重偏左。
  • 同意。我不确定这不是家庭作业,所以我故意犯了错误:)。通过为每次迭代重新计算 rangePerNumber 可以减少该问题。
【解决方案8】:

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

这样想:一旦你选择了你的第一个数字,你就有一个 +/- 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 小于 excludeMin,我们知道 y “在 x-c 的左侧”,我们都可以。但是,如果 y>excluded min,我们添加 2*c(总排除空间),以确保它大于 x+c,但仍会小于 b,因为我们的范围缩小了。

现在,扩展 n 个数字很容易,每次只需将范围缩小到其他点之间的排除空间即可。您继续,直到排除的空间等于原始范围 (b-a)。

【讨论】:

    【解决方案9】:

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

    假设一个全局点列表: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 的密度填充空间

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-28
      • 1970-01-01
      • 2020-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多