MENU

C++ 乱数

プログラミングにおいて、乱数の生成は非常に重要な機能の一つです。ゲーム開発、シミュレーション、暗号化など、様々な分野で乱数が活用されています。C++では、標準ライブラリを使用して簡単に乱数を生成することができます。本記事では、C++における乱数生成の方法について、初心者の方にも分かりやすく解説していきます。基本的な使い方から、より高度な技術まで、具体的なコード例を交えながら学んでいきましょう。

目次

C++での乱数生成の基本

C++11以降、乱数生成には主に<random>ヘッダーに定義されている機能を使用します。この方法は、古いrand()関数よりも高品質で、より柔軟な乱数生成が可能です。

基本的な乱数生成の流れは以下の通りです:

  1. 乱数生成器(エンジン)を選択する
  2. 分布を選択する
  3. 乱数を生成する

それでは、簡単な例から見ていきましょう。

#include <iostream>
#include <random>

int main() {
    // 乱数生成器を初期化
    std::random_device rd;  // ランダムシードを生成するためのデバイス
    std::mt19937 gen(rd()); // メルセンヌ・ツイスター法による乱数生成器

    // 1から6までの一様分布を定義
    std::uniform_int_distribution<> dis(1, 6);

    // 乱数を生成して表示
    for (int i = 0; i < 10; ++i) {
        std::cout << dis(gen) << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、1から6までの整数をランダムに10個生成しています。これはサイコロを10回振るシミュレーションと考えることができます。

乱数生成器(エンジン)について

C++には複数の乱数生成器が用意されていますが、最も一般的に使用されるのはstd::mt19937です。これはメルセンヌ・ツイスター法という高品質な乱数生成アルゴリズムを使用しています。

std::random_device rd;
std::mt19937 gen(rd());

ここでは、std::random_deviceを使用してシードを生成し、それをstd::mt19937の初期化に使用しています。これにより、プログラムを実行するたびに異なる乱数列が生成されます。

分布について

C++の<random>ヘッダーには、様々な確率分布が用意されています。主な分布には以下のようなものがあります:

  1. std::uniform_int_distribution: 整数の一様分布
  2. std::uniform_real_distribution: 実数の一様分布
  3. std::normal_distribution: 正規分布
  4. std::bernoulli_distribution: ベルヌーイ分布

それぞれの使用例を見ていきましょう。

整数の一様分布

std::uniform_int_distribution<> dice(1, 6);
int result = dice(gen);  // 1から6までの整数をランダムに生成

実数の一様分布

std::uniform_real_distribution<> unif(0.0, 1.0);
double result = unif(gen);  // 0.0以上1.0未満の実数をランダムに生成

正規分布

std::normal_distribution<> norm(0.0, 1.0);  // 平均0.0、標準偏差1.0の正規分布
double result = norm(gen);

ベルヌーイ分布

std::bernoulli_distribution bern(0.3);  // 確率0.3でtrueを返す
bool result = bern(gen);

実践的な使用例

例1: パスワード生成器

ランダムなパスワードを生成する簡単な関数を作成してみましょう。

#include <iostream>
#include <random>
#include <string>

std::string generatePassword(int length) {
    const std::string chars = 
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "!@#$%^&*()";

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, chars.size() - 1);

    std::string password;
    for (int i = 0; i < length; ++i) {
        password += chars[dis(gen)];
    }

    return password;
}

int main() {
    std::cout << "生成されたパスワード: " << generatePassword(12) << std::endl;
    return 0;
}

この関数は、指定された長さのランダムなパスワードを生成します。文字、数字、特殊文字をランダムに組み合わせています。

例2: モンテカルロ法による円周率の推定

乱数を使用して、モンテカルロ法で円周率を推定する例を見てみましょう。

#include <iostream>
#include <random>
#include <cmath>

double estimatePi(int numPoints) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dis(-1.0, 1.0);

    int insideCircle = 0;
    for (int i = 0; i < numPoints; ++i) {
        double x = dis(gen);
        double y = dis(gen);
        if (x*x + y*y <= 1.0) {
            ++insideCircle;
        }
    }

    return 4.0 * insideCircle / numPoints;
}

int main() {
    int numPoints = 1000000;
    double estimatedPi = estimatePi(numPoints);
    std::cout << "推定された円周率: " << estimatedPi << std::endl;
    std::cout << "実際の円周率: " << M_PI << std::endl;
    return 0;
}

この例では、単位円内にランダムな点を生成し、その割合から円周率を推定しています。点の数を増やすほど、推定値の精度が向上します。

乱数生成における注意点

  1. シードの重要性: 同じシードを使用すると、同じ乱数列が生成されます。これはデバッグには便利ですが、セキュリティが重要な場面では注意が必要です。
  2. 乱数生成器の再利用: パフォーマンスの観点から、乱数生成器(std::mt19937のインスタンス)は可能な限り再利用することが推奨されます。
  3. 分布オブジェクトの再利用: 同様に、分布オブジェクト(例:std::uniform_int_distribution)も再利用することでパフォーマンスが向上します。
  4. 暗号用途には不適切: <random>ライブラリの乱数生成器は、暗号用途には適していません。暗号学的に安全な乱数が必要な場合は、専用のライブラリを使用してください。

C++での乱数生成は、適切に使用することで非常に強力なツールとなります。基本的な使い方を理解し、様々な分布を活用することで、多くの問題を解決できるようになるでしょう。初心者の方は、まずは簡単な例から始めて、徐々に複雑な用途に挑戦していくことをお勧めします。

乱数生成のスキルは、シミュレーション、ゲーム開発、統計処理など、幅広い分野で活用できます。実際のプロジェクトでこれらの技術を適用することで、より深い理解と実践的なスキルを得ることができるでしょう。継続的な学習と実験を通じて、C++プログラミングの奥深さと可能性を探求してください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次