1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pythonで真性乱数を使う。

1
Last updated at Posted at 2023-09-18

真性乱数を取り出すpythonのモジュールです。

コンピュータで使える乱数は、2種類あり、周期をもつ疑似乱数と、TRNGから得られる真性乱数があります。他に、「完全乱数」というものも存在し、確率的にしか扱うことは出来ません。つまり、完全乱数はイデア界にしか存在しません。

python3では、乱数の利用は主にrandomモジュールを使いますが、これは、疑似乱数を発生するもので、真の乱数ではありません。しかし、x86_64はRDSEEDで、環境ノイズからの真性乱数を得ることができるので、これを使います。(普通RDSEEDは疑似乱数の種に使いますが)
/dev/randomは乱数を1バイト単位で読み出しますが、真の真性乱数ではなく、「エントロピーで初期化されたCSPRNGの出力」で、一度エントロピーで初期化されれば、多数のソースからのエントロピーで定期的(最大毎秒10回)に再シードされない限り、決定論的ですので、ハードウェアエントロピー源からRDSEEDを使って、毎回新規取得します。

「真性乱数」は「完全乱数」と定義が違います。

・真性乱数とは、原理的に予測不能で真に無秩序な乱数。コンピューターによって算術的に生成する疑似乱数に対していう。放射性元素の崩壊など、量子的なふるまいを利用する物理乱数が知られる。真乱数。- 小学館 - デジタル大辞泉より。

・完全乱数とは、完全にでたらめな順番で発生する数値の列を指し、理想の乱数とも呼ばれます。確率的にしか扱うことができません。 - Search Labs | AIによる。

定義が違いますが、真性乱数を完全乱数と看做して、同様に扱って良いでしょう。

完全乱数はユングの共時性(因果律を超えた奇妙な偶然の一致)があるので、物理的に言うと、EPRパラドックスがEPR相関になったこともあり、実験によると、Quantum Brain DynamicsがQuantum Dynamicsに干渉し、環境ノイズがそれに左右されてしまうため、現実世界で作るのは困難もしくは不可能かもしれません。もしくは、完全に偶然の産物かも知れません。真性乱数が完全乱数か否かであるかは、yes/noの2つの答があります。

「数学的に完全な乱数(真の乱数)」を発生させる方法は、数学だけでは原理的に不可能とされ(数学アルゴリズムは決定論的なので)、まだ見つかっていません。しかし、ある日、天才が現れ、無限を包含させ、実現させるかもしれません。

メソッドの説明

rand(n)はn*8ビット正の整数の範囲の真性乱数を返します。

rand_f(n)は、実用的に、[0,1]のfloat型の乱数を返しますが、分布は実数のように連続ではありませんので、真性乱数ではなくなっています。

randomint(k,n)は、実用的に0~k-1の整数乱数を返しますが、剰余計算を行っているため、真性乱数ではなくなっています。

rdseed.c
/*
 * rdseed.c — CPU の RDSEED 命令でハードウェアエントロピーを取り出す。
 *   コンパイル(FreeBSD/clang, Linux/gcc 共通):
 *     cc -O2 -mrdseed -shared -fPIC -o librdseed.so rdseed.c
 *   RDSEED 対応 CPU が必要(Intel Broadwell 以降 / AMD Zen 以降)。
 */
#include <immintrin.h>
#include <stdint.h>

/* 成功で1、エントロピー未準備で0(=要リトライ)を返す。 */
int rdseed64(uint64_t *out){
    return _rdseed64_step((unsigned long long *)out);
}

コンパイル・ライブラリ化

cc -O2 -mrdseed -shared -fPIC -o librdseed.so rdseed.c
truerand.py
#!/usr/bin/env python3
#
# truerand.py
#   CPU の RDSEED 命令から直接ハードウェアエントロピーを引く真性乱数モジュール。
#   /dev/random(Fortuna)を経由せず、1バイトごとにエントロピー源から新規取得する。
#   要: RDSEED 対応 CPU と、同じディレクトリにコンパイル済みの librdseed.so。
#     cc -O2 -mrdseed -shared -fPIC -o librdseed.so rdseed.c
#
import os
import ctypes

# --- RDSEED スタブの読み込み ---------------------------------------------
_here = os.path.dirname(os.path.abspath(__file__))
_lib  = ctypes.CDLL(os.path.join(_here, "librdseed.so"))
_lib.rdseed64.argtypes = [ctypes.POINTER(ctypes.c_uint64)]
_lib.rdseed64.restype  = ctypes.c_int

_RDSEED_RETRY = 1000   # RDSEED は一時的に失敗しうるのでリトライ上限を設ける

def _rdseed64():
    """RDSEED で 64bit のハードウェアエントロピーを1個取得して返す。"""
    v = ctypes.c_uint64()
    for _ in range(_RDSEED_RETRY):
        if _lib.rdseed64(ctypes.byref(v)):
            return v.value
    raise RuntimeError("RDSEED が連続失敗しました(ハードウェアエントロピー枯渇?)")

# n byte の RAND_MAX を返す
def rand_max(n):
    v = 0
    for _ in range(n):
        v = v*256 + 0xff
    return v

# n byte の真性乱数(正の整数)を返す。1バイトごとに RDSEED から新規取得。
def rand(n):
    r = 0
    for _ in range(n):
        b = _rdseed64() & 0xff      # 一々エントロピー源から引く
        r = r*256 + b
    return r

# [0,1] の実数乱数(割り算のため厳密には真性乱数ではない)
def rand_f(n):
    return rand(n) / rand_max(n)

# 0〜k-1 の整数乱数(剰余のため厳密には真性乱数ではない)
def randomint(k, n):
    return rand(n) % k

if __name__ == "__main__":
    b = 8
    times = 20
    print(f"{b*8}bit正の真性整数乱数")
    for _ in range(times):
        print(rand(b), end=" ")
    print("\n実数[0,1]の範囲の実数乱数")
    for _ in range(times):
        print(rand_f(b), end=" ")
    print("\n整数0〜99の乱数")
    for _ in range(times):
        print(randomint(100, b), end=" ")
    print("")

実行結果

% truerand.py   
64bit正の真性整数乱数
7593683692756404560 1137002372603596603 15790170822563052133 15398564476323138863 5666431897510622232 14727313056768347926 1591286415691868933 13756589528406737812 14017529931204693033 6126976806754524406 9625760106799710579 10221137030372070039 5789631471258037124 15844850515166960795 16310560720210454887 584665298658331905 11747899575069696907 17623763738758574929 2856509978569915829 7845507258779463291 
実数[0,1]の範囲の実数乱数
0.9971038511422857 0.04623809284437578 0.25208883991654213 0.23197559278871627 0.21992506030041897 0.062286248874933114 0.972354488288027 0.9724443108477789 0.6121628351627036 0.9760755436977876 0.2878544810907126 0.6180388833588414 0.8617915343869584 0.38774012248377726 0.7124578572583314 0.20837745592535706 0.8668496736230473 0.5165770706713765 0.40389838880267426 0.5204816417932834 
整数0〜99の乱数
62 75 92 2 90 7 97 96 96 59 23 14 61 55 12 94 88 17 13 53 

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?