web-dev-qa-db-ja.com

コンパイル時にC ++で乱数を生成する

コンパイル時にC++ 11のrandomライブラリを使用してランダムな値を事前計算しようとしています。私は主に例に従っています。私はここで何が間違っているのですか?

using namespace std;
#include <iostream>
#include <vector>
#include <random>

vector<double> rands;
typedef std::mt19937_64 RNG;
uint64_t seed_val;
RNG rng; 

void initialize() {
     rng.seed(seed_val);
}

constexpr vector<double> generate_random( )                 //size_t numbers)
{   
    int numbers = 1000;
    std::uniform_real_distribution<double> zero_one(0.0, 1.0);
        for (unsigned int i = 0; i < numbers; i++) { 
             double Rand_num = zero_one(rng);
             rands.Push_back( Rand_num );
    }
    return rands;
}

int main()
{
    cout << "TMP rands";
    for_each( rands.begin(), rands.end(), [] (double value)
    {
        cout<<value<<endl;
    });
}

これは ここ から恥知らずに盗まれたコンパイル時の乱数ジェネレーターの例ですが、これを調べる人には役立つかもしれないと思いました:

template<u32 S, u32 A = 16807UL, u32 C = 0UL, u32 M = (1UL<<31)-1>
struct LinearGenerator {
    static const u32 state = ((u64)S * A + C) % M;
    static const u32 value = state;
    typedef LinearGenerator<state> next;
    struct Split { // Leapfrog
        typedef LinearGenerator< state, A*A, 0, M> Gen1;
        typedef LinearGenerator<next::state, A*A, 0, M> Gen2;
    };
};
25
pyCthon

コンパイル時に評価できるのは、constexpr関数と定数式のみです。これにより、<chrono><random>が除外されます。

できることは、__TIME__プリプロセッサマクロにアクセスして、1行のconstexpr関数で構成される独自のPRNG 1)を定義することです。

24
Potatoswatter

このトピックに関する研究論文があります: C++テンプレートメタプログラム用の乱数ジェネレーター 含む コードスニペット__TIME__ 騙す。また、直交する選択肢として、さまざまな乱数エンジンと分布をサポートすることについても説明します。

5
jmihalicza

system_clock::now()はコンパイル時に認識できないだけでなく、関数はブール値を返すものとしてラベル付けされていますが、どこにもreturnステートメントがありません。

4
Cicada

この質問は5年前のものであり、すでに受け入れられている回答があります。それでも、同じシーケンスが得られることを理解した上で、コンパイル時に乱数を生成することは確かに可能であると付け加えたいと思います。プログラムを実行するたびに乱数が発生します。簡単に言うと、コンパイル時にシードがわかっている場合、コンパイラは許可されて、出力される乱数を把握し、 「この一連の数字を出力する」にプログラムします。

コンパイラーは、最適化の積極性に制限があるため、常にこの置換を行うことを約束することはできません。また、どのコンパイラーも、メルセンヌツイスターのような複雑なものの置換を行うことができるとは思えませんが、linear_congruential_engineのような単純なものです。チャンスがあります(また、それが起こったことを確認する唯一の方法は、コンパイラにアセンブリコードを出力させてから、アセンブリコードを確認することです)。

MarsagliaのXorshiftアルゴリズム を使用したrandom_deviceをモデルにしたランダムジェネレーターを実装したため、これが可能であることを私は知っています。 Marsagliaの論文には実際には複数の関連するアルゴリズムが含まれていたため、使用するシフトパターンを選択するためにクラスにテンプレートパラメーターを取得させました。コンパイラが使用したswitchステートメントを最適化するかどうかを知りたいと思いました。シードを渡すのを忘れたため、コンパイラはデフォルトを使用しました。つまり、シードはコンパイル時に認識されていました。アセンブリコードを見ると、switchがなくなっただけでなく、GCCはプログラムを「これらの3つの数値を出力する」ように最適化しました。

質問にリストされているプログラムの最終バージョンは、実際に数値のシーケンスを生成する関数を呼び出すことはなく、ジェネレーターをシードする関数を呼び出すこともありませんでした。このバージョンはそれを行いますが、「この乱数のシーケンスを印刷する」に変わるとは思えません。

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <random>

int get_seed()
{
    int hour = std::atoi(__TIME__);
    int min = std::atoi(__TIME__ + 3);
    int sec = std::atoi(__TIME__ + 6);
    return 10000 * hour + 100 * min + sec;
}

int main()
{
    // get_seed() returns an int based on __TIME__ (a string literal
    // set by the preprocessor), which is known at compile time.
    //
    // Also, w/r/t the engines in <random>: not setting a seed explicitly
    // will use a default seed, which is known at compile time.  So if
    // you're OK getting the same sequence of numbers for any compilation,
    // then "std::mt19937_64 rng;" may be all you need.
    std::mt19937_64 rng(get_seed());
    std::uniform_real_distribution<double> zero_one(0.0, 1.0);
    const int COUNT = 1000;
    std::generate_n(std::ostream_iterator<double>(std::cout, "\n"), COUNT,
        [&rng, &zero_one]() { return zero_one(rng); });
    return 0;
}
4
Max Lybbert

外部ソースからプルしてみます。非常に簡単な例は、compileコマンドで定義されたマクロ変数を使用してプログラムをコンパイルすることです。ここに $RANDOMは、ランダムな16ビット数を自動的に返すunix/linuxシステムの特別な組み込み変数です。

g++ -D__RANDOM__=$RANDOM yourprog.cpp -o yourprog

//yourprog.cpp
#include <iostream>
int main() {
  std::cout << "Random variable " << __RANDOM__ << std::endl;
  return 0;
}

マクロ変数に割り当てる独自のスクリプトまたは実行可能ファイルを作成することもできます。

//DevRandomGenerator.cpp
#include <iostream>
#include <fstream>

class DevRandom {
private:
    std::ifstream stream;
public:

    DevRandom() {
        stream.open("/dev/urandom",std::ios::in|std::ios::binary);
    }

    unsigned int unsignedInt() {
        unsigned int u = 0;
        stream.read((char*)&u, sizeof(unsigned int));
        return u;
    }
};

int main() {
  DevRandom Rand;
  std::cout << Rand.unsignedInt() << std::endl;
  return 0;
}

次に、次のようにコンパイルします。

g++ DevRandomGenerator.cpp -o DevRandomGenerator
g++ -D__RANDOM__="$(./DevRandomGenerator)" yourprog.cpp -o yourprog

より良いランダムジェネレータは、オーディオおよびビジュアル入力を使用するプログラムを作成することです。

4
user2074102

エラーメッセージによると:

 cpp11tmprands.cpp:22:15:エラー:「rands」はこのスコープで宣言されていません

変数randsmainのスコープで宣言されていません。 generate_randomでローカルではなくグローバル変数にすると、そのエラーはなくなります。