验证用户ID的线性同余生成器唯一性

时间:2018-01-18 07:57:37

标签: javascript algorithm proof-of-correctness

Id要求:

  • 短(用户键入的字符数)
  • 允许尽可能多的ID
  • 看似随机(因此没有人可以通过id轻松猜出用户数)
  • 没有大小写混合字符(用户更容易输入)
  • 没有发誓生成的话

所以我选择使用6个字符的大写字符串,没有由用户计数排序的线性同余生成器生成的元音。我从wiki那里学到了LCG,我想验证我的代码是正确的,因为我自己编写了LCG中的系数,并且讨厌有ids的碰撞。我自己尝试过碰撞测试,但是在200万之后将ID存储在地图中时,我的堆空间用尽了。我认为数学检查,但真的会欣赏第二(或千)组的眼睛。 (也不是真的有JS经验,所以如果有一种更有效的方式来交换元音,很想知道)。



// UserId generated from stored userCount key in redis, sequenced by a Linear
// Congruental generator, and stringified with a radix of 31 (all numbers and
// letters besides vowels = 10 + 26 - 5 = 31).
//
// The equation we will use to generate the sequence is:
// userVal = (a * userCount + c) % m + minVal

// To guarantee each userId is at least 6 characters, we need:
// minVal = 31^5 = 28629151
//
// To guarantee that our userId is at most 6 characters, we need:
// log31(minVal + maxVal + 1) = 6
// => 31^6 = minVal + maxVal + 1
// => maxVal = 31^6 - minVal - 1 = 858874529
//
// So our LCG needs to have:
// 1) m < maxVal
// 2) m and c relatively prime
// 3) a-1 is divisible by 4 if m is
// 4) a-1 is divisible by all prime factors of m
//
// m = 858062848 = 2^16 * 13093
// c = 1
// a = 3351809 = 2^8 * 13093 + 1
//
// This means we can support 858062848 unique userIds (>850 million).
// If we ever cross that amount, it will be a good problem to solve :)
this.getUserId = function(){
    var userCount = Spark.getRedis().incr("unique-id:user");

    var a = 3351809;
    var c = 1;
    var m = 858062848;
    var minVal = 28629151;

    var userVal = (a * userCount + c) % m + minVal;

    return userVal.toString(31)
        .toUpperCase()
        .replace(/A/g, 'V')
        .replace(/E/g, 'W')
        .replace(/I/g, 'X')
        .replace(/O/g, 'Y')
        .replace(/U/g, 'Z');
};
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:0)

我怀疑您在代码中引用的线性同余生成器的条件足以确保序列具有最大长度。但是,我认为这并不能保证得到的随机数具有良好的统计特性。 LCG参数有一些“优选的”选择,它们给出了良好的统计特性(例如连续数字组之间的低相关性),这些特征在Numerical Recipes(Press等人)中列出。更详细的理论讨论在Knuth的计算机编程艺术中。