Pythonを用いたデータサイエンスや機械学習の現場において、乱数の生成はシミュレーション、モデルの重み初期化、データのサンプリングなど多岐にわたる用途で利用されます。
特に、0から1の範囲で均一に分布する乱数を生成する操作は、あらゆるアルゴリズムの基礎となる重要なステップです。
NumPyライブラリには、この乱数生成を効率的に行うための関数が多数用意されていますが、その中でも古くから親しまれているのがnumpy.random.randです。
しかし、近年のNumPyのアップデートにより、より柔軟で高速な新しい乱数生成の仕組み(Generator)が推奨されるようになっています。
この記事では、従来の方法であるrandom.randの使い方はもちろん、最新の推奨メソッドを用いた0から1の乱数生成手法についても詳しく解説します。
NumPyにおけるrandom.randの基本概要
NumPyのnumpy.random.randは、指定した形状の配列を作成し、その各要素を0以上1未満([0.0, 1.0))の範囲で一様分布する乱数で満たす関数です。
この関数は、引数として配列の「次元のサイズ」を直接受け取るという特徴を持っています。
例えば、5つの要素を持つ1次元配列が必要な場合は引数に「5」を、3行2列の行列が必要な場合は「3, 2」を渡します。
生成される値は数学的に「半開区間」と呼ばれ、0は含まれる可能性がありますが、1が含まれることはありません。
この特性は、確率計算やインデックスのランダム選択など、コンピュータサイエンスにおける多くの計算モデルと非常に相性が良いものです。
乱数範囲と一様分布の特性
一様分布とは、指定された範囲内のすべての値が、同じ確率で出現する分布のことです。
random.randで生成される乱数は、この一様分布に従っています。
理論上、生成回数を増やせば増やすほど、出現する値の平均値は0.5に近づいていきます。
また、生成される値は浮動小数点数(float64)であり、非常に高い精度を持っています。
精度が高いということは、シミュレーションにおいて微細な変化を表現できることを意味します。
random.randの実装方法とコード例
実際にNumPyを使用して、0から1の乱数を生成する具体的なプログラムコードを見ていきましょう。
まずは、NumPyをインポートするところから始まります。
import numpy as np
# 1次元の乱数配列を生成(5要素)
rand_array_1d = np.random.rand(5)
print("1次元配列:")
print(rand_array_1d)
1次元配列:
[0.42310123 0.98321054 0.12384756 0.55678901 0.74123567]
このように、引数に数値を1つ渡すだけで、簡単に0から1の間の数値を取得できます。
多次元配列(行列)の生成
random.randの便利な点は、複数の引数を渡すことで、多次元配列を直感的に生成できることです。
タプルとして形状を渡す必要はなく、カンマ区切りでサイズを指定します。
# 2行3列の2次元配列を生成
rand_matrix = np.random.rand(2, 3)
print("2次元配列(2x3):")
print(rand_matrix)
2次元配列(2x3):
[[0.12034567 0.88456712 0.45321098]
[0.22345678 0.66789012 0.33456789]]
3次元以上の配列も同様に、np.random.rand(2, 3, 4)のように引数を追加することで生成可能です。
ただし、引数が多くなると可読性が下がるため、後述する最新のメソッドでは異なるアプローチが取られています。
最新の推奨メソッド:numpy.random.Generator
現在のNumPy(バージョン1.17以降、および2026年現在の標準)では、従来のnp.random.randよりも、「Generator」クラスを使用する方法が強く推奨されています。
これは、従来のランダム関数がグローバルな状態(シード値など)を共有していたために、並列計算や大規模なシステムでの再現性管理に課題があったためです。
新しい推奨メソッドでは、np.random.default_rng()を使用してジェネレータオブジェクトを作成します。
なぜ古いrandom.randではなくGeneratorを使うのか
新しいAPIは、ビット生成エンジン(BitGenerator)と分布生成器(Generator)が分離されています。
これにより、より高速なアルゴリズム(PCG64など)を利用できるほか、統計的な質も向上しています。
また、従来のrandは形状を個別の引数として受け取る特殊な仕様でしたが、新しいrandomメソッドは標準的なsize引数(タプル)を受け取るため、他のNumPy関数と一貫性があります。
将来的なコードの保守性を考えるなら、必ず新しいGeneratorを使用するようにしましょう。
default_rngを用いた乱数生成の手順
最新の書き方は以下のようになります。
# 最新のジェネレータを作成
rng = np.random.default_rng()
# 0から1の乱数を生成(randomメソッドを使用)
# 引数にはshapeをタプルで渡すのが一般的
modern_rand = rng.random(size=(2, 3))
print("Generatorによる乱数生成:")
print(modern_rand)
Generatorによる乱数生成:
[[0.65432109 0.12345678 0.98765432]
[0.45678901 0.23456789 0.76543210]]
このrng.random()メソッドも、デフォルトで0.0以上1.0未満の値を返します。
従来のnp.random.rand()と機能的には同等ですが、内部処理の信頼性が大幅に高まっています。
他の乱数生成関数との使い分け
NumPyには他にも多くの乱数生成関数が存在します。
目的が「0から1の範囲」であっても、分布の種類や引数の渡し方によって使い分ける必要があります。
| 関数・メソッド | 主な特徴 | 推奨度 |
|---|---|---|
np.random.rand() | 引数に個別の次元サイズを渡す。一様分布。 | 低(レガシー) |
np.random.random() | 引数にタプルで形状を渡す。一様分布。 | 中(レガシー) |
rng.random() | 最新のGeneratorメソッド。一様分布。 | 高(推奨) |
rng.uniform() | 範囲(low, high)を任意に指定可能。 | 高 |
rng.standard_normal() | 平均0、標準偏差1の正規分布(0〜1以外も出る)。 | 高 |
uniform関数による範囲指定
もし0から1だけでなく、例えば「0.5から1.5」のような特定の範囲の乱数が欲しい場合は、uniform関数が便利です。
rng.uniform(low=0.0, high=1.0, size=(3, 3))と記述すれば、rng.random()と同じ結果が得られます。
このように、範囲を明示的に指定したい場合はuniformを選択してください。
乱数の再現性を確保するシード値の設定
プログラミングにおいて、デバッグや実験の比較を行う際には、実行するたびに結果が変わると困る場合があります。
このような時には「乱数シード(Seed)」を設定することで、生成される乱数のパターンを固定できます。
従来のnp.random.seed(42)という書き方は、プログラム全体の乱数設定を書き換えてしまうため、現在は推奨されません。
新しいAPIでのシード管理
新しいGeneratorでは、default_rngの引数にシード値を直接渡します。
# シード値を固定してジェネレータを初期化
rng = np.random.default_rng(seed=42)
# 1回目の生成
print("1回目:", rng.random(3))
# 同じシードで別のジェネレータを作成
rng_repro = np.random.default_rng(seed=42)
print("再現:", rng_repro.random(3))
1回目: [0.77395605 0.43887844 0.85859792]
再現: [0.77395605 0.43887844 0.85859792]
このようにシード値を指定することで、何度実行しても同じ乱数列が得られるようになります。
これは、論文の実装やチーム内でのデータ共有において極めて重要なプラクティスです。
0から1の乱数を利用する具体的な用途
なぜ「0から1」という範囲がこれほど頻繁に使われるのでしょうか。
その理由は、この範囲の値が「確率」や「割合」として直感的に扱いやすいためです。
機械学習の重み初期化
ニューラルネットワークの学習を開始する際、ネットワーク内の各結合重みを0にしてしまうと学習が進みません。
そのため、小さなランダムな値で初期化するのが一般的です。
rng.random()で生成した0から1の値を加工して(例えば-0.5〜0.5にスケーリングするなどして)、初期パラメータとして利用します。
モンテカルロ法によるシミュレーション
円周率の計算や複雑な確率分布の積分計算などに使われる「モンテカルロ法」でも、0から1の乱数は必須です。
例えば、1×1の正方形の中にランダムに点を打ち、原点からの距離が1以下の点の割合を調べることで円周率を近似できます。
このような大量の乱数を生成する処理において、NumPyの高速な配列演算は圧倒的なパフォーマンスを発揮します。
まとめ
NumPyを用いて0から1の乱数を生成する方法には、いくつかの選択肢があることが分かりました。
従来のnp.random.randはシンプルで使いやすい反面、多次元の指定方法が特殊であり、最新のシステム設計にはそぐわない面があります。
一方で、最新の推奨メソッドであるnp.random.default_rng().random()は、安全かつ高速で、現代のPythonプログラミングにおいて標準となっています。
基本的な概念である「0以上1未満の一様分布」を理解した上で、適切なメソッドを選択し、必要に応じてシード値を設定して再現性を確保しましょう。
これからNumPyを学ぶ、あるいは既存のコードをメンテナンスする場合は、ぜひ新しいGeneratorクラスを活用した実装を取り入れてみてください。
