【问题标题】:Non-uniform random numbers in Objective-CObjective-C 中的非均匀随机数
【发布时间】:2013-02-06 20:46:48
【问题描述】:

我想计算[0, n - 1] 范围内的非均匀分布随机数。所以最小可能值为零。最大可能值为 n-1。我希望最小值出现最频繁,最大值相对不频繁出现,两者之间的近似线性曲线(高斯也很好)。 如何在 Objective-C 中做到这一点? (可能使用基于 C 的 API)

我当前想法的一个非常粗略的草图是:

// min value w/ p = 0.7
// some intermediate value w/ p = 0.2
// max value w/ p = 0.1
NSUInteger r = arc4random_uniform(10);
if (r <= 6) 
    result = 0;
else if (r <= 8)
    result = (n - 1) / 2;
else
    result = n - 1;

【问题讨论】:

    标签: objective-c c random


    【解决方案1】:

    我认为您基本上走在正确的轨道上。可能存在精度或范围问题,但一般来说,如果您想随机选择 3、2、1 或 0,并且您希望选择 3 的概率是选择 0 的概率的四倍,那么如果是一个纸练习,你可能会在一个填满的网格上:

    3 3 3 3
    2 2 2
    1 1
    0
    

    把东西扔到上面,然后读出它落在的数字。

    您想要的线性比例的选项数量是:

    - 1 if number of options, n, = 1
    - 1 + 2 if n = 2
    - 1 + 2 + 3 if n = 3
    - ... etc ...
    

    这是一个算术级数的简单总和。您最终会得到 n(n+1)/2 个可能的结果。例如。对于 n = 1,这是 1 * 2 / 2 = 1。对于 n = 2,这是 2 * 3 /2 = 3。对于 n = 3,这是 3 * 4 / 2 = 6。

    所以你会立即写这样的东西:

    NSUInteger random_linear(NSUInteger range)
    {
        NSUInteger numberOfOptions = (range * (range + 1)) / 2;
        NSUInteger uniformRandom = arc4random_uniform(numberOfOptions);
    
        ... something ...
    }
    

    此时您只需要决定 uniformRandom 属于哪个 bin。最简单的方法是使用最明显的循环:

    NSUInteger random_linear(NSUInteger range)
    {
        NSUInteger numberOfOptions = (range * (range + 1)) / 2;
        NSUInteger uniformRandom = arc4random_uniform(numberOfOptions);
    
        NSUInteger index = 0;
        NSUInteger optionsToDate = 0;
        while(1)
        {
            if(optionsToDate >= uniformRandom) return index;
            index++;
            optionsToDate += index;
        }
    }
    

    鉴于您可以在不迭代的情况下计算 optionsToDate,一个明显更快的解决方案是二分搜索。

    一个更聪明的看待它的方法是,uniformRandom 是从 (0, 0) 到 (n, n) 的线下方的框的总和。所以它是图形下方的区域,图形是一个简单的直角三角形。所以你可以从面积公式倒推。

    具体来说,图下方从 (0, 0) 到 (n, n) 在位置 x 处的面积为 (x*x)/2。所以你正在寻找 x,其中:

    (x-1)*(x-1)/2 <= uniformRandom < x*x/2
    
    => (x-1)*(x-1) <= uniformRandom*2 < x*x
    => x-1 <= sqrt(uniformRandom*2) < x
    

    在这种情况下,您想取 x-1,因为结果没有进展到数字网格的下一个离散列。所以你可以通过平方根运算简单的整数截断到达那里。

    所以,假设我一路上没有混淆我的确切不等式,并假设所有精度都合适:

    NSUInteger random_linear(NSUInteger range)
    {
        NSUInteger numberOfOptions = (range * (range + 1)) / 2;
        NSUInteger uniformRandom = arc4random_uniform(numberOfOptions);
    
        return (NSUInteger)sqrtf((float)uniformRandom * 2.0f);
    }
    

    【讨论】:

      【解决方案2】:

      如果您尝试将arc4random_uniform() 的返回值平方(或将它们中的两个相乘)会怎样?

      int rand_nonuniform(int max)
      {
          int r = arc4random_uniform(max) * arc4random_uniform(max + 1);
          return r / max;
      }
      

      我已经快速编写了一个示例程序来测试它,它看起来很有希望:

      int main(int argc, char *argv[])
      {
          int arr[10] = { 0 };
      
          int i;
          for (i = 0; i < 10000; i++) {
              arr[rand_nonuniform(10)]++;
          }
      
          for (i = 0; i < 10; i++) {
              printf("%2d. = %2d\n", i, arr[i]);
          }
      
          return 0;
      }
      

      结果:

      0. = 3656
      1. = 1925
      2. = 1273
      3. = 909
      4. = 728
      5. = 574
      6. = 359
      7. = 276
      8. = 187
      9. = 113
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-28
        • 1970-01-01
        • 1970-01-01
        • 2020-09-07
        • 2011-03-02
        • 2010-12-04
        • 1970-01-01
        相关资源
        最近更新 更多