这是我之前询问过的问题的后续问题Is this JS Unique ID Generator Unreliable? (Getting collisions)。
在下面的scriptlet中,我使用2种方法生成10000个随机数。方法1是直接随机数,最大为10 ^ 6,而方法2将随机数连接到10 ^ 6(与[1]中相同的想法)与当前JS Date()。time()时间戳。还有方法[3]只能在RNG上进行Math.round而不是整个连接结果。
我的问题是,如果你一直点击测试按钮,你会看到[1]总是产生10000个唯一数字,但[2]无论如何产生~9500。 [3]产生~9900但从未产生最大值。这是为什么?从[0..10 ^ 6]中的先前随机数获得+/- 1并且与时间戳串联的时间戳恰好相反+/- 1混合的可能性是不可能的。我们在一个循环中产生相同的毫秒数。 10 ^ 6是一个巨大的限制,比我原来的问题大得多,我们知道这是真的,因为方法[1]完美地运作。
是否存在截断某种情况,修剪字符串并使其更有可能重复?矛盾的是,较小的字符串比使用其中相同RNG的较大字符串效果更好。但是如果没有截断,我希望结果是[1]中的100%。
function run() {
var nums1 = new Set(), nums2 = new Set(), nums3 = new Set();
for (var i = 0; i < 10000; i++) {
nums1.add(random10to6th());
}
for (var i = 0; i < 10000; i++) {
nums2.add(random10to6th_concatToTimestamp());
}
for (var i = 0; i < 10000; i++) {
nums3.add(random10to6th_concatToTimestamp_roundRNGOnly());
}
console.clear();
console.log('Random 10^6 Unique set: ' + nums1.size);
console.log('Random 10^6 and Concat to Date().time() Unique set: ' + nums2.size);
console.log('Random 10^6 and Concat to Date().time(), Round RNG Only Unique set: ' + nums3.size);
function random10to6th() {
return Math.random() * Math.pow(10, 6);
}
function random10to6th_concatToTimestamp() {
return Math.round(new Date().getTime() + '' + (Math.random() * Math.pow(10, 6)));
}
}
function random10to6th_concatToTimestamp_roundRNGOnly() {
return new Date().getTime() + '' + Math.round(Math.random() * Math.pow(10, 6));
}
<button onclick="run()">Run Algorithms</button>
<p>(Keep clicking this button)</p>
答案 0 :(得分:3)
是否存在某种截断的截断,这会削减字符串 并使它更有可能得到重复?
是的,只需将一个随机数舍入,就可以切掉小数位数。与非舍入随机数相比,这减少了可能结果的数量。
除此之外,还可以将时间戳(13位)与0到1000000(1到7位)之间的值连接起来。因此,您的连接结果的总数将为14到20位数,但JavaScript的数字数据类型的精度有限,并且忠实地表示整数,最多只能达到大约16位数(请参阅Number.MAX_SAFE_INTEGER
)。
示例:我们假设时间戳为1516388144210
,并且您将500000
中的随机数附加到500400
:
+'1516388144210500000' == 1516388144210500000
+'1516388144210500100' == 1516388144210500000
+'1516388144210500200' == 1516388144210500000
+'1516388144210500300' == 1516388144210500400
+'1516388144210500400' == 1516388144210500400
您可以看到,在将这些字符串转换为数字时,它们会四舍五入到最近的可用IEEE-754双精度(64位)数字。这是因为1516388144210500000 > Number.MAX_SAFE_INTEGER
。
答案 1 :(得分:0)
我认为这里有很多问题。我不知道下面的每个项目对观察到的差异有哪些或多大程度,只是它们是可以解释结果的东西。
一个是因为您将一个数字与一个带有数字的字符串连接起来,然后将该值强制转换回一个数字作为舍入结果的一部分。将意外结果输入Round函数会很容易(由于浮动精度本身可能会导致碰撞,如下所述)
其次,我认为在连接时间戳时,实际上减少结果数的随机性。该功能很可能每秒多次调用;如果以&gt;的速率调用它Date.getTime()精度返回的值与前一循环迭代中生成的值相同。
第三,除非我错过了什么,你是否认为随机数gen只能保证伪 -random?在处理您发布的代码中的大值时,精度和数字限制会起到一定作用。由于数字的随机部分被添加到最不重要的部分,因此更有可能被截断,切断或修改。
尝试反转连接并查看结果(只有大约4次左右的碰撞)。碰撞是由我和@ le_m的答案概述的原因解释的。
function run() {
var nums1 = new Set(), nums2 = new Set()
for (var i = 0; i < 10000; i++) {
nums1.add(random10to6th());
}
for (var i = 0; i < 10000; i++) {
nums2.add(random10to6th_concatToTimestamp());
}
console.clear();
console.log('Random 10^6 Unique set: ' + nums1.size);
console.log('Random 10^6 and Concat to Date().time() Unique set: ' + nums2.size);
function random10to6th() {
return Math.random() * Math.pow(10, 6);
}
function random10to6th_concatToTimestamp() {
return Math.round((Math.random() * Math.pow(10, 6)) + '' + new Date().getTime());
}
}
<button onclick="run()">Run Algorithms</button>
<p>(Keep clicking this button)</p>