Twitterに投稿 はてなブックマークに追加 Google Bookmarksに追加

目次 >> C、C++ >> Tips

ハードウェア乱数生成器による乱数

ハードウェア乱数生成器による乱数を得るには、
/dev/urandom
もしくは
/dev/random
を使う。/dev/urandomはハードウェア乱数生成器の乱数生成が間に合わない場合、疑似乱数を使うので/dev/randomの方が、より真の乱数に近いが、乱数生成が完了するまでプログラムがストップする。その為、/dev/urandomの方が使い勝手は良い。
なお、Ivy Bridge以降のCPUではCPUに独自のハードウェア乱数生成器が搭載されており、/dev/randomでも高速に乱数を生成することができる。

乱数の読み込みに必要な関数はopen()、read()、close()であり、ヘッダーはfcntl.hおよびunistd.hが必要となる。
/dev/randomおよび/dev/urandomから配列に読み込むことによって、一気に複数の乱数を得ることもできる。

実際のCコードであるが、まず下記のコードを実行してみる。

#include <iostream>

#include <fcntl.h>
#include <unistd.h>

using namespace std;
int main(void) {
  int fid=open("/dev/random", O_RDONLY);
  if (fid == -1) {
    cerr<<("can't open");
    return fid;
  }

  long long int r[100];
  for (int i=0;i<100;i++)r[i]=0;

  int ret=read(fid, &r, sizeof (r));
  if (ret < 0) {
    cerr<<("can't read");
    return ret;
  }
  cout<<"ret="<<ret<<" ret/size()= "<<ret/sizeof(long long int)<<endl;

  for (int i=0;i<100;i++)
    cout<<i<<" "<<r[i]<<endl;

  if (close(fid))cerr<<"can't close";

  return 0;
}

すると、

ret=84 ret/size()= 10
0 -6718051102082338824
1 5097410379476334973
2 -7809465558258835484
3 -7506695009096760579
4 1043311878644120306
5 2570282861251990761
6 9005240483906281924
7 -934377726622283194
8 8238844123701322014
9 8269152880138797499
10 3534958681
11 0
12 0
13 0
14 0
15 0
16 0
17 0
18 0

のような結果が得られるはずである。この場合、84ビット分の乱数が得られ、long long intは8ビット使うので、10個分のみ正しく、最後の11個目は4ビット分しか埋まっていない不正な乱数である。
この場合、最初の10個だけを使えばよいが、

ret=6 ret/size()= 0
0 19155406562275

このような結果になった場合、最初の乱数も不正な乱数である。そのため、

配列を使わない通常の変数に乱数を読み込む場合、単純に

#include <iostream>

#include <fcntl.h>
#include <unistd.h>

using namespace std;
int main(void) {
  int fid=open("/dev/random", O_RDONLY);
  if (fid == -1) {
    cerr<<("can't open");
    return fid;
  }

  long long int r=0;

  int ret=read(fid, &r, sizeof (r));
  if (ret < 0) {
    cerr<<("can't read");
    return ret;
  }
  cout<<"ret="<<ret<<" ret/size()= "<<ret/sizeof(long long int)<<endl;

  cout<<r<<endl;

  if (close(fid))cerr<<"can't close";

  return 0;
}

とすると、

ret=6 ret/size()= 0
204688894798701

となって、正しい乱数を得られない。たとえ6バイトでも乱数であるので、一見乱数を次々と生成しているように見えるが、平均値などをとると、乱数としては不適格な数値を出力している。

下記のようにコードを書いてやると、正しい乱数を得ることができる。

getrandomを使ってもハードウェア乱数を得ることができる。

必要なヘッダーは#include <linux/random.h>である。


最終更新日


本文中のFC4はFedora ProjectのFedora Core 4を、FC5はFedora Core 5を、FC6はFedora Core 6をopenSUSEはNovellのSUSE Linux OSSを表します。Fedora7以降は、単にFedora7、Fedora8、Fedora9、Fedora10、Fedora11、Fedora12、Fedora13、Fedora14、Fedora15と表示しています。Ubuntuは、必要に応じて20.04、21.04のようにバージョン番号をつけて区別しています。

ここに登場するドメイン名やIPアドレスなどはフィクションです。実在の人物・団体等とは一切関係がありません。
実際に使用する際は、各自の環境に合わせて書き換えてください。
もし何か間違いなどありましたらこちらからご連絡ください
リンクに許可は不要です。
Copyright (C) 2021 Chikuma Engineering Co., Ltd. All Rights Reserved.