Python
2021.10.12
Pythonのrandomで乱数を作ってみよう! choice、sample、randintから応用編まで
2023.11.18

1.Pythonの乱数関数とは? どんな時に使う?


多くのプログラミング言語と同じく、Pythonにも乱数を発生させる仕組みが備わっています。この記事ではPythonで乱数を発生させるrandomモジュールについて解説します。

①乱数とは?

乱数とは、「でたらめな数」です。発生させるには関数を呼び出せばいいのですが、その度に違う数を返します。


でも、ちょっと待ってください。コンピュータでどうやってそんなことが可能なのでしょうか?実は、コンピュータが発生させる乱数は「疑似乱数」と言って、完全にでたらめではないのです。


ごくごく簡単なモデルで言うと、内部に数の表を持っていて、その表のどこから数を抜き出すかを「種(シード)」を元にして決定します。シードは現在時などで与えます。このおかげで、呼び出すたびに違う数が発生しているかのように見えるのです。

ですので、シードを同じにすれば発生する乱数は同じになります。これが疑似乱数と呼ばれるゆえんです。


Pythonではシードを与えることもできますが、一般的には気にする必要はありません。

②Pythonの乱数関数でできること

例えば、Webサイトに登録して、パスワードを登録するとき、「安全に生成されたパスワード」として、一見意味不明の文字列がサジェストで出てきたことはありませんか?この文字列は乱数で生成しています。


膨大なデータから一部を抽出するときにも、公平性の確保のために乱数でどこを抽出するか決めます。乱数は統計処理で活躍します。


抽選するときも乱数で当選番号を決めます。他には、ゲームの敵の攻撃パターンの制御などにも使われます。いつも同じパターンで攻撃してきたらすぐクリアできてしまいますからね。乱数はこのように、幅広く使われています。



2.Pythonの乱数関数の基本的な使い方


Pythonの乱数はrandomモジュールが担っています。それではrandomモジュールの使い方を見ていきましょう。

①randomモジュールのインポート

randomモジュールは標準モジュールですので、インストールする必要はありません。

使う前に

import random

と書いてインポートして使います。

以下のサンプルコードではrandomはインポートされているものとします。また、Pythonの対話シェルで行われているとします。

②random.randomの使い方とサンプル

random()は0.0以上1.0未満の乱数を生成します。

>>> random.random()
0.6440121215618787
>>> random.random()
0.08425067935459307
>>> random.random()
0.6071031668141786

呼び出すたびに違う数が返ってきているのが分かりますね。

③random.uniformの使い方とサンプル

uniform(a, b)はa以上b以下(またはb以上a以下)の乱数を返します。

>>> random.uniform(0, 3)
0.7177228215929227
>>> random.uniform(0, 3)
2.368018706429308
>>> random.uniform(100, 200)
117.5825447719314
>>> random.uniform(100, 200)
159.373895340749

bの値が含まれるかどうかは、Pythonのドキュメントによると


「端点の値 b が範囲に含まれるかどうかは、等式 a + (b-a) * random() における浮動小数点の丸めに依存します。」


ということです。「丸め」はまた科学的な言葉なのですが、ざっくり言うと、コンピュータ内部での小数の扱い(コンピュータは少数を本当に厳密には扱えません)により、bが少数であると含まれるか含まれないか分からない、ということです。「丸め」は2進数で行われるので、10進数だけをみて判断はできません。

④random.randintの使い方とサンプル

uniform(a, b)の返す値が少数なのに対して、randint(a, b)の返す値はa以上b以下の整数です。こちらはbは必ず含まれると決まっています。

>>> random.randint(0, 3)
3
>>> random.randint(0, 3)
0
>>> random.randint(100, 200)
121
>>> random.randint(100, 200)
160

⑤random.randrangeの使い方とサンプル

randrange(start, stop, step)は、start以上stop未満step刻みの乱数を返します。

randrange(a, b+1, 1)はrandint(a,b)と同じ意味です。

>>> random.randrange(0, 100, 3)
99
>>> random.randrange(0, 100, 3)
63

なぜrangeと書かれているのかというと、この関数の働きは、range(start, stop, step)から要素を一つ選ぶ動作と同一だからです。

>>> print(list(range(0, 100, 3)))
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]

例えば範囲内の奇数偶数の乱数を発生させたりするのに使えます。startを範囲の下限、stopを範囲の上限+1、stepを2とすれば良いのです。


例えば偶数なら

>>> random.randrange(0, 10, 2)
6

とすればいいですし、奇数なら

>>> random.randrange(1, 10, 2)
7

とすればいいでしょう。

⑥random.sampleの使い方とサンプル

ここで、リストやタプル、文字列から要素を抽出する方法について考えてみます。Pythonのrandomモジュールにはその関数が存在します。


まずはsample関数です。

第1引数に与えられたオブジェクトから第2引数の数の要素を重複なしで抽出します。

重複なしというのは、「要素が重複していない」という意味です。


文字列の場合は

>>> s = 'abcde'
>>> print(random.sample(s, 3))
['e', 'a', 'b']

と使います。

リストの場合は

>>> l = [0, 1, 2, 3, 4]
>>> print(random.sample(l, 3))
[0, 2, 4]

などと使います。

⑦random.choicesの使い方とサンプル

対して、choices関数は重複ありで抽出するものです。重複ありというのは、「要素が重複しているかもしれない」という意味です。

>>> s = 'abcde'
>>> print(random.choices(s, k=3))
['a', 'd', 'a']
>>> l = [0, 1, 2, 3, 4]
>>> print(random.choices(l, k=3))
[1, 3, 3]

ここで第2引数の与え方に注意してください。必ず

k=

と書く必要があります。


この書き方をする理由は難しいので割愛しますが、こういうものだ、と理解してください。

⑧random.shuffleの使い方とサンプル

shuffle関数は文字通り、リストの要素の順番をシャッフルするものです。

>>> l = [0, 1, 2, 3, 4]
>>> print(l)
[0, 1, 2, 3, 4]
>>> random.shuffle(l)
>>> print(l)
[1, 4, 0, 3, 2]

インデックスが入れ替わり、シャッフルされているのが分かりますね。


3.乱数を要素として持つリストの作り方


単純に考えると

>>> l = []
>>> for i in range(10):
...     l.append(random.random())
...
>>> print(l)
[0.495413535983358, 0.9503374192633068, 0.6822044384669844, 0.11258125943559649, 0.7858523314767191, 0.9010534144034589, 0.49929235923588344, 0.7470050323001457, 0.8500755283931358, 0.2350683252808461]

となりますが、「リスト内包表記」というものを使ってみましょう。


「リスト内包表記」についてはこちらの記事を参考にしてください。

>>> l = [random.random() for i in range(10)]
>>> print(l)
[0.8301167939328232, 0.15669427339409636, 0.8092278394894193, 0.7925997977264508, 0.7382651399146981, 0.8466224276339865, 0.44162795067405525, 0.19549788179448535, 0.02959215078185584, 0.50811032462553]

このサンプルコードはrandomを使っていますが、たとえば一桁の偶数を発生させるなら

>>> l = [random.randrange(0, 10, 2) for i in range(10)]
>>> print(l)
[0, 8, 8, 0, 8, 8, 6, 8, 4, 0]

とrandrangeを使えばいいのです。

でもこのリスト、値が重複していますね。重複するのを避けたかったらsampleとrangeを組み合わせて使います。

>>> l = random.sample(range(0, 10, 2), 5)
>>> print(l)
[8, 6, 2, 4, 0]

rangeでリストを作り、そこからsampleで抜き出すというわけです。このサンプルでは、一桁の偶数のリストを作り、そこから重複なしで5個抜き出しています。だから一桁の偶数が全て揃うのです(一桁の偶数は5個しかないので)。



4.ランダムな文字列の作り方


ここで応用編です。ランダムな文字列を作ってみましょう。

それにはstringパッケージを使います。

>>> import string
>>> string.ascii_letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

string.ascii_lettersはアルファベットの文字列という定数です。ここから文字を抜き出せて組み合わせればいいわけです。

>>> chrs = random.sample(string.ascii_letters, 10)
>>> s = ''.join(chrs)
>>> print(s)
qLuNSFCedf

chrsはリストになるので、str型のjoin関数を使って文字列に連結します。

サンプルで使った定数以外にも、stringモジュールには様々な定数の文字列が存在しています。

これでランダムなパスワードのサジェストが生成できますね!


5.Pythonで乱数を扱う際の注意点とコツ


この記事ではPythonの乱数についてみてきました。randomモジュールは必ずimportしてから使ってくださいね。あと書き方も少しクセがあるところがあるので、慣れるようにしてください。


実は、この記事で紹介した乱数は、乱数の中でも「一様分布に従う乱数」なのです。


「一様分布」とは数学の言葉で、「要素が出現する確率がどの要素も等しい」場合を言います。0から10の整数の乱数をrandintで作ったら、どの整数も出現する確率は同じです。


ところが、自然界の一見でたらめに見える現象は、この「一様分布」には従っていないことが分かっています。0から10の整数の乱数を自然界で発生させたら、端の0と10の出現確率は低くて、中央の5の出現確率が高いのです。


そのために、より自然界に近づけるために、数学的に様々な「分布」が考えられています。

Pythonのrandomモジュールでもサポートされていますが、難しいのでこの記事では割愛します。

普通に使う分には、この記事で紹介した関数を押さえておけば大丈夫です。


この記事をシェア