コンピュータプログラミングに興味のない人にとっても、ある状況下で乱数を発生させることの有用性は明らかなことである。
ほとんどのボードゲームでは、サイコロを投げて、プレイヤーの次の行動を決定する予測不可能な数字を生成する。
また、どんなカードゲームでも、ラウンドの間に適切なシャッフルを行わなければ意味がないことは誰もが認めるところです。
しかし、乱数が重要なのは、娯楽やギャンブルのような比較的些細な分野だけではありません。
特に暗号の分野では極めて重要である。
データを安全に伝送するためには、安全な接続が必要となるたびに、乱数の鍵を生成しなければならない。
さまざまな電子通信で、この種のセキュリティが利用されている。
鍵は推測されにくいことが重要で、そのためには鍵をランダムにするのが一番です。
誰かが鍵を推測した瞬間にメッセージを解読されてしまい、通信が安全でなくなってしまうからです。
真のランダムネスと疑似ランダムネスの比較
乱数は乱数発生器(RNG)と呼ばれる手法を適用した結果として得ることができ、真の乱数発生器(TRNG:ハードウェア乱数発生器とも呼ばれる)と疑似乱数発生器(PRNGS)に分類される。
真の乱数発生器
真の乱数生成器は、物理プロセスの予測不可能な側面からランダム性、または予測不可能性を導き出す方法です。
これらの方法は直接数字を生成するのではなく、数字として解釈できる状態を生成するため、通常ランダムイベントジェネレータ(REG)と呼ばれています。
マクロな事象を利用するものでは、サイコロを投げる、コインをはじく、カードをシャッフルするなどの方法が一般に知られている。
一方、真の乱数発生器は、より複雑な物理現象を利用することが多い。
放射性崩壊、熱雑音、電波雑音など、量子力学の特異性から予測不可能性を導き出すものもある。
また、大気中のノイズや溶岩の挙動を利用したものもある。
疑似乱数発生器
実は、本当にランダムな数字を生成する必要がない場合が非常に多いのです。
多くの場合、ランダムに見える数字の集合が必要なのだ。
このようなデータは、擬似乱数生成器から得ることができます。
これは、ごく一部の情報(シードと呼ばれる)を使って、複雑な数式を適用することで、本当にランダムな集合に似た決定論的な数字の集合を生成するアルゴリズムである。
シードには、真の乱数生成器から得られる値のほか、システムの時計や現在の時刻のような別の情報源が使われることがあります。
同じシードを使用してジェネレーターを複数回実行すると、毎回同じ出力が得られます。
その結果得られる数値は、分布に隠れた規則性があるとはいえ、真の乱数発生器から得られた数値とはほとんど認識できない。
しかし、多くの用途では、このような決定論的な疑似乱数で十分である。
Python Random モジュール
Python は明らかに、乱数を扱うための非常に使いやすいツールキットを提供しています。
なぜか random
と呼ばれるモジュールは、擬似乱数生成器を実装しており、乱数が登場する多くの異なるプログラミングの問題を直接解決するためのメソッドを含んでいます。
これは、Pythonだけでなく、Microsoft Excel、MATLAB、R、PHPなど、他の多くのソフトウェアシステムでもデフォルトの擬似乱数生成器として使用されている、非常に人気のあるアルゴリズムです。
その重要な利点は、寛容なライセンス、多くの統計テストによるランダム性の確認、他のPRNGと比較しての比較的高い速度です。
乱数()メソッド
randomモジュールの最も重要なメソッドは
random()メソッドです。
他のほとんどの関数はこのメソッドに依存しています。
random() メソッドは、範囲 (0.0, 1.0) のランダムな浮動小数点数を生成します。
>>> import random
>>> random.random()
0.8474337369372327
seed() メソッド
擬似乱数生成のためのシードを設定しない場合、デフォルトのシードは現在のシステム時刻になります。
しかし、シードの正確な値を手動で設定することができます。
これは、特に将来的に擬似乱数の結果を再現したい場合に便利です。
このためには、random.seed()
メソッドを使用します。
>>> random.seed(5)
>>> random.random()
0.6229016948897019
>>> random.random()
0.7417869892607294
>>> random.random()
0.7951935655656966
>>> random.seed(5)
>>> random.random()
0.6229016948897019
random.seed()
メソッドは、 random
モジュールのすべてのメソッドに影響を与えます。
上の例では、シードを 5
に設定してから random.random()
メソッドを複数回実行しています。
注意すべき点は、ユーザー定義のシードは他の random
メソッドを最初に実行したときのみ使用され、それ以降のメソッドのシードは以前に生成された乱数値を使用して変更されることです。
これにより、Pythonは毎回新しい数字を思いつくことができます。
しかし、それでも random.seed()
メソッドで種を設定し直せば、いつでも全く同じ疑似乱数列を再現することができます。
これはテストの実行などに非常に便利です。
もし、 random
のメソッドを使用するテストを実行するたびに同じシードを与えれば、そのテストの出力がどうなるかを知ることができます。
randomモジュールの他の使用例
random.random()メソッドは、ある範囲からランダムに float 値を与えるもので、経験の浅い Python 開発者がこのメソッドを使ってどんな種類のランダムな操作でも設計するには十分でしょう。
リストからランダムに値を取得する関数や、floatの代わりにランダムな整数を返す関数を書くために、ifを1つか2つ入れることは想像できるのではないでしょうか。
しかし、randomモジュールを使用すると、そのようなタスクも自動的に処理することができます。
以下に、一般的なランダム化操作を簡略化するクールなメソッドをいくつか紹介します。
Python の公式ドキュメントでrandom` モジュールのすべての可能性を知ることができます。
>>> random.randint(1,10)
4
random.randint()` メソッドは 2 つの引数を取り、そこからランダムな整数を引き出します。
>>> random.randrange(2,10,2)
2
>>> random.randrange(2,10,2)
4
>>> random.randrange(2,10,2)
8
>>> random.randrange(2,10,2)
6
上のスクリプトでは、 random.randrange()
メソッドは random.randint()
と似ていますが、第3引数を定義することができ、それは定義した範囲のステップポイントになります。
上の例では、2 から 10 までの範囲から偶数のみを要求しています。
>>> cards = ['ace_spades','10_hearts','3_diamonds','king_hearts']
>>> random.choice(cards)
'10_hearts'
上のスクリプトでは、random.choice()
メソッドがリストからランダムに要素を選んでいます。
>>> cards = ['ace_spades','10_hearts','3_diamonds','king_hearts']
>>> random.shuffle(cards)
>>> print(cards)
['king_hearts', '3_diamonds', 'ace_spades', '10_hearts']
前のスクリプトでは、random.shuffle()
メソッドが要素のリストをシャッフルしています。
つまり、None
を返し、実際には cards
変数を変更します。
結論
暗号のようなシリアスなアプリケーションにおいて、良い乱数を得ることは簡単なことではありません。
しかし、確かな擬似乱数が我々の用途に十分な場合、いつものようにPythonは我々の目標に素早く到達するための超簡単な方法を数多く備えているのです。