skimemo


skimemo - 日記/2019-12-08/JavaScriptで本当にランダムな文字列を生成しようとしてみる の変更点


#blog2navi()
*JavaScriptで本当にランダムな文字列を生成しようとしてみる [#be252cee]

JavaScriptでランダムな文字列を生成しようとして検索すると、こういう処理が大量に出てきます。~
#code(php){{
const str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const len=15;    // 文字列長さ
let result = "";
for(let i=0;i<len;i++){
    result += str.charAt(Math.floor(Math.random() * str.length));
}
}}
でも、JavaScriptのMath.random()って偏るんじゃないの? とか思って調べてみました。
** 方法 [#ad949c59]
上記の方法と、[[Fisher&#8211;Yatesのシャッフル:https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%83%E3%82%B7%E3%83%A3%E3%83%BC%E2%80%93%E3%82%A4%E3%82%A7%E3%83%BC%E3%83%84%E3%81%AE%E3%82%B7%E3%83%A3%E3%83%83%E3%83%95%E3%83%AB]]を使って混ぜて生成した文字列から、各文字の登場回数をカウントしてばらつきを見ます。
** コード [#la863fe0]
見た方が早いと思います。以下です。
#code(php){{
const len = 8;
const str = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const count = 10000;

let good={};
let bad={};
for(let i=0; i<str.length; i++){
	good[str.substr(i,1)] = 0;
	bad[str.substr(i,1)] = 0;
}
for(let i=0; i<count; i++){
	const code = goodCode(len,str);
	for(let j=0; j<code.length; j++){
		good[code.substr(j,1)]++;
	}
}
for(let i=0; i<count; i++){
	const code = badCode(len,str);
	for(let j=0; j<code.length; j++){
		bad[code.substr(j,1)]++;
	}
}
for(let i=0; i<str.length; i++){
	console.log(str.substr(i,1)+': good='+good[str.substr(i,1)]+' bad='+bad[str.substr(i,1)]);
}

function badCode(len,str) {
	let result = "";
	for(let i=0;i<len;i++){
		result += str.charAt(Math.floor(Math.random() * str.length));
	}
	return result;
}

function goodCode(len,str){
	return makeRandomArray(str.split('')).join('').substr(0, len);
}
// 配列をランダムに混ぜる
function makeRandomArray(array) {
	for (let i = array.length - 1; i > 0; i--) {
		const r = Math.floor(Math.random() * (i + 1));
		[array[i], array[r]] = [array[r], array[i]];
	}
	return array;
}
}}
~2つのランダムな文字列を生成する関数、&inlinecode{goodCode()};と、&inlinecode{badCode()};を作成し、それらを1万回呼んで、生成された8文字の文字列に登場する文字をカウントしています。
~出力結果は以下の通り。
 0: good=1299 bad=1316
 1: good=1355 bad=1306
 2: good=1335 bad=1294
 3: good=1248 bad=1264
 4: good=1286 bad=1250
 5: good=1245 bad=1281
 6: good=1287 bad=1290
 7: good=1330 bad=1260
 8: good=1260 bad=1277
 9: good=1377 bad=1269
 A: good=1277 bad=1299
 B: good=1343 bad=1271
 C: good=1267 bad=1264
 D: good=1307 bad=1347
 E: good=1353 bad=1283
 F: good=1231 bad=1317
 G: good=1419 bad=1300
 H: good=1257 bad=1340
 I: good=1314 bad=1315
 J: good=1275 bad=1271
 K: good=1250 bad=1225
 L: good=1317 bad=1226
 M: good=1263 bad=1293
 N: good=1305 bad=1232
 O: good=1290 bad=1326
 P: good=1212 bad=1351
 Q: good=1264 bad=1230
 R: good=1300 bad=1322
 S: good=1262 bad=1235
 T: good=1244 bad=1359
 U: good=1286 bad=1269
 V: good=1285 bad=1379
 W: good=1271 bad=1318
 X: good=1298 bad=1323
 Y: good=1241 bad=1315
 Z: good=1260 bad=1260
 a: good=1332 bad=1246
 b: good=1254 bad=1274
 c: good=1285 bad=1277
 d: good=1335 bad=1306
 e: good=1321 bad=1268
 f: good=1214 bad=1272
 g: good=1284 bad=1300
 h: good=1263 bad=1244
 i: good=1217 bad=1260
 j: good=1272 bad=1406
 k: good=1361 bad=1291
 l: good=1273 bad=1326
 m: good=1275 bad=1314
 n: good=1287 bad=1312
 o: good=1282 bad=1303
 p: good=1317 bad=1293
 q: good=1254 bad=1307
 r: good=1290 bad=1195
 s: good=1315 bad=1268
 t: good=1332 bad=1249
 u: good=1294 bad=1318
 v: good=1303 bad=1269
 w: good=1319 bad=1305
 x: good=1357 bad=1319
 y: good=1280 bad=1311
 z: good=1271 bad=1290
~これだけだとよく分からないので、散布図にしてみます。
~&ref(good.png,,50%);&ref(bad.png,,50%);
~うーん、バラつき具合は実は変わらない?~
もしかしたらJavaScriptの実装によるのでしょうか・・。
~そこで、[[メルセンヌ・ツイスタ乱数:https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%AB%E3%82%BB%E3%83%B3%E3%83%8C%E3%83%BB%E3%83%84%E3%82%A4%E3%82%B9%E3%82%BF]]というのを使ってみました。
#ref(mt.png,,50%);
~&ref(mt.png,,50%);&ref(mt-bad.png,,50%);
~確かに良くはなってます。でも劇的に違うわけではありません。~
厳密さを求めないのであれば、普通にMath.random()で良いのかも・・・。

** 参考 [#jcba2842]
-- [[JavaScriptで配列をシャッフルする方法:https://qiita.com/komaji504/items/62a0f8ea43053e90555a]]
-- [[Array#sort実装のshuffleは偏る:https://qiita.com/minodisk/items/94b6287468d0e165f6d9]]
-- [[JavaScriptでメルセンヌ・ツイスト乱数を使う方法まとめ:https://pisuke-code.com/javascript-how-to-use-mt-rand/]]

#htmlinsert(twitterbutton.html)
RIGHT:Category: &#x5b;[[Linux>日記/Category/Linux]]&#x5d; - 01:05:48
----
RIGHT:&blog2trackback();
#comment(above)
#blog2navi()