kozyonikkiのブログ

どこか知の旅に出たときのことを書いていきます。

物理乱数サイコロを作った話

↓↓ 参考にさせてもらったもの ↓↓
github.com
www.zenken.co.jp

今回は物理現象によって作られる乱数を作ってみました。トランジスタを逆接続してなだれ降伏という予測できない雑音を生成しています。

乱数の品質を高めるために雑音発生器を2つ作り、2ビット取得します。その2ビットの値が同じであればその結果を捨てて、違うビットになるまで取得します。

例として...

雑音1雑音2処理
結果を捨てて、もう一度取得する
違うビットなので、雑音2のビット(1)を結果とする  
違うビットなので、雑音2のビット(0)を結果とする
結果を捨てて、もう一度取得する


pdf と違うところ
LCDに表示ではなく7セグメント表示にすることにしました。
2n3904というトランジスタが手元になかったので2SC1815を使っています。

Arduinoのランダム関数と今回作った物理関数を比べてみるとどうだったかというと...

f:id:kozyonikki:20190516193719p:plain
↑ ランダム関数のやつ ↑

f:id:kozyonikki:20190516194305p:plain
↑ 物理乱数のやつ ↑

出やすい数字が固まってしまいました。
今回pdfとは違うトランジスタを使っているため出やすい数字が固まってしまったのかもしれません。

↓↓ 完成品の様子 ↓↓
f:id:kozyonikki:20190516204435j:plain

↓↓ コード ↓↓

/*3ビットの物理乱数を得る
   条件
   ・0,0 → 同じビットが連続しているので捨てて再度2ビット取得する
   ・0,1 → 違うビット同士なので1つ目の「0」を結果として扱う 
   ・1,0 → 違うビット同士なので1つ目の「1」を結果として扱う
   ・1,1 → 同じビットが連続しているので捨てて再度2ビット取得する

   追加で頑張ること
   ・スイッチを押したら乱数を7セグに表示する。
   ・一秒経たないでで押されたら7セグを消す
   ・経ったあと押されたら数字を表示する

*/
#include <MsTimer2.h>

//物理乱数生成の変数
const byte sbit = 13;
const byte rbit = 12;
const int sw = 2;
const int sw1 = 3;

volatile int point = 7;

//sw のチャタリングに関してのプログラム
boolean sw_stable = false;
boolean sw_flag = false;
int sw_stable_count = 0;
const int sw_count_threshold = 1;

boolean sw_stable1 = false;
boolean sw_flag1 = false;
int sw_stable_count1 = 0;
const int sw_count_threshold1 = 1;

byte m = B00000000;
byte a = B00000000;
byte b = B00000000;

void setup() {
  pinMode(sbit, INPUT);
  pinMode(rbit, INPUT);
  pinMode(sw, INPUT);

  //7セグメントで使う変数
  for (int i = 5; i < 12; i++) {
    pinMode(i, OUTPUT);
  }
  Serial.begin(9600);

  MsTimer2::set(20, Timer);
  MsTimer2::start();
}

//ランダムな数値を返す
int PhysicsRnd() {
  m = B00000000;
  for (int i = 1; i < 4;) {

    a = digitalRead(sbit);
    b = digitalRead(rbit);

    if (a == b) {
      //やり直し
    }
    else {
      m <<= 1;
      m |= a;
      i++;
    }
  }
  if (m != 0 && m != 7) {
    return m;
  }
}

//LEDレイアウトを定義
boolean Num_Array[10][7] = {
  {0, 0, 0, 0, 0, 0, 1}, //0
  {1, 1, 0, 0, 1, 1, 1}, //1
  {0, 0, 1, 0, 0, 1, 0}, //2
  {1, 0, 0, 0, 0, 1, 0}, //3
  {1, 1, 0, 0, 1, 0, 0}, //4
  {1, 0, 0, 1, 0, 0, 0}, //5
  {0, 0, 0, 1, 0, 0, 0}, //6
  {1, 1, 0, 0, 0, 0, 1}, //7
  {0, 0, 0, 0, 0, 0, 0}, //8
  {1, 1, 0, 0, 0, 0, 0}  //9
};

//LED表示関数を定義
void NumPrint(int Number) {
  for (int w = 0; w <= 7; w++) {
    digitalWrite(w + 5, -Num_Array[Number][w]);
  }
}

//全てのLEDを非表示にする
void off7SegLED() {
  //for文で2番ピンから8番ピンまでをOFFにする
  for (int i = 5; i < 12; i++) {
    digitalWrite(i, HIGH);
  }
}

void Timer() {
  boolean sw_new = !digitalRead(sw);
  if (sw_stable != sw_new)
  {
    sw_stable_count++;

    if (sw_stable_count == sw_count_threshold) {
      sw_stable = sw_new;
      sw_flag = true;

      sw_stable_count = 0;
    }
  } else {
    sw_stable_count = 0;
  }
}

void Timer1() {
  boolean sw_new1 = !digitalRead(sw1);
  if (sw_stable1 != sw_new1)
  {
    sw_stable_count1++;

    if (sw_stable_count1 == sw_count_threshold1) {
      sw_stable1 = sw_new1;
      sw_flag1 = true;

      sw_stable_count1 = 0;
    }
  } else {
    sw_stable_count1 = 0;
  }
}

void loop() {
  int rnd = PhysicsRnd();
  int rnd1 = random(1,7);
  int a;
  Serial.println(digitalRead(sw));
  
  if (sw_flag && sw_stable) {
    sw_flag = false;
    // swを押したときにしたい処理
    a = rnd;
    NumPrint(a);

    if (sw_flag && !sw_stable)
    {
      sw_flag = false;
      // swを離したときにしたい処理
      NumPrint(a);
      off7SegLED();
    }
  }
  else  if (digitalRead(sw1) == 0) {
    sw_flag1 = false;
    // swを押したときにしたい処理
    a = rnd1;
    NumPrint(a);

    if (digitalRead(sw1) == 1)
    {
      sw_flag1 = false;
      // swを離したときにしたい処理
      NumPrint(a);
      off7SegLED();
    }
  }
}

感想
物理乱数で生成される値の確立が均等ではないのでこれは使えない感じではありますが、乱数の発生方法はいろんな形がありそれぞれメリット・デメリットがあることがわかりました。
物理現象を使った乱数はほかにないのか探してみたいなと思いました。