243.583.606.221.817.150.598.111.409x daha fazla entropi
Crypto.randomBytes kullanmanızı öneririm . Değil sha1
, ama kimlik amaçları için, daha hızlı ve "rastgele" gibi.
var id = crypto.randomBytes(20).toString('hex');
//=> f26d60305dae929ef8640a75e70dd78ab809cfe9
Ortaya çıkan dize, oluşturduğunuz rasgele baytların iki katı kadar uzun olacaktır; onaltılık olarak kodlanan her bayt 2 karakterdir. 20 bayt, onaltılık 40 karakter olacaktır.
20 bayt kullanarak, var 256^20
ya 1.461.501.637.330.902.918.203.684.832.716.283.019.655.932.542.976 benzersiz çıkış değerleri. Bu, SHA1'in 160 bit (20 bayt) olası çıkışları ile aynıdır .
Bunu bilmek, shasum
rastgele baytlarımız için gerçekten anlamlı değil . Bir kalıbı iki kez yuvarlamak gibi ama sadece ikinci ruloyu kabul etmek gibi; Ne olursa olsun, her bir ruloda 6 olası sonuç vardır, bu nedenle ilk rulo yeterlidir.
Bu neden daha iyi?
Bunun neden daha iyi olduğunu anlamak için, öncelikle hash fonksiyonlarının nasıl çalıştığını anlamalıyız. Aynı giriş verilirse karma işlevleri (SHA1 dahil) her zaman aynı çıktıyı üretir.
Kimlik oluşturmak istediğimizi, ancak rastgele girdimizin bir bozuk para atma tarafından oluşturulduğunu varsayalım. Sahip olduğumuz "heads"
veya"tails"
% echo -n "heads" | shasum
c25dda249cdece9d908cc33adcd16aa05e20290f -
% echo -n "tails" | shasum
71ac9eed6a76a285ae035fe84a251d56ae9485a4 -
Eğer "heads"
tekrar çıkageldi, SHA1 çıkışı olacaktır aynı o ilk kez olarak
% echo -n "heads" | shasum
c25dda249cdece9d908cc33adcd16aa05e20290f -
Tamam, bu yüzden sadece 2 olası çıkışımız var.
Standart 6 taraflı bir kalıp kullanırsak, 6 olası girişimiz olur. Kaç olası SHA1 çıkışı tahmin edin? 6!
input => (sha1) => output
1 => 356a192b7913b04c54574d18c28d46e6395428ab
2 => da4b9237bacccdf19c0760cab7aec4a8359010b0
3 => 77de68daecd823babbb58edb1c8e14d7106e83bb
4 => 1b6453892473a467d07372d45eb05abc2031647a
5 => ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4
6 => c1dfd96eea8cc2b62785275bca38ac261256e278
Bu sırf bizim fonksiyonu çıkış düşünerek kendimizi kandırma kolay görünüyor o, çok tesadüfi olduğu çok rastgele.
İkimiz de bir bozuk para atma veya 6 taraflı bir kalıbın kötü bir rasgele kimlik üreteci yapacağına katılıyoruz, çünkü olası SHA1 sonuçlarımız (kimlik için kullandığımız değer) çok az. Peki ya çok daha fazla çıktısı olan bir şey kullanırsak? Milisaniyelik bir zaman damgası gibi mi? Veya JavaScript Math.random
? Ya da bu ikisinin bir kombinasyonu mu ?!
Kaç tane benzersiz id alacağımızı hesaplayalım ...
Milisaniye ile bir zaman damgasının benzersizliği
Kullanırken (new Date()).valueOf().toString()
13 karakterlik bir sayı alırsınız (ör 1375369309741
.). Bununla birlikte, bu ardışık olarak güncellenen bir sayı (milisaniye başına bir kez) olduğundan, çıktılar neredeyse her zaman aynıdır. Hadi bir bakalım
for (var i=0; i<10; i++) {
console.log((new Date()).valueOf().toString());
}
console.log("OMG so not random");
// 1375369431838
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431840
// 1375369431840
// OMG so not random
Adil olmak gerekirse, karşılaştırma amacıyla, belirli bir dakika içinde (cömert bir operasyon yürütme süresi), sahip olacaksınız 60*1000
veya 60000
benzersiz olacaksınız .
Benzersizliği Math.random
Şimdi, kullanırken Math.random
, JavaScript'in 64 bit kayan nokta sayılarını temsil etme şekli nedeniyle, 13 ila 24 karakter uzunluğunda bir uzunlukta bir sayı alırsınız. Daha uzun bir sonuç daha fazla hane anlamına gelir, bu da daha fazla entropi anlamına gelir. İlk olarak, hangisinin en olası uzunluk olduğunu bulmamız gerekir.
Aşağıdaki komut dosyası hangi uzunluğun en olası olduğunu belirleyecektir. Bunu 1 milyon rasgele sayı üreterek ve .length
her sayıya göre bir sayaç artırarak yaparız .
// get distribution
var counts = [], rand, len;
for (var i=0; i<1000000; i++) {
rand = Math.random();
len = String(rand).length;
if (counts[len] === undefined) counts[len] = 0;
counts[len] += 1;
}
// calculate % frequency
var freq = counts.map(function(n) { return n/1000000 *100 });
Her sayacı 1 milyona bölerek, geri dönen sayının uzunluğu olasılığını elde ederiz Math.random
.
len frequency(%)
------------------
13 0.0004
14 0.0066
15 0.0654
16 0.6768
17 6.6703
18 61.133 <- highest probability
19 28.089 <- second highest probability
20 3.0287
21 0.2989
22 0.0262
23 0.0040
24 0.0004
Tamamen doğru olmasa da, cömert olalım ve 19 karakter uzunluğunda rastgele bir çıktı elde edeceğinizi varsayalım; 0.1234567890123456789
. İlk karakterler her zaman olacak 0
ve .
bu yüzden gerçekten sadece 17 rastgele karakter alıyoruz. Bu bizi 10^17
+1
(olası 0
; aşağıdaki notlara bakın) veya 100.000.000.000.000.001 benzersiz bırakır .
Peki kaç rastgele girdi üretebiliriz?
Tamam, milisaniye zaman damgası için sonuç sayısını hesapladık ve Math.random
100,000,000,000,000,001 (Math.random)
* 60,000 (timestamp)
-----------------------------
6,000,000,000,000,000,060,000
Bu 6.000.000.000.000.000.060.000 taraflı tek bir kalıp. Veya bu sayıyı insanca daha sindirilebilir hale getirmek için, bu rakam kabaca aynı sayıdır.
input outputs
------------------------------------------------------------------------------
( 1×) 6,000,000,000,000,000,060,000-sided die 6,000,000,000,000,000,060,000
(28×) 6-sided die 6,140,942,214,464,815,497,21
(72×) 2-sided coins 4,722,366,482,869,645,213,696
Kulağa hoş geliyor, değil mi? Hadi öğrenelim ...
SHA1 , olası 256 ^ 20 sonuçlarla 20 baytlık bir değer üretir. Bu yüzden SHA1'i tam potansiyeline kullanmıyoruz. Peki ne kadar kullanıyoruz?
node> 6000000000000000060000 / Math.pow(256,20) * 100
Milisaniyelik bir zaman damgası ve Math.random, SHA1'in 160 bit potansiyelinin sadece yüzde 4.11e-27'sini kullanıyor!
generator sha1 potential used
-----------------------------------------------------------------------------
crypto.randomBytes(20) 100%
Date() + Math.random() 0.00000000000000000000000000411%
6-sided die 0.000000000000000000000000000000000000000000000411%
A coin 0.000000000000000000000000000000000000000000000137%
Kutsal kediler dostum! Şu sıfırlara bak. Peki ne kadar iyi crypto.randomBytes(20)
? 243.583.606.221.817.150.598.111.409 kat daha iyi.
+1
Sıfır ve sıklığı hakkında notlar
Eğer merak ediyorsanız +1
, Math.random
geri dönmek mümkündür, bu 0
da hesaba katmamız gereken 1 olası benzersiz sonuç daha var demektir.
Aşağıda olan tartışmaya dayanarak, a'nın ortaya çıkma sıklığını merak ettim 0
. İşte küçük bir senaryo, random_zero.js
bazı veriler elde ettim
#!/usr/bin/env node
var count = 0;
while (Math.random() !== 0) count++;
console.log(count);
Sonra, 4 iş parçacığında çalıştırdım (4 çekirdekli işlemcim var), çıktıyı bir dosyaya ekliyorum
$ yes | xargs -n 1 -P 4 node random_zero.js >> zeroes.txt
Yani a'nın 0
elde edilmesi o kadar zor değil. Sonra 100 değerleri kaydedildi, ortalama
3.164.854.823 rastgele 1 0
Güzel! Bu sayının eşit v8 Math.random
uygulamasının dağılımı ile eşit olup olmadığını bilmek için daha fazla araştırma yapılması gerekecektir.