乱数の生成

Go:
乱数の生成

方法:

Goにおいて、乱数は擬似乱数の場合はmath/randパッケージを使って、暗号学的に安全な擬似乱数の場合はcrypto/randを使って生成されます。両方について探求しましょう。

擬似乱数のためのmath/randの使用

まず、ジェネレーターをシードするためにmath/randパッケージとtimeパッケージをインポートします。シードすることで、実行ごとに異なる数列を得ることができます。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())
	fmt.Println("A random number:", rand.Intn(100)) // 0から99の間の数を生成
}

サンプル出力:A random number: 42

暗号学的に安全な擬似乱数のためのcrypto/randの使用

よりセキュリティに敏感なアプリケーションのためには、予測が困難な乱数を生成し、暗号操作に適しているため、crypto/randパッケージが適しています。

package main

import (
	"crypto/rand"
	"fmt"
	"math/big"
)

func main() {
	n, _ := rand.Int(rand.Reader, big.NewInt(100))
	fmt.Println("A secure random number:", n)
}

サンプル出力:A secure random number: 81

深堀り

math/randcrypto/randパッケージの根本的な違いは、エントロピーの源とそれぞれの使用目的にあります。math/randは初期シードに基づいて擬似乱数を生成するため、その数列は決定論的であり、シードが知れてしまえば予測可能です。これは、シミュレーションやゲームのように、絶対的な予測不可能性ではなく高性能が重要視されるシナリオに適しています。

一方で、crypto/randは基盤となるオペレーティングシステムからランダム性を導出し、予測不可能性が重要である暗号化用途に適しています。しかし、これは性能や、生成される数値(整数の場合は*big.Int型など)を扱う複雑さを犠牲にしています。

歴史的に、コンピューターにおける乱数生成の概念は常に真の「ランダム性」の縁で踊ってきました。早期のシステムはランダム性を模倣した決定論的アルゴリズムに大きく依存していました。コンピューターが進化するにつれて、これらのアルゴリズムも、環境からのより洗練されたエントロピー源を取り入れ、進化してきました。

これらの進展にもかかわらず、コンピューター自体の決定論的な性質を考えると、計算における完璧なランダム性を追求することは本質的に矛盾しています。これが、crypto/randのようなソースからの暗号学的に安全な擬似ランダム数が、そのオーバーヘッドにもかかわらず、予測可能性が有害であるほとんどのアプリケーションにとってより良い選択肢である理由です。

要するに、Goの乱数生成に対する二つの独立したパッケージのアプローチは、性能とセキュリティの間の妥協を巧みに対処し、開発者がその特定のニーズに基づいて選択できるようにしています。