LoginSignup
21
8

More than 1 year has passed since last update.

【PHP8.2】PHPの乱数がさらに改善される

Posted at

PHP8.2でPHPの乱数がとても改善するという話をしましたが、RFCが採択された後になって幾つか問題や不足が出てきたため、それらの対応を行うフォローアップのRFCが提出されました。

ということで以下は該当のRFC、Random Extension Improvementの紹介です。
7つの提案があったのですが、全て採択されています。

なお、このRFCはPHP8.2向けなので、2022/12/08にリリースされたPHP8.2から既に使用可能です。

PHP RFC: Random Extension Improvement

Introduction

先日のRandom Extension 5.xにおいて、投票が始まったあとで幾つか問題が発生ました。

Engine implementations are not final

Random Extensionでネイティブに提供されたRNGエンジンのクラスには、finalがついていませんでした。
これはつまりネイティブクラスをextendsしたクラスが作れてしまうわけですが、前回のRFCで述べたように、ユーザランドエンジンは実行効率がネイティブクラスに劣ります。
この効率劣化はメソッドをオーバーライドしなかった場合でも発生するため問題になります。

エクステンションは既にRandom\Engineインターフェイスを提供しているため、ネイティブクラスがfinalになったとしてもこちらを使えば問題ありません。

これはAPI設計のミスであり、乱数エンジンのネイティブ実装はfinalにすべきです。

Random\SerializableEngine is not useful

Random\SerializableEngineは、Serializableインターフェイスがまだ生きていたころの名残です。
現在のPHPは、シリアライズ可能か否かは__serialize()メソッドが存在するかだけで判断されるようになったため、このインターフェイスは不要になりました。

従って、Random\SerializableEngineを削除します。

現在SerializableEngineをimplementsしている乱数エンジンはこれを実装しなくなりますが、シリアライズ可能かどうかの判定には影響しません。

Random\Engine\CombinedLCG is low quality

Random\Engine\CombinedLCGlcg_value関数でのみ使われている乱数エンジンです。
しかし、このアルゴリズムは古めかしく、そして低品質なものです。

lcg_valueを維持するため内部実装はそのままにしますが、クラスは廃止して外部からは使用できないようにします。

There is no equivalent of array_rand()

array_randに相当する機能がRandomizerには存在しません。
かつてのRFCによると、これは意図的なものでした。
しかし、調査したところではarray_randは多くのライブラリで使用されていたため、Randomizerでも使用可能にするべきでしょう。

そこで、Randomizer::pickArrayKeys(array $array, int $num): arrayメソッドを追加します。
一見array_rand(array $array, int $num = 1): int|string|arrayと互換性が無いように見えますが、以下のようにすると、同じ結果を得ることができます。

$array = ['foo', 'bar', 'baz'];

// これまで
mt_srand(1234, MT_RAND_PHP);
$single = array_rand($array); // 0
$multiple = array_rand($array, 2); // [1, 2]

// 今後
$engine = new Random\Engine\Mt19937(1234, MT_RAND_PHP);
$randomizer = new Random\Randomizer($engine);
$single = $randomizer->pickArrayKeys($array, 1)[0]; // 0
$multiple = $randomizer->pickArrayKeys($array, 2); // [1, 2]

"string" means a binary

PHPにおいてstringはバイナリを意味します。
これはマルチバイト文字列を扱う際に問題であり、たとえばstr_shuffleを日本語に使うとめちゃくちゃになります。

従って、str_shuffleに相当するメソッドRandomizer::shuffleString()Randomizer::shuffleBytes()に改名した方が、より適切な名称となるでしょう。

Engine classnames are not precise

エンジンのクラス名が曖昧であり、アルゴリズムを明確にするために改名する必要があります。
Random\Engine\PCG64Random\Engine\PcgOneseq128XslRr64
Random\Engine\MersenneTwisterRandom\Engine\Mt19937

PCG is not so famous

PCGは優れたアルゴリズムであり、素晴らしいランダム性とパフォーマンスを持っています。
しかしその知名度はXorshiftに比べるとずっと劣ります。

そのため、PCGがよくわからないからMT19937を使おうといった選択を避けるため、以前から候補として挙がっていたRandom\Engine\Xoshiro256StarStarを追加で実装します。

Proposal

各議題に対し、それぞれ投票を行います。

Engine implementations are not final

賛成18、反対1の賛成多数で可決されました。

Random\SerializableEngine is not useful

賛成18、反対0の賛成多数で可決されました。

Random\Engine\CombinedLCG is low quality

賛成18、反対0の賛成多数で可決されました。

There is no equivalent of array_rand()

賛成12、反対2の賛成多数で可決されました。

"string" means a binary

賛成16、反対1の賛成多数で可決されました。

Engine classnames are not precise

賛成14、反対1の賛成多数で可決されました。

PCG is not so famous

賛成13、反対1の賛成多数で可決されました。

Backward Incompatible Changes

以下のクラス名は、使用できなくなります。

Random\Engine\Mt19937
Random\Engine\PcgOneseq128XslRr64
Random\Engine\Xoshiro256StarStar

以下のクラス名は、使用可能になります。

Random\Engine\CombinedLCG
Random\Engine\MersenneTwister
Random\Engine\PCG64
Random\SerializableEngine

Proposed PHP Version

PHP8.2

RFC Impact

特になし。

Patches and Tests

References

感想

乱数について最初のRFCで足りなかったところを補足・変更するRFCとなります。

PHP internalはどうも提案の時点では無風なのに投票を開始した途端に活気づくという悪癖がありがちで、たとえばConstants in traitsを作った人もたいへんつらい的なことを言っていました。
(なんかもっと直接的な発言を見たような気がするのだが見つけられなかった)

ということで、Random Extensionについても投票開始後にfinalにしたほうがいいんじゃねとか色々指摘が入り、改善RFCの提案に至ったようです。

このRFCにもやっぱり投票開始後に多少の物言いが入りましたが、無事に全ての提案が通ったため、PHP8.2から既に使用することができます。
めでたし。

21
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
8