おぎろぐはてブロ

なんだかんだエンジニアになって10年以上

mt_rand([int $min, int $max])で $min > $max のときの挙動

まぁ、$min < $max でないときの挙動を気にする必要はないのだけれど、mt_rand(0, -100)と入れた場合も、エラーはでずに、-25 とか -86 とか返って来てうまく動いているようにみえる。けど、乱数のレンジが微妙。
サンプルコードを書いてみる。

<?php
list(, $min, $max) = $argv;

foreach (range($min, $max) as $i) {
    $x[$i] = 0;
}
for ($i = 0; $i < 10000; ++$i) {
    $x[mt_rand($min, $max)]++;
}
print_r($x);
?>

list()とかrange()とかべんりですね。
で、指定のmin、maxから10,000回コールの分布をみてみる。

正常! ($min < $max)

ちゃんと0から10まで均等に分布してます

$ php rand.php 0 10
Array
(
    [0] => 949
    [1] => 912
    [2] => 898
    [3] => 910
    [4] => 870
    [5] => 940
    [6] => 914
    [7] => 930
    [8] => 899
    [9] => 921
    [10] => 857
)
$ php rand.php -5 5
Array
(
    [-5] => 875
    [-4] => 889
    [-3] => 889
    [-2] => 904
    [-1] => 905
    [0] => 904
    [1] => 900
    [2] => 903
    [3] => 957
    [4] => 927
    [5] => 947
)

異常

$ php rand.php 0 -10
Array
(
    [0] => 1120
    [-1] => 1121
    [-2] => 1108
    [-3] => 1070
    [-4] => 1117
    [-5] => 1122
    [-6] => 1089
    [-7] => 1121
    [-8] => 1132
    [-9] => 0
    [-10] => 0
)
$ php rand.php 5 -5
Array
(
    [5] => 1065
    [4] => 1161
    [3] => 1114
    [2] => 1100
    [1] => 1077
    [0] => 1125
    [-1] => 1090
    [-2] => 1089
    [-3] => 1179
    [-4] => 0
    [-5] => 0
)

$max側の2つが表れてないことがわかります。

$ php rand.php 10 8
Array
(
    [10] => 10000
    [9] => 0
    [8] => 0
)

で、$i == mt_rand($i, $i - 2)になりそう。

ソース

ソースコードでは、一旦、 (long) (php_mt_rand(TSRMLS_C) >> 1); で、0 〜 PHP_MT_RAND_MAX の乱数を生成して、範囲が指定されている場合に、

#define RAND_RANGE(__n, __min, __max, __tmax) \
    (__n) = (__min) + (long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))

のマクロで範囲に変換してるのですが、算数力が低下していて読み解くのめんどくさくなって挫折。

追記

mt_rand([int $min, int $max])で $min &gt; $max のときの挙動 (理由解説編) - おぎろぐはてなで、このマクロの動きと、それによって起こる上のような問題について解説してます。