再谈非重复随机序列号生成算法

来源:互联网 发布:淘宝卖家淘宝客推广 编辑:程序博客网 时间:2024/06/05 22:38

这段时间项目中又要开发兑换码功能,此前的项目已经开发过,但是为了保证这个功能在将来的可重复利用,我决定重构一下相关模块。

原来的模块不是我开发的,但也已经可以完成这个要求。但其中存在两个问题,这也是基本上非重复随机序列生成算法都要面对的问题
(1)是非重复性
(2)是效率

我们原来的程序员开发的,是使用的最低效的方式,即随机生成后,遍历已经生成的所有随机序列号,如果重复则放弃这组随机序列号,重新生成。在这个过程中,首先暴露的是低效,其次是只能保证本批次的序列号不重复,再次启动工具生成,两个批次的随机序列号就无法保证不重复了。

那么也会有人问,为什么不使用GUID之类的算法,因为兑换码这个东西,其实有很多额外的需求,比如长度,随机码中使用的字符,有纯数字的序列号,也有纯字母的,所以诸如GUID,MD5,SHA等都不完全适合。

我前几天曾经转了一篇非重复随机序列生成算法 ,其中作者的思路确实提供我很大的帮助,但是仔细看了作者的实现后,其实也发现了一点欠缺,该文中的算法解决了效率问题,也解决了重复问题,但是其能生成的序列号数量大大减少。打个比方,如果是生成长度10的序列号,文中的算法将保证[0~9]的数组只出现一次,按照排列组合的算法,最终出现的序列号总数是10*9*7*6*5*4*3*2*1,但实际我们需求的序列号,每一位的数字可以重复,但整个序列号不能产生重复,换句话说,我们的序列号总数应该是10的10次幂。

改进方案

(一)对于一个非重复的随机序列号,要做到非重复,其N位上出现的字符的顺序不能有相同的情况。使用排列组合的知识我们知道,假设我们有N位,每一位上允许的字符有m个的话,我们能产生的不重复的排列组合总量为m1 m2 * m3 …*mN个,这就是非重复性
(二)随机性,也就是我们从上述m的N次幂的序列中,随机抽取K个,就能完成我们的目标。当然,不能用随机数来决定抽取哪一个,这样还得遍历已经抽取出来的序列号,来判断是否两次挑选序列号是否挑到了同一个

(三)我的算法思路,首先求得一个排列组合的子集,即s1 * s2 * s3 *,,,*sN = C,C为我们要生成的序列号总量,s是m的一个字符子集,s这个子集是从m集合中抽取的随机的不重复的字符。这样就能保证C的序列号是完全不会重复的序列号。s的子集,我使用了转载文章中作者的算法,通过构建一个包含m个不同字符的数组,并对数组随机抽取索引,进行移位交换,最后得到一个包含不重复字符的s子集

(四)序列号需求,对于任意长度的序列号,我们只需要增加N,对于序列号中出现的字符限制,我们只需要更改m集合中的字符内容,就可以得到纯字符,或是纯数字,或是混合的序列号

(五)不同批次的永不重复性,这个我选择了使用增加前缀的方法,比如采用时间,来确定唯一性,然后将时间作为序列号的一部分,加入前缀或是后缀,也可以自定义。
其实还有一种方法就是先利用本算法生成K个较短长度的非重复随机序列号,K足够大,保证这批序列号足够使用,然后存入一个文件中,以后再生成时,将这K个中随机抽取一个,作为批次的标识,生成一次,就抛弃K中的一个,就可以了

附上相应的只要算法代码

if (count.ToString().Length > length){    Console.WriteLine("生成数量大于位数,无法生成");    return null;}var seed = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0);var random = new Random((int)seed.TotalSeconds);int[] bitTimes = new int[length];int total_count = 1;int idx = 0;while (idx < length){    int f = random.Next(0, 10);    bitTimes[idx] = f;    total_count *= f > 0 ? f : 1;    idx++;    if (total_count >= count)    {        break;    }}while (total_count < count){    for (int i = 0; i < length; i++)    {        if (bitTimes[i] < 10)        {            var c = bitTimes[i];            bitTimes[i] += 1;            total_count = total_count + total_count / c;        }        if (total_count >= count)        {            break;        }    }}var codes = new string[total_count];for (int i = 0; i < length; i++){    int coloum = bitTimes[i];    coloum = Math.Max(1, coloum);    int row = total_count / coloum;    int[] num_array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };    int[] num = new int[coloum];    int x = 0, tmp = 0, n = 0;    for (int m = num_array.Length - 1; m > 0; m--)    {        x = random.Next(0, m + 1);        tmp = num_array[m];        num_array[m] = num_array[x];        num_array[x] = tmp;        num[n] = num_array[m];        n++;        if (n >= coloum)        {            break;        }    }    for (int j = 0; j < row; j++)    {        for (int k = 0; k < coloum; k++)        {            var index = j * coloum + k;            codes[index] = i == 0 ? num[k].ToString() : codes[index] + num[k];        }    }}return codes;

这里只有实现,还未作优化,相关优化,代码工程请移步
https://github.com/duotemplar/RedeemCodeGenerator

原创粉丝点击