skimemo


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

_ JavaScriptで本当にランダムな文字列を生成しようとしてみる

JavaScriptでランダムな文字列を生成しようとして検索すると、こういう処理が大量に出てきます。

  1
  2
  3
  4
  5
  6
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()って偏るんじゃないの? とか思って調べてみました。

_ 方法

上記の方法と、Fisher–Yatesのシャッフルを使って混ぜて生成した文字列から、各文字の登場回数をカウントしてばらつきを見ます。

_ コード

見た方が早いと思います。以下です。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
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つのランダムな文字列を生成する関数、goodCode()と、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

これだけだとよく分からないので、散布図にしてみます。

good.pngbad.png

うーん、バラつき具合は実は変わらない?
もしかしたらJavaScriptの実装によるのでしょうか・・。

そこで、メルセンヌ・ツイスタ乱数というのを使ってみました。

mt.pngmt-bad.png

確かに良くはなってます。でも劇的に違うわけではありません。
厳密さを求めないのであれば、普通にMath.random()で良いのかも・・・。

_ 参考

Category: [Linux] - 01:05:48



 
Last-modified: 2019-12-08 (日) 10:10:36 (316d)