【问题标题】:Adjust function for pyramid-like distribution金字塔状分布的调整功能
【发布时间】:2011-12-22 20:00:44
【问题描述】:

this question 中,我得到了帮助,我编写了一个 PHP 函数,该函数给出了类似金字塔的分布:

function getRandomStrength($min, $max) {
    $ln_low = log($min, M_E);
    $ln_high = log($max, M_E);
    $scale = $ln_high-$ln_low;
    $rand = (mt_rand()/mt_getrandmax())*$scale+$ln_low;
    $value = round(pow(M_E, $rand), 1);
    return $value;
}
getRandomStrenth(1.1, 9.9);
// output could be: 1.4 or 8.3 or 9.8 or 7.2 or 2.9 or ...

当我运行 50,000 次迭代并检查从 1 到 9 的数字出现的频率时,我得到以下列表:

  • 1 » 26%
  • 2 » 19%
  • 3 » 14%
  • 4 » 10%
  • 5 » 9%
  • 6 » 7%
  • 7 » 6%
  • 8 » 6%
  • 9 » 4%

这就是我想要的。但现在我想稍微调整一下这个功能。较小的值应该出现得更频繁,而较大的值应该出现得更少——所以我得到一个这样的列表:

  • 1 » 28%
  • 2 » 20%
  • 3 » 15%
  • 4 » 11%
  • 5 » 9%
  • 6 » 6%
  • 7 » 5%
  • 8 » 5%
  • 9 » 2%

如您所见,我只需要轻微 修改。但是我可以更改什么以使我的函数按预期运行?

我尝试了几件事(例如更改对数的底),但这并没有改变任何东西。

【问题讨论】:

    标签: php function random numbers distribution


    【解决方案1】:

    您可以对随机数使用 pow。

    $rand = pow( mt_rand()/mt_getrandmax(), 1.2 )*$scale+$ln_low;

    通过使用指数值,您可以获得更少或更多的小值。

    【讨论】:

    • 问题是结果值必须在给定的范围内(在参数 $min 和 $max 之间)。不能只使用与 ln() 不同的另一个函数吗?
    • mt_rand()/mt_getrandmax() 介于 0 和 1 之间,pow(mt_rand()/mt_getrandmax(), x) 与任何 x 将介于 0 和 1 之间。因此 $rand 将具有完全相同的范围,但分布不同
    • 哦,是的,你是对的,当然:D 这么简短的回答,但是太好了!这正是我所需要的。但有一件事很惊人:如果我计算从 1.1 到 9.9 的所有值的相对频率,我可以看到 1.1 的频率低于 1.2。为什么会这样? 1.1 应该更频繁,因为它更小,对吧?这不是一个随试验而变化的随机事情。这是一个很大的区别。我得到了这些值:1.1»6.14%、1.2»7.1%、1.3»5.08%
    • 原因很简单,你是在用轮子来创造你的价值。但是圆形没有正确分布。让我们举个简单的例子:如果我取一个 0 到 2 之间的随机数,round 将创建这样的值:0->0.49999 = 0, 0.5->1.49999 = 1, 1.5->2 = 2. 那么分布是 25 % 代表 0,50% 代表 1,25% 代表 2。您的代码遇到了这个问题
    • 当然是这个原因。对我来说应该很明显!非常感谢您的解释、出色的 cmets 和简单但很好的答案。
    【解决方案2】:

    将函数的$scale 减少少量(恒定)似乎会产生非常接近您正在寻找的结果。您可以通过将$scale 的减少作为从mt_rand() 随机生成的数字的函数来获得更准确的结果,这需要将(mt_rand()/mt_getrandmax()) 保存到一个变量中并对$scale 执行一些额外的数学运算。

    这是我的测试,你可以自己运行:http://codepad.viper-7.com/ssblbQ

    function getRandomStrength($min, $max) 
    {
        $ln_low = log($min, M_E);
        $ln_high = log($max, M_E);
        $scale = $ln_high-$ln_low - .05; // Subtract a small constant, vary between .05 and .08
        $rand = (mt_rand()/mt_getrandmax())*$scale+$ln_low;
        $value = round(pow(M_E, $rand), 1);
        return $value;
    }
    
    $values = array_fill(1, 9, 0);
    for( $i = 0; $i < 50000; $i++) 
    {
        $values[ intval( getRandomStrength(1.1, 9.9)) ]++;
    }
    
    for( $i = 1; $i <= 9; $i++) 
    {
        $values[ $i] /= 500; // / 50000 * 100 to get a percent
    }
    
    var_dump( $values);
    

    输出

    运行 #1 - 常数 = 0.5

    array(9) {
      [1] => float(26.626) // Should be 28
      [2] => float(19.464) // Should be 20
      [3] => float(13.476) // Should be 15
      [4] => float(10.41) // Should be 11
      [5] => float(8.616) // Should be 9
      [6] => float(7.198) // Should be 6
      [7] => float(6.258) // Should be 5
      [8] => float(5.52) // Should be 5
      [9] => float(2.432) // Should be 2
    }
    

    运行 #2 - 常数 = 0.65

    array(9) {
      [1] => float(26.75) // Should be 28
      [2] => float(19.466) // Should be 20
      [3] => float(13.872) // Should be 15
      [4] => float(10.562) // Should be 11
      [5] => float(8.466) // Should be 9
      [6] => float(7.222) // Should be 6
      [7] => float(6.454) // Should be 5
      [8] => float(5.554) // Should be 5
      [9] => float(1.654) // Should be 2
    }
    

    运行 #3 - 常数 = 0.70

    array(9) {
      [1] => float(26.848) // Should be 28
      [2] => float(19.476) // Should be 20
      [3] => float(13.808) // Should be 15
      [4] => float(10.764) // Should be 11
      [5] => float(8.67) // Should be 9
      [6] => float(7.148) // Should be 6
      [7] => float(6.264) // Should be 5
      [8] => float(5.576) // Should be 5
      [9] => float(1.446) // Should be 2
    }
    

    【讨论】:

    • 非常感谢您的详细调查和测试!唯一的问题是我需要在 $min 和 $max (参数)之间的范围内的值。如果我正确理解您的修改,9.9 之类的值将不再可能,对吗?
    • 是的,你没看错,最大值在9.3左右我的错,我不知道这个限制。
    • 我当然应该说得更清楚些。无论如何,非常感谢您的努力!
    【解决方案3】:

    对于 {0..1} 中的 n,y=(x^n)-1,y 的范围为 0 到 x-1。然后,通过乘以范围并除以 (x-1),该曲线很容易从 0 映射到某个最大值。如果将 x 值更改为接近 1 的值,则曲线将接近于线性,并且在较大的值下,曲线变得更像曲棍球棒,但仍会落在相同的范围内。

    我的初始样本值 3 与您所表达的并不完全相同,但您可以对其进行调整以获得您正在寻找的分布曲线。

    function getCustomStrength($min, $max, $x_val, $base) {
      $logmax = $base-1;
      $range = $max-$min;
      return (pow($base,$x_val)-1)*($range/($base-1))+$min;
    }
    
    function getRandomStrength($min, $max) {
      $rand = mt_rand()/mt_getrandmax();
      $base = 3.0;
      return getCustomStrength($min, $max, $rand, $base);
    }
    

    getRandomStrength(1.1, 9.9);

    【讨论】:

    • 只是对您最终得到的任何公式的注释...如果您提供一个均匀分布的值列表来表示您的随机数,您可以在折线/条形图中可视化分数的分布。
    • 非常感谢你的回答,phatfingers :) 经过一些测试,我发现你的函数与我的函数在crazyjul的修改后给出的分布完全相同。所以两者都是相似的,我使用哪一个并不重要。但是你能再解释一下你的最后评论吗?我没明白你的意思,对不起。我不明白我必须做什么(所以“如果……随机数”)。
    • 在编码之前,我模拟了一个电子表格以更好地理解问题。我使用了类似于伪代码的东西: for ($i=0; $i
    • 啊,是的,现在我明白了:) 非常感谢,这是个好主意!再次感谢您提供的优质代码。
    猜你喜欢
    • 1970-01-01
    • 2017-03-16
    • 2017-10-19
    • 2012-09-30
    • 2016-04-15
    • 1970-01-01
    • 2019-09-14
    • 2017-06-13
    • 1970-01-01
    相关资源
    最近更新 更多