みなさん、こんにちは。
さて、前回前々回にわたって、
指定した範囲の乱数の作成の仕方を書きました。
改めて奥が深いですね。

読み忘れた方のためにリンクしますっ(^o^)
指定した範囲の乱数を作成したい(前編)
指定した範囲の乱数を作成したい(後編)
指定した範囲の乱数を作成したい(実戦編)←いまここっ!


これまでの話では、
小さなより精度が低い乱数発生器を使用していたため、
実用上は、そんなに関係ないんじゃないかな。
と思う方も多いかもしれません。

今回の話はかなり短いですが、
実際の実用例を考えて、
乱数の落とし穴にはまっていきたいと思います。(^o^)丿

あなたは、ネットワークゲームを設計しています。
モンスターを倒すと、レアアイテムを落とします。
確率は、0.01%刻みで設定できるようにしたいです。
言語は、VC++、またはHSPを使用することとします。

ほら、ちょっと本当にありそうな雰囲気になってきましたよね。
さて、どのように設計するでしょうか、
VC++の乱数発生器だったら、みんな使ってるし大丈夫だろう。
乱数作成の高速化も考えて、 x = rand() % 10000 こんな感じにして、
で、if(x < 1000) で 10% みたいに計算しちゃおう。

では、実際にコードを書いてみましょう。
とりあえず、HSPが好きなので、HSPで書きます。
まず、 HSP には rnd(n) という関数があるのですが、
これはCで rand() % n という文章にすぎません。

#define multiplier 214013
#define addend 2531011

x = 1

goto *start

#defcfunc rnd2 int s
	x = x * multiplier + addend
	return (((x >> 16) & 0x7FFF) \ s)

*start

mes "HSPの rnd(n) は、余りを使用して乱数を切り出している。"
mes "HSPの rnd -> " + rnd(100) + " は"
mes "上記の rnd2 -> " + rnd2(100) + " と同じ。"

上記をふまえて、
乱数の値がどのように分布するか度数分布表を作ってみましょう。

size = 100000
dim bin, 10

repeat size
	x = rnd(10000)
	bin(int(x / 1000))++	
loop

repeat 10
	mes strf("[%5d ,%5d) ... %5d回", (cnt * 1000), ((cnt + 1) * 1000), bin(cnt))
loop

実行すると、

範囲回数
[ 0, 1000)12401
[1000, 2000)12176
[2000, 3000)11618
[3000, 4000)9079
[4000, 5000)9088
[5000, 6000)9107
[6000, 7000)9150
[7000, 8000)9048
[8000, 9000)9316
[9000,10000)9017

つまり、10%を作るつもりが、12%になってしまいます!!
さらに、if(rnd(10000) < 3000) で 30.00% みたいに計算しちゃおうと思うと 36%になります。

棒グラフを作ると分かりやすい。
histogram

そんなこんなで、
今回は、ちょっと身近に感じそうな話を作ってみました。
みなさん、乱数には十分に気を付けましょう!

ちなみに、HSPの機能の中でより正しい乱数を作りたい場合は、
これまでの話をふまえると下のような感じ。(色々と追加しました。)

#module

#defcfunc rnd15 int s
	// 15ビットの実数の乱数を作成
	return int(((double(rnd(0x8000))) / 0x8000) * s)

#defcfunc rnd30 int s
	// 30ビットの実数の乱数を作成
	return int(((32768.0 * rnd(0x8000) + rnd(0x8000)) / 0x40000000) * s)

#defcfunc rnd52 int s, local a
	// 52ビットの実数の乱数を作成
	a = double(rnd(0x8000))
	repeat 3
		a *= 32768
		a += rnd(0x8000)
	loop
	return int(a / 1152921504606846976.0 * s)

#defcfunc rnd4 int s, local a, local b
	// 繰り返して範囲外は捨てる
	repeat
		a = rnd(0x8000)
		b = a \ s
		if(a - b + s <= 0x8000) {
			break
		}
	loop
	return b

#global

font "MS ゴシック", 12
pos 5, 5

// 作成する乱数の範囲によって、
// 乱数のビットを大きいものを選ぶか、
// 上記の rnd4() を利用するといいです。

randomize
size = 1000000

mes "通常版"
dim bin, 10
repeat size
	x = rnd(10000)
	bin(int(x / 1000))++	
loop
repeat 10
	mes strf("[%5d ,%5d) ... %5d回", (cnt * 1000), ((cnt + 1) * 1000), bin(cnt))
loop

mes "工夫したもの"
dim bin, 10
repeat size
	x = rnd15(10000)
	bin(int(x / 1000))++	
loop
repeat 10
	mes strf("[%5d ,%5d) ... %5d回", (cnt * 1000), ((cnt + 1) * 1000), bin(cnt))
loop

mes "誤差が少ない"
dim bin, 10
repeat size
	x = rnd30(10000)
	bin(int(x / 1000))++	
loop
repeat 10
	mes strf("[%5d ,%5d) ... %5d回", (cnt * 1000), ((cnt + 1) * 1000), bin(cnt))
loop

pos 200, 5

mes "最も誤差が少ない"
dim bin, 10
repeat size
	x = rnd52(10000)
	bin(int(x / 1000))++	
loop
repeat 10
	mes strf("[%5d ,%5d) ... %5d回", (cnt * 1000), ((cnt + 1) * 1000), bin(cnt))
loop

mes "正しい乱数"
dim bin, 10
repeat size
	x = rnd4(10000)
	bin(int(x / 1000))++	
loop
repeat 10
	mes strf("[%5d ,%5d) ... %5d回", (cnt * 1000), ((cnt + 1) * 1000), bin(cnt))
loop

上記の「正しい乱数」のアルゴリズムについては、下で紹介していますが、前編から読むとより勉強になるかもしれません。
指定した範囲の乱数を作成したい(後編)

関連記事

  • C言語のアドレス演算子の不思議C言語のアドレス演算子の不思議 前々から、C言語のポインタで不思議だったこと。 最適化C 言語 […] Posted in プログラミング
  • Bresenhamアルゴリズムで線分の計算は早いのかなBresenhamアルゴリズムで線分の計算は早いのかな 以前、直線を描くのにこんなのを作った。 特に工夫点もない、素直な方法である。 #module "linem" #deffunc line2 int line_x1,int line_y1,int line_x2,int line_y2 line_xabs = abs(line_x1-line_x2) line_yabs = […] Posted in アルゴリズム
  • 男は黙ってボゴソート男は黙ってボゴソート 男は黙ってボゴソート! 馬鹿らしいソートなんだけど、アイデアが面白い! しかし、O(n*n!)ってすごいですね。 下のサンプルはとりあえずWikipediaにかいてあったトランプの例をやってみました。 #include <stdio.h> #include <stdlib.h> #include <string.h> […] Posted in アルゴリズム
  • JScriptバッチ処理用ライブラリ 20130825公開JScriptバッチ処理用ライブラリ 20130825公開 私のサイトで公開している JScriptバッチ処理用ライブラリのバージョン「20130825」を公開しました。 主な内容は、JavaのString.formatを、JScriptでも使用できるようにするというものです。 JavaのString.formatは、大体はCのsprintfと似ているのですが、もっといろいろできます。 ただ、今回JScriptで使用できる […] Posted in ライブラリ制作
  • メディアンカットによる減色アルゴリズムメディアンカットによる減色アルゴリズム 3日前から、いつものHSP言語で減色プログラムを作ってたのですが、なかなか難しい。 メディアンカットを実装したいんだけど、資料がないんですよねー。 ということで、減色アルゴリズムを調べた形跡を紹介します。 減色アルゴリズム1 1. 全ての色からある程度階調を落とした色数のヒストグラム作成 2. 色数を多い方から昇順でソートする。 3. […] Posted in ライブラリ制作
  • JavaでStringとint[]の相互変換する方法JavaでStringとint[]の相互変換する方法 はじめに charAt1文字を1変数(コードポイントで)に割り当てたい。 そんな要望からStringとint[]との相互変換を行ってみます。 使い方はお察し下さい。 文字列から数値配列へ static public int[] toInteger(String text) { int textlength = text.length(); for (int […] Posted in プログラミング