2

私は次のような数字のリストを持っています

 $list = array(1,5,19,23,59,51,24) 

実際のコードでは、これはデータベースから生成されるため、この配列は互いに異なる最大 500 個の数値を保持します。

データベース内のこれらの各数値には、発生する確率が記録されています。だから私は、1から500までの乱数を生成するための以前の実行からのデータを持っており、1000回のように生成された各数値の確率を記録しました。

数値と各数値の確率のリストができたので、確率に基づいてこれらの 500 の数値から乱数を生成する関数を書きたいと思います。

例えば:

    number 1 has a chance of: 0.00123 //0.123%
    number 6 has a chance of: 0.0421 //4.21%
    number 11 has a chance of: 0.0133 //1.33%

したがって、変数 $finallist は次のようになります。

   $finallist[1] = 0.00123;
   $finallist[6] = 0.0421;
   $finallist[11] = 0.0133;

ここで、関数を実行して $finallist をパラメーターとして渡すと、1 から 6 までの乱数を取得したいのですが、6 は 1 よりも出てくる可能性が高く、11 は 1 よりも出てくる可能性が高くなります。

確率に基づいて乱数を返す処理を行う関数をいくつか作成しましたが、パラメーターとして 1 つの値しか取りません。

private function randomWithProbability($chance, $num, $range = false)
{
    /* first generate a number 0 and 1 and see if that number is in the range of chance */
    $rand = $this->getRandomFloatValue(0, 1);

    if ($rand <= $chance) 
    {
        /* the number should be returned */
        return $num;
    }
    else 
    {
        /* otherwise return a random number */
        if ($range !== false)
        {
            /* make sure that this number is not same as the number for which we specified the chance */
            $rand = mt_rand(1, $range);
            while ($rand == $num)
            {
                $rand = mt_rand(1, $range);
            }

            return $rand;
        }
    }
}

これを行うためのソリューション/アルゴリズムを誰かが知っている場合、またはPHPに組み込まれているものがあれば、大きな助けになります。どうもありがとう。

4

1 に答える 1

3

探している基本的なアルゴリズム:

  • すべての確率を合計し、最大値を決定します
  • 0 から 1 の間の乱数を選び、最大値を掛けます。
  • その値に対応するエントリを見つけます

コード例:

<?php

// create some weighted sample data (id => weight)
$samples = array(
  'a' => 0.001,
  'b' => 0.004,
  'c' => 0.006,
  'd' => 0.05,
  'e' => 0.01,
  'f' => 0.015,
  'g' => 0.1
);

class Accumulator {
   function __construct($samples) {
      // accumulate all samples into a cumulative amount (a running total)
      $this->acc = array();
      $this->ids = array();
      $this->max = 0;
      foreach($samples as $k=>$v) {
         $this->max += $v;
         array_push($this->acc, $this->max);
         array_push($this->ids, $k);
      }
   }

   function pick() {
      // selects a random number between 0 and 1, increasing the multiple here increases the granularity
      // and randomness; it should probably at least match the precision of the sample data (in this case 3 decimal digits)
      $random = mt_rand(0,1000)/1000 * $this->max;
      for($i=0; $i < count($this->acc); $i++) {
         // looks through the values until we find our random number, this is our seletion
         if( $this->acc[$i] >= $random ) {
            return $this->ids[$i];
         }
      }
      throw new Exception('this is mathematically impossible?');
   }

   private $max; // the highest accumulated number
   private $acc; // the accumulated totals for random selection
   private $ids; // a list of the associated ids
}

$acc = new Accumulator($samples);

// create a results object to test our random generator
$results = array_fill_keys(array_keys($samples), 0);

// now select some data and test the results
print "picking 10000 random numbers...\n";
for($i=0; $i < 10000; $i++) {
   $results[ $acc->pick() ]++;
}

// now show what we found out
foreach($results as $k=>$v) {
   print "$k picked $v times\n";
}

結果:

> php.exe rand.php
picking 10000 random numbers...
a picked 52 times
b picked 198 times
c picked 378 times
d picked 2655 times
e picked 543 times
f picked 761 times
g picked 5413 times

このサンプルで同じコードを実行します。

// samples with even weight
$samples = array(
   'a' => 0.1,
   'b' => 0.1,
   'c' => 0.1,
   'd' => 0.1
);

次の結果が生成されます。

> php.exe rand.php
picking 10000 random numbers...
a picked 2520 times
b picked 2585 times
c picked 2511 times
d picked 2384 times
于 2012-10-28T01:50:10.473 に答える