【问题标题】:Generate Random Numbers with Probabilistic Distribution生成具有概率分布的随机数
【发布时间】:2011-03-07 18:48:45
【问题描述】:

好的,这是我的问题。我们正在考虑从一家公司购买数据集以扩充我们现有的数据集。出于这个问题的目的,假设该数据集使用有机数字对地点进行排名(这意味着分配给一个地方的数字与分配给另一个地方的数字无关)。技术范围是 0 到无穷大,但从我看到的样本集来看,它是 0 到 70。根据样本,它绝对不是均匀分布(在 10,000 个中,可能有 5 个位置得分超过 40, 50 分超过 10 分,1000 分超过 1 分)。在我们决定购买此套装之前,我们想对其进行模拟,以便了解它的用途。

所以,为了模拟它,我一直在考虑为每个地方生成一个随机数(大约 150,000 个随机数)。但是,我也想保持数据的精神,并保持分布相对相同(或至少相当接近)。我整天都在绞尽脑汁想办法做到这一点,结果却一无所获。

我的一个想法是对随机数(在 0 和 sqrt(70) 之间)进行平方。但这将有利于小于 1 和更大的数字。

我在想他的真实分布在第一象限应该是双曲线的......我只是对如何将随机数的线性均匀分布转变为双曲线分布(如果双曲线是我想要的首先)。

有什么想法吗?

所以,总而言之,这是我想要的分布(大约):

  • 40 - 70:0.02% - 0.05%
  • 10 - 40: 0.5% - 1%
  • 1 - 10: 10% - 20%
  • 0 - 1:剩余 (78.95% - 89.48%)

【问题讨论】:

  • 我找到了这个统计词汇表 [stats.gla.ac.uk/steps/glossary/… ]。这可能会有所帮助。
  • 我不太明白。您是否有 0 到 70 之间的 10k 浮点数要分配到一组 150k 上?
  • @Jonas Elfström:嗯,反过来。我想生成 150k 具有指定分布的随机浮点数...

标签: php random distribution probability


【解决方案1】:

查看可靠性分析中使用的分布 - 它们往往有这些长尾。一个相对简单的可能性是具有 P(X>x)=exp[-(x/b)^a] 的 Weibull 分布。

将您的值拟合为 P(X>1)=0.1 和 P(X>10)=0.005,我得到 a=0.36 和 b=0.1。这意味着 P(X>40)*10000=1.6,这有点太低了,但 P(X>70)*10000=0.2 是合理的。

编辑 哦,要从统一 (0,1) 值 U 生成 Weibull 分布的随机变量,只需计算 b*[-log(1-u)]^(1/a)。这是 1-P(X>x) 的反函数,以防我计算错误。

【讨论】:

  • 哇,这看起来与我所追求的结果集几乎相同 (4 > 40, 60 > 10, 1030 > 1)。优秀!谢谢!
  • 您是否有机会使用有效的 PHP 代码更新您的答案?
【解决方案2】:

生成遵循给定分布的随机数的最简单(但不是很有效)的方法是一种称为Von Neumann Rejection 的技术。

技术的简单解释是这样的。创建一个完全封闭您的发行版的盒子。 (让我们调用你的分布f)然后在框中选择一个随机点(x,y)。如果y < f(x),则使用x 作为随机数。如果y > f(x),则丢弃xy 并选择另一个点。继续,直到您有足够数量的值可供使用。您不拒绝的x的值将按照f进行分配。

【讨论】:

  • 除非我弄错了,这不就是在f(x)定义的曲线下得到随机点吗?考虑到我的曲线看起来是双曲线的,点的最大密度将在原点周围,因此生成的数字不会偏向在原点和顶点之间创建的有界框的中间(因此不喜欢较低的数字作为我需要它)?
【解决方案3】:

这种幼稚的做法很可能会以某种我现在看不到的方式扭曲分布。这个想法只是迭代你的第一个数据集,排序和成对。然后在每对之间随机化 15 个新数字,得到新数组。

Ruby 示例,因为我不会说太多 PHP。希望这样一个简单的想法应该很容易转化为 PHP。

numbers=[0.1,0.1,0.12,0.13,0.15,0.17,0.3,0.4,0.42,0.6,1,3,5,7,13,19,27,42,69]
more_numbers=[]
numbers.each_cons(2) { |a,b| 15.times { more_numbers << a+rand()*(b-a) } }
more_numbers.sort!

【讨论】:

    【解决方案4】:

    几年前为 PHP4 编写的,只需选择您的发行版:

    <?php
    
    define( 'RandomGaussian',           'gaussian' ) ;          //  gaussianWeightedRandom()
    define( 'RandomBell',               'bell' ) ;              //  bellWeightedRandom()
    define( 'RandomGaussianRising',     'gaussianRising' ) ;    //  gaussianWeightedRisingRandom()
    define( 'RandomGaussianFalling',    'gaussianFalling' ) ;   //  gaussianWeightedFallingRandom()
    define( 'RandomGamma',              'gamma' ) ;             //  gammaWeightedRandom()
    define( 'RandomGammaQaD',           'gammaQaD' ) ;          //  QaDgammaWeightedRandom()
    define( 'RandomLogarithmic10',      'log10' ) ;             //  logarithmic10WeightedRandom()
    define( 'RandomLogarithmic',        'log' ) ;               //  logarithmicWeightedRandom()
    define( 'RandomPoisson',            'poisson' ) ;           //  poissonWeightedRandom()
    define( 'RandomDome',               'dome' ) ;              //  domeWeightedRandom()
    define( 'RandomSaw',                'saw' ) ;               //  sawWeightedRandom()
    define( 'RandomPyramid',            'pyramid' ) ;           //  pyramidWeightedRandom()
    define( 'RandomLinear',             'linear' ) ;            //  linearWeightedRandom()
    define( 'RandomUnweighted',         'non' ) ;               //  nonWeightedRandom()
    
    
    
    function mkseed()
    {
        srand(hexdec(substr(md5(microtime()), -8)) & 0x7fffffff) ;
    }   //  function mkseed()
    
    
    
    
    /*
    function factorial($in) {
        if ($in == 1) {
            return $in ;
        }
        return ($in * factorial($in - 1.0)) ;
    }   //  function factorial()
    
    
    function factorial($in) {
        $out = 1 ;
        for ($i = 2; $i <= $in; $i++) {
            $out *= $i ;
        }
    
        return $out ;
    }   //  function factorial()
    */
    
    
    
    
    function random_0_1()
    {
        //  returns random number using mt_rand() with a flat distribution from 0 to 1 inclusive
        //
        return (float) mt_rand() / (float) mt_getrandmax() ;
    }   //  random_0_1()
    
    
    function random_PN()
    {
        //  returns random number using mt_rand() with a flat distribution from -1 to 1 inclusive
        //
        return (2.0 * random_0_1()) - 1.0 ;
    }   //  function random_PN()
    
    
    
    
    function gauss()
    {
        static $useExists = false ;
        static $useValue ;
    
        if ($useExists) {
            //  Use value from a previous call to this function
            //
            $useExists = false ;
            return $useValue ;
        } else {
            //  Polar form of the Box-Muller transformation
            //
            $w = 2.0 ;
            while (($w >= 1.0) || ($w == 0.0)) {
                $x = random_PN() ;
                $y = random_PN() ;
                $w = ($x * $x) + ($y * $y) ;
            }
            $w = sqrt((-2.0 * log($w)) / $w) ;
    
            //  Set value for next call to this function
            //
            $useValue = $y * $w ;
            $useExists = true ;
    
            return $x * $w ;
        }
    }   //  function gauss()
    
    
    function gauss_ms( $mean,
                       $stddev )
    {
        //  Adjust our gaussian random to fit the mean and standard deviation
        //  The division by 4 is an arbitrary value to help fit the distribution
        //      within our required range, and gives a best fit for $stddev = 1.0
        //
        return gauss() * ($stddev/4) + $mean;
    }   //  function gauss_ms()
    
    
    function gaussianWeightedRandom( $LowValue,
                                     $maxRand,
                                     $mean=0.0,
                                     $stddev=2.0 )
    {
        //  Adjust a gaussian random value to fit within our specified range
        //      by 'trimming' the extreme values as the distribution curve
        //      approaches +/- infinity
        $rand_val = $LowValue + $maxRand ;
        while (($rand_val < $LowValue) || ($rand_val >= ($LowValue + $maxRand))) {
            $rand_val = floor(gauss_ms($mean,$stddev) * $maxRand) + $LowValue ;
            $rand_val = ($rand_val + $maxRand) / 2 ;
        }
    
        return $rand_val ;
    }   //  function gaussianWeightedRandom()
    
    
    function bellWeightedRandom( $LowValue,
                                 $maxRand )
    {
        return gaussianWeightedRandom( $LowValue, $maxRand, 0.0, 1.0 ) ;
    }   //  function bellWeightedRandom()
    
    
    function gaussianWeightedRisingRandom( $LowValue,
                                           $maxRand )
    {
        //  Adjust a gaussian random value to fit within our specified range
        //      by 'trimming' the extreme values as the distribution curve
        //      approaches +/- infinity
        //  The division by 4 is an arbitrary value to help fit the distribution
        //      within our required range
        $rand_val = $LowValue + $maxRand ;
        while (($rand_val < $LowValue) || ($rand_val >= ($LowValue + $maxRand))) {
            $rand_val = $maxRand - round((abs(gauss()) / 4) * $maxRand) + $LowValue ;
        }
    
        return $rand_val ;
    }   //  function gaussianWeightedRisingRandom()
    
    
    function gaussianWeightedFallingRandom( $LowValue,
                                            $maxRand )
    {
        //  Adjust a gaussian random value to fit within our specified range
        //      by 'trimming' the extreme values as the distribution curve
        //      approaches +/- infinity
        //  The division by 4 is an arbitrary value to help fit the distribution
        //      within our required range
        $rand_val = $LowValue + $maxRand ;
        while (($rand_val < $LowValue) || ($rand_val >= ($LowValue + $maxRand))) {
            $rand_val = floor((abs(gauss()) / 4) * $maxRand) + $LowValue ;
        }
    
        return $rand_val ;
    }   //  function gaussianWeightedFallingRandom()
    
    
    function logarithmic($mean=1.0, $lambda=5.0)
    {
        return ($mean * -log(random_0_1())) / $lambda ;
    }   //  function logarithmic()
    
    
    function logarithmicWeightedRandom( $LowValue,
                                        $maxRand )
    {
        do {
            $rand_val = logarithmic() ;
        } while ($rand_val > 1) ;
    
        return floor($rand_val * $maxRand) + $LowValue ;
    }   //  function logarithmicWeightedRandom()
    
    
    function logarithmic10( $lambda=0.5 )
    {
        return abs(-log10(random_0_1()) / $lambda) ;
    }   //  function logarithmic10()
    
    
    function logarithmic10WeightedRandom( $LowValue,
                                          $maxRand )
    {
        do {
            $rand_val = logarithmic10() ;
        } while ($rand_val > 1) ;
    
        return floor($rand_val * $maxRand) + $LowValue ;
    }   //  function logarithmic10WeightedRandom()
    
    
    function gamma( $lambda=3.0 )
    {
        $wLambda = $lambda + 1.0 ;
        if ($lambda <= 8.0) {
            //  Use direct method, adding waiting times
            $x = 1.0 ;
            for ($j = 1; $j <= $wLambda; $j++) {
                $x *= random_0_1() ;
            }
            $x = -log($x) ;
        } else {
            //  Use rejection method
            do {
                do {
                    //  Generate the tangent of a random angle, the equivalent of
                    //      $y = tan(pi * random_0_1())
                    do {
                        $v1 = random_0_1() ;
                        $v2 = random_PN() ;
                    } while (($v1 * $v1 + $v2 * $v2) > 1.0) ;
                    $y = $v2 / $v1 ;
                    $s = sqrt(2.0 * $lambda + 1.0) ;
                    $x = $s * $y + $lambda ;
                //  Reject in the region of zero probability
                } while ($x <= 0.0) ;
                //  Ratio of probability function to comparison function
                $e = (1.0 + $y * $y) * exp($lambda * log($x / $lambda) - $s * $y) ;
            //  Reject on the basis of a second uniform deviate
            } while (random_0_1() > $e) ;
        }
    
        return $x ;
    }   //  function gamma()
    
    
    function gammaWeightedRandom( $LowValue,
                                  $maxRand )
    {
        do {
            $rand_val = gamma() / 12 ;
        } while ($rand_val > 1) ;
    
        return floor($rand_val * $maxRand) + $LowValue ;
    }   //  function gammaWeightedRandom()
    
    
    function QaDgammaWeightedRandom( $LowValue,
                                     $maxRand )
    {
        return round((asin(random_0_1()) + (asin(random_0_1()))) * $maxRand / pi()) + $LowValue ;
    }   //  function QaDgammaWeightedRandom()
    
    
    function gammaln($in)
    {
        $tmp = $in + 4.5 ;
        $tmp -= ($in - 0.5) * log($tmp) ;
    
        $ser = 1.000000000190015
                + (76.18009172947146 / $in)
                - (86.50532032941677 / ($in + 1.0))
                + (24.01409824083091 / ($in + 2.0))
                - (1.231739572450155 / ($in + 3.0))
                + (0.1208650973866179e-2 / ($in + 4.0))
                - (0.5395239384953e-5 / ($in + 5.0)) ;
    
        return (log(2.5066282746310005 * $ser) - $tmp) ;
    }   //  function gammaln()
    
    
    function poisson( $lambda=1.0 )
    {
        static $oldLambda ;
        static $g, $sq, $alxm ;
    
        if ($lambda <= 12.0) {
            //  Use direct method
            if ($lambda <> $oldLambda) {
                $oldLambda = $lambda ;
                $g = exp(-$lambda) ;
            }
            $x = -1 ;
            $t = 1.0 ;
            do {
                ++$x ;
                $t *= random_0_1() ;
            } while ($t > $g) ;
        } else {
            //  Use rejection method
            if ($lambda <> $oldLambda) {
                $oldLambda = $lambda ;
                $sq = sqrt(2.0 * $lambda) ;
                $alxm = log($lambda) ;
                $g = $lambda * $alxm - gammaln($lambda + 1.0) ;
            }
            do {
                do {
                    //  $y is a deviate from a Lorentzian comparison function
                    $y = tan(pi() * random_0_1()) ;
                    $x = $sq * $y + $lambda ;
                //  Reject if close to zero probability
                } while ($x < 0.0) ;
                $x = floor($x) ;
                //  Ratio of the desired distribution to the comparison function
                //  We accept or reject by comparing it to another uniform deviate
                //  The factor 0.9 is used so that $t never exceeds 1
                $t = 0.9 * (1.0 + $y * $y) * exp($x * $alxm - gammaln($x + 1.0) - $g) ;
            } while (random_0_1() > $t) ;
        }
    
        return $x ;
    }   //  function poisson()
    
    
    function poissonWeightedRandom( $LowValue,
                                    $maxRand )
    {
        do {
            $rand_val = poisson() / $maxRand ;
        } while ($rand_val > 1) ;
    
        return floor($x * $maxRand) + $LowValue ;
    }   //  function poissonWeightedRandom()
    
    
    function binomial( $lambda=6.0 )
    {
    }
    
    
    function domeWeightedRandom( $LowValue,
                                 $maxRand )
    {
        return floor(sin(random_0_1() * (pi() / 2)) * $maxRand) + $LowValue ;
    }   //  function bellWeightedRandom()
    
    
    function sawWeightedRandom( $LowValue,
                                $maxRand )
    {
        return floor((atan(random_0_1()) + atan(random_0_1())) * $maxRand / (pi()/2)) + $LowValue ;
    }   //  function sawWeightedRandom()
    
    
    function pyramidWeightedRandom( $LowValue,
                                   $maxRand )
    {
        return floor((random_0_1() + random_0_1()) / 2 * $maxRand) + $LowValue ;
    }   //  function pyramidWeightedRandom()
    
    
    function linearWeightedRandom( $LowValue,
                                   $maxRand )
    {
        return floor(random_0_1() * ($maxRand)) + $LowValue ;
    }   //  function linearWeightedRandom()
    
    
    function nonWeightedRandom( $LowValue,
                                $maxRand )
    {
        return rand($LowValue,$maxRand+$LowValue-1) ;
    }   //  function nonWeightedRandom()
    
    
    
    
    function weightedRandom( $Method,
                             $LowValue,
                             $maxRand )
    {
        switch($Method) {
            case RandomGaussian         :
                $rVal = gaussianWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomBell             :
                $rVal = bellWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomGaussianRising   :
                $rVal = gaussianWeightedRisingRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomGaussianFalling  :
                $rVal = gaussianWeightedFallingRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomGamma            :
                $rVal = gammaWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomGammaQaD         :
                $rVal = QaDgammaWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomLogarithmic10    :
                $rVal = logarithmic10WeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomLogarithmic      :
                $rVal = logarithmicWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomPoisson          :
                $rVal = poissonWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomDome             :
                $rVal = domeWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomSaw              :
                $rVal = sawWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomPyramid          :
                $rVal = pyramidWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            case RandomLinear           :
                $rVal = linearWeightedRandom( $LowValue, $maxRand ) ;
                break ;
            default                     :
                $rVal = nonWeightedRandom( $LowValue, $maxRand ) ;
                break ;
        }
    
        return $rVal;
    }
    
    ?>
    

    【讨论】:

    • 感谢您的代码。但是,我尝试查找您提供的所有方法,但没有看到任何似乎适合我的模型的方法。统计数据从来都不是我的强项。如果你能指出一个你认为合适的模型,我会全神贯注......谢谢......
    • 一种选择是尝试生成一系列值并使用每个不同的预定义分布将它们绘制在图表上,以查看曲线的样子。 Wikipedia 在其中许多分布上也有大量条目.....尽管对于您所描述的内容(如果我的解释正确的话),如果您想要更多的上限值,请尝试 gaussianWeightedRisingRandom,如果您想要更多的下限值,请尝试 gaussianWeightedFallingRandom。 .. 虽然泊松对于许多现实世界的情况通常是一种有用的方法
    • 好的,我都试过了。 GaussianWeightedFallingRandom 是最接近的,但它的下降速度仍然不够快(200 而不是 5 超过 40,5700 而不是 50 超过 10,9500 而不是 1000 超过 1。我试过 csch,它看起来更接近(因为它匹配高范围),但在中间下降得太快。想法?
    • 在这种情况下,请使用 gaussianWeightedRandom( $LowValue, $maxRand, $mean, $stddev ) 但设置您自己的均值和标准差值,或者修改 GaussianWeightedFallingRandom 中对 gauss() 的调用() 调用 gauss_ms( $mean, $stddev ) 用您自己的平均值和标准差值。这可能需要一些实验......但请查看维基百科页面以了解这些参数的更改如何影响曲线的形状
    • @MarkBaker 很棒的资源!!我看到这篇文章已经很老了,但请问基于经验数据的离散分布函数是否可能不比这里的理论函数更好?
    猜你喜欢
    • 1970-01-01
    • 2012-02-11
    • 1970-01-01
    • 1970-01-01
    • 2014-10-06
    • 2017-12-20
    • 2013-02-08
    • 2015-05-18
    相关资源
    最近更新 更多