はじめまして、53代のソエキです。プログラミング研究会に所属しています。所属しているといっても大したことは出来ていません。弱々な私のことはどうでもいいですね。早速本題に入りましょう。
乱数について
皆さん乱数と聞いて何を思い浮かべますか。おそらくだいたいの人はサイコロなどを思い浮かべるでしょう(私がそう思ってるだけかも)。その乱数を大まかに分けると、乱数には疑似乱数と真の乱数の2つあります。
疑似乱数はゲームなどのソフトウェアに使われています。そして、サイコロやルーレットなどが真の乱数と言われています。ソフトウェアだけでは真の乱数を作れないのは有名ですね。
そして、乱数には3つの性質があります。それは、
- 無作為性
- 予測不可能性
- 再現不可能性
です。そして、これらの関係は、
無作為性 ⊂ 予測不可能性 ⊂ 再現不可能性
といった感じになります。つまり、再現不可能性を持った乱数列が最強ということです。ちなみに、再現不可能性を持ってないと真の乱数は名乗れません。ゲームに使われているような乱数は無作為性しか持っていません。また、予測不可能性を持っていると、暗号技術に使うことができます。
線形合同法
タイトルにもある通り、この後乱数を予測します。その前に少し説明させてください。今回使うのはJavaのjava.util.Randomです。このクラスの乱数は線形合同法というアルゴリズムで乱数を生成しています。このクラスの中身はここに載っています。
乱数列をRとし、n番目の乱数をRnとすると、線形合同法で作られた乱数列は、
Rn = ( A × Rn-1 + C ) mod M ※(A,C,Mは定数)
となります。このアルゴリズムは一個前の結果を元に次の値を作ります。じゃあ一番最初はどうするのか気になりますよね。一番最初の値はシード(種)から計算されます。シードはメモリの状況やその時の時間を元につくられます。時間を元に乱数が作られるという話はこういうことです。
賢い人は気付いたかもしれませんが、このアルゴリズムは無作為性しか持っていません。つまり、このアルゴリズムで作られた乱数列は予測も再現もすることができるのです。
実際にやってみる
乱数について簡単に説明したので、それを実際に可能なことをお見せしましょう。
java.util.Randomを使って乱数を作るので、乱数を表示するコードはJavaで書きました。中身はこんな感じです。
import java.util.Random; import java.util.Scanner; public class JavaRandom { public static void main(String[] args) { Random random = new Random(); Scanner scanner = new Scanner(System.in); System.out.println(random.nextInt()); System.out.println(random.nextInt()); scanner.next(); System.out.println(random.nextInt()); } }
このコードについて一言で説明すると、乱数を2つ表示し、その後なにか文字を入力すると3つ目の乱数が表示されます。乱数の範囲はJavaのInt(32bit)の範囲内です。
そして、乱数を予測する側のコードはGolangで書きました。特に理由はありません。JVM言語以外にしただけです。
まず、最初の乱数を2つ表示させます。
最初の2つは
- -1154752450
- 85546660
でした。
これを予測する方のプログラムに入力します。
入力し、計算した結果は、このようになります。
画像の"next: "にある数字が次に出力される乱数の予測です。予測値は 1085405238 でした。
適当に文字を入力し、3つ目の乱数を表示します。
結果は 1085405238 で、予測と一致しましたね。
ということで実際に乱数を予測することが出来ました。
終わり
こんな記事を読んでプログラミングに少しでも興味を持ってくれたら幸いです。あと、一応フォローしておくとJavaの乱数が弱いわけではないです。java.security.SecureRandomという暗号用の乱数のクラスが存在します。こっちは予測不可能性を持っています。機会があったら使ってみてください。。
明日は52代まつりさんのイラストのお話です。お楽しみに。