1. 兑换码需求
-
可读性好:兑换码是要给用户使用的,用户需要输入兑换码,因此可读性必须好。我们的要求:
-
长度不超过10个字符
-
只能是24个大写字母和8个数字:ABCDEFGHJKLMNPQRSTUVWXYZ23456789
-
-
数据量大:优惠活动比较频繁,必须有充足的兑换码,最好有10亿以上的量
-
唯一性:10亿兑换码都必须唯一,不能重复,否则会出现兑换混乱的情况
-
不可重兑:兑换码必须便于校验兑换状态,避免重复兑换
-
防止爆刷:兑换码的规律性不能很明显,不能轻易被人猜测到其它兑换码
-
高效:兑换码生成、验证的算法必须保证效率,避免对数据库带来较大的压力
2. 算法分析
2.1 Base32转码
将24个字母和8个数字放到数组中,如下:
角标 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
字符 |
A |
B |
C |
D |
E |
F |
G |
H |
J |
K |
L |
M |
N |
P |
Q |
R |
角标 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
字符 |
S |
T |
U |
V |
W |
X |
Y |
Z |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
这样,0~31的角标刚好对应了我们的32个字符!而2的5次幂刚好就是32,因此5位二进制数的范围就是0~31。
那因此,只要我们让数字转为二进制的形式,然后每5个二进制位为一组,转10进制的结果是不是刚好对应一个角标,就能找到一个对应的字符呢?
这样是不是就把一个数字转为我们想要的字符个数了。这种把二进制数经过加密得到字符的算法就是Base32法,类似的还有Base64法。
举例:假如我们经过自增id计算出一个复杂数字,转为二进制,并每5位一组,结果如下:
01001 00010 01100 10010 01101 11000 01101 00010 11110 11010
此时,我们看看每一组的结果:
-
01001转10进制是9,查数组得字符为:K
-
00010转10进制是2,查数组得字符为:C
-
01100转10进制是12,查数组得字符为:N
-
10010转10进制是18,查数组得字符为:B
-
01101转10进制是13,查数组得字符为:P
-
11000转10进制是24,查数组得字符为:2
-
...
依此类推,最终那一串二进制数得到的结果就是KCNBP2PC84,刚好符合我们的需求。
自增id从1增加到Integer的最大值,可以达到40亿以上个数字,而占用的字节仅仅4个字节,也就是32个bit位,距离50个bit位的限制还有很大的剩余,符合要求!
综上,我们可以利用自增id作为兑换码,但是要利用Base32加密,转为我们要求的格式。此时就符合了我们的几个要求了:
-
可读性好:可以转为要求的字母和数字的格式,