[解決済み] これは「十分な」ランダムアルゴリズムなのか、もっと速いのになぜ使われないのか?
質問
というクラスを作りました。
QuickRandom
その仕事は、乱数を素早く生成することです。それは本当に簡単で、古い値を取って、それに
double
で、小数点以下を取る。
ここで、私の
QuickRandom
クラスの全体像を示しています。
public class QuickRandom {
private double prevNum;
private double magicNumber;
public QuickRandom(double seed1, double seed2) {
if (seed1 >= 1 || seed1 < 0) throw new IllegalArgumentException("Seed 1 must be >= 0 and < 1, not " + seed1);
prevNum = seed1;
if (seed2 <= 1 || seed2 > 10) throw new IllegalArgumentException("Seed 2 must be > 1 and <= 10, not " + seed2);
magicNumber = seed2;
}
public QuickRandom() {
this(Math.random(), Math.random() * 10);
}
public double random() {
return prevNum = (prevNum*magicNumber)%1;
}
}
そして、それをテストするために書いたコードがこちらです。
public static void main(String[] args) {
QuickRandom qr = new QuickRandom();
/*for (int i = 0; i < 20; i ++) {
System.out.println(qr.random());
}*/
//Warm up
for (int i = 0; i < 10000000; i ++) {
Math.random();
qr.random();
System.nanoTime();
}
long oldTime;
oldTime = System.nanoTime();
for (int i = 0; i < 100000000; i ++) {
Math.random();
}
System.out.println(System.nanoTime() - oldTime);
oldTime = System.nanoTime();
for (int i = 0; i < 100000000; i ++) {
qr.random();
}
System.out.println(System.nanoTime() - oldTime);
}
これは非常に単純なアルゴリズムで、単純に前の2倍を"マジックナンバー"2倍で乗算します。かなり急いで作ったので、もっといいものができるかもしれませんが、不思議なことにうまくいっているようです。
のコメントアウトされた行の出力例です。
main
メソッドを使用します。
0.612201846732229
0.5823974655091941
0.31062451498865684
0.8324473610354004
0.5907187526770246
0.38650264675748947
0.5243464344127049
0.7812828761272188
0.12417247811074805
0.1322738256858378
0.20614642573072284
0.8797579436677381
0.022122999476108518
0.2017298328387873
0.8394849894162446
0.6548917685640614
0.971667953190428
0.8602096647696964
0.8438709031160894
0.694884972852229
ふむ。かなりランダムですね。実際、ゲームの乱数発生装置には使えそうですね。
以下は、コメントアウトしていない部分の出力例です。
5456313909
1427223941
すごい! と比べて約4倍高速に動作します。
Math.random
.
どこかで読んだ記憶があるのですが
Math.random
中古
System.nanoTime()
とか、とんでもないモジュレーションやディビジョンの数々。それは本当に必要なのでしょうか?私のアルゴリズムはかなり速く実行され、かなりランダムなようです。
2つほど質問があります。
- 私のアルゴリズムは十分に良いものでしょうか? 本当に 乱数はあまり重要ではない)?
-
なぜ
Math.random
単純な掛け算と小数の切り捨てで十分だと思うのですが、ここまでやるのですか?
解き方は?
あなたの
QuickRandom
の実装は、実際には一様な分布ではありません。一般に、頻度は低い値で高くなり、一方
Math.random()
はより均一な分布をしています。以下は
エスシーシーイー
ということを表しています。
package com.stackoverflow.q14491966;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception {
QuickRandom qr = new QuickRandom();
int[] frequencies = new int[10];
for (int i = 0; i < 100000; i++) {
frequencies[(int) (qr.random() * 10)]++;
}
printDistribution("QR", frequencies);
frequencies = new int[10];
for (int i = 0; i < 100000; i++) {
frequencies[(int) (Math.random() * 10)]++;
}
printDistribution("MR", frequencies);
}
public static void printDistribution(String name, int[] frequencies) {
System.out.printf("%n%s distribution |8000 |9000 |10000 |11000 |12000%n", name);
for (int i = 0; i < 10; i++) {
char[] bar = " ".toCharArray(); // 50 chars.
Arrays.fill(bar, 0, Math.max(0, Math.min(50, frequencies[i] / 100 - 80)), '#');
System.out.printf("0.%dxxx: %6d :%s%n", i, frequencies[i], new String(bar));
}
}
}
平均的な結果はこのようになります。
QR distribution |8000 |9000 |10000 |11000 |12000
0.0xxx: 11376 :#################################
0.1xxx: 11178 :###############################
0.2xxx: 11312 :#################################
0.3xxx: 10809 :############################
0.4xxx: 10242 :######################
0.5xxx: 8860 :########
0.6xxx: 9004 :##########
0.7xxx: 8987 :#########
0.8xxx: 9075 :##########
0.9xxx: 9157 :###########
MR distribution |8000 |9000 |10000 |11000 |12000
0.0xxx: 10097 :####################
0.1xxx: 9901 :###################
0.2xxx: 10018 :####################
0.3xxx: 9956 :###################
0.4xxx: 9974 :###################
0.5xxx: 10007 :####################
0.6xxx: 10136 :#####################
0.7xxx: 9937 :###################
0.8xxx: 10029 :####################
0.9xxx: 9945 :###################
テストを繰り返すと、QR分布は初期シードによって大きく変化するのに対して、MR分布は安定していることがわかります。時には望ましい一様分布になることもありますが、そうでないことの方が多いのです。以下は、より極端な例で、グラフの境界を越えていることさえあります。
QR distribution |8000 |9000 |10000 |11000 |12000
0.0xxx: 41788 :##################################################
0.1xxx: 17495 :##################################################
0.2xxx: 10285 :######################
0.3xxx: 7273 :
0.4xxx: 5643 :
0.5xxx: 4608 :
0.6xxx: 3907 :
0.7xxx: 3350 :
0.8xxx: 2999 :
0.9xxx: 2652 :
関連
-
エラー java.util.NoSuchElementException
-
スタイルが読み込まれず、ブラウザのコンソールでエラーが報告される。リソースはスタイルシートとして解釈されますが、MIMEタイプtext/htmlで転送されます。
-
Java のエラーです。未解決のコンパイル問題 解決方法
-
JDKの設定時にjava.dllが見つからない、java SE Runtime Environmentが見つからない問題が発生しました。
-
Eclipseプロンプトを実行する java仮想マシンを使用しない
-
テストが見つかりませんでした
-
ブラウザでの大容量ファイルスライスアップロード(Javaサーバサイド実装)
-
Maven Pluginの実行がライフサイクル設定の対象外であるエラーの解決
-
[解決済み] ランダムな文字列を使用するこのコードは、なぜ "hello world" と表示されるのですか?
-
[解決済み】連続したintに対するJavaのswitchは、ケースを追加することでより速く実行されるように見えるのはなぜですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
スタイルが読み込まれず、ブラウザコンソールでエラーが報告される。リソースはスタイルシートとして解釈されますが、MIMEタイプtext/htmlで転送されます。
-
springboot project MIMEタイプ text/htmlで転送された静的ファイルを読み込む。
-
Java Exceptionが発生しました エラー解決
-
JQuery DataTable 详解
-
自動配線された依存性のインジェクションに失敗しました。
-
linux run jarfile Invalid or corrupt jarfile error.
-
spring aop アドバイスからの Null 戻り値が、サマリーのプリミティブ戻り値と一致しない。
-
org.glassfish.jersey.servlet.ServletContainer
-
あるコードに出会いましたが、何に使うのか理解できません。 List<String> list = new ArrayList<String>() { { a
-
このラインで複数のマーカーを解決する方法