趣谈唯一邀请码生成方法
趣谈唯一邀请码生成方法
前段时间项目上需要生成唯一邀请码!嘿嘿,多简单的一件事,心里就已默默将代码写了一遍。但小小的邀请码生成却也小有乾坤,这就是后话了。
一、最简单的实现
很多人 肯定都和我一开始一样!嘿嘿,这还不简单,26个字母+10个数字,去掉容易混淆的,O、0、I、1等字符就剩下32个,
/*** 随机字符串*/private static final char[] CHARS = new char[] {'F', 'L', 'G', 'W', '5', 'X', 'C', '3','9', 'Z', 'M', '6', '7', 'Y', 'R', 'T', '2', 'H', 'S', '8', 'D', 'V', 'E', 'J', '4', 'K','Q', 'P', 'U', 'A', 'N', 'B'};
然后再生成随机的六个重复的数字,对应着CHARS中的数字下标,邀请码生成搞定
Random random = new Random();List<Character> list = random.ints(0,32).distinct().limit(6).boxed().map(t -> CHARS[t]).reduce().collect(toList());
好啦,邀请码生成好了。你还要不重复是吧,OK!没问题,我们把邀请码保存到缓存里面,例如redis。每次新生成的邀请码都先去缓存判断下是否存在,如果已经存在则再生成一个,直到找到合适的。 功能完成。喝下午茶去。。。。
思考 :这种实现是否真的最佳实现了呢? 至少存在以下一些问题
- 唯一性判断需要通过缓存,引入第三方组件,并且消耗内存资源
- 在99.999%的情况下,缓存判断是否存在都是 FALSE 的,可以说是一种浪费
- 用户与邀请码的对应关系需要记录,如果数据库查询的话,至少需要在邀请码字段上加个索引,又是要消耗资源
那么我们有其它办法来解这个问题吗?
二、数学和密码学来实现
前提 :存在唯一的标识,例如给用户生成邀请码,那么用户id就是唯一标识(id在任何情况下都不会重复)。
那么我们首先想到的是根据唯一的id,是使用算法来生成。
/*** 生成邀请码** @param id 唯一的id主键* @return code*/String gen(Long id) {//进行补位id = id * PRIME1 + SLAT;//将 id 转换成32进制的值long[] b = new long[CODE_LENGTH];//32进制数b[0] = id;for (int i = 0; i < CODE_LENGTH-1; i++) {b[i + 1] = b[i] / CHARS_LENGTH;b[i] = b[i] % CHARS_LENGTH;}b[5] = (b[2] + b[3] + b[4]) * PRIME1 % CHARS_LENGTH;StringBuilder buffer = new StringBuilder();Arrays.stream(b).boxed().map(Long::intValue).map(t -> CHARS[t]).forEach(buffer::append);return buffer.toString();}
大概就是像上面代码一样,先将id扩大,然后再转换成32进制的数。为啥是32进制呢,因为 CHARS 里面是32个char,那么32进制正好对应 CHARS 的下标。
好了,基于算法根据id来生成邀请码就完成了。然后再逆向下算法,就可以根据邀请码来还原成用户id,找到是哪个用户的了。
YEAH、YEAH!任务完成
然后代码一运行
public static void main(String [] args){for(long id=10000L;id<10020L;id++){String code = InviteCode.instance().gen(id);System.out.println("code:"+code);}}
code:HASCL6
code:DASCL6
code:JASCL6
code:QASCL6
code:AASCL6
code:FNSCL6
code:WNSCL6
code:CNSCL6
code:ZNSCL6
code:7NSCL6
连续的 id 产生的邀请码是惊人的相似,后四位是完全相同的!这样如果有心用户发现这个规律后,就有可能破解邀请码的生成算法。
三、再进一步
之前有说过密码学,密码学里面有两个重要概念,就是扩大和混淆。既然后四位如此的相似,那么我们希望能够扩大后四位的差异。
/*** 生成邀请码** @param id 唯一的id主键* @return code*/String gen(Long id) {//补位,并扩大整体id = id * PRIME1 + SLAT;//将 id 转换成32进制的值long[] b = new long[CODE_LENGTH];//32进制数b[0] = id;for (int i = 0; i < CODE_LENGTH - 1; i++) {b[i + 1] = b[i] / CHARS_LENGTH;//扩大每一位的差异b[i] = (b[i] + i * b[0]) % CHARS_LENGTH;}b[5] = (b[0] + b[1] + b[2] + b[3] + b[4]) * PRIME1 % CHARS_LENGTH;//进行混淆long[] codeIndexArray = new long[CODE_LENGTH];for (int i = 0; i < CODE_LENGTH; i++) {codeIndexArray[i] = b[i * PRIME2 % CODE_LENGTH];}StringBuilder buffer = new StringBuilder();Arrays.stream(codeIndexArray).boxed().map(Long::intValue).map(t -> CHARS[t]).forEach(buffer::append);return buffer.toString();}
这样我们就能对连续id生成差异化的邀请码了。
public static void main(String [] args){for(long id=10000L;id<10020L;id++){String code = InviteCode.instance().gen(id);System.out.println("code:"+code);}}
code:H8XKDR
code:DEHGQH
code:JKA6FD
code:QUZDCJ
code:ABVA7Q
code:FXLCSN
code:W9YT4L
code:C6K4N5
code:ZRXL53
四、结语
任务总算完成了,汇报给领导求表扬。但生成算法还是存在瑕疵的,因为对于六位数的邀请码,只取了5个有效位,第六位作为校验位。所有最大能表示的id到千万级,如果还需要更大的id,则需要扩大邀请码的位数。
详细算法地址(包含将邀请码还原成id的算法哦):https://gist.github.com/zjnxyz/6ef6007f493a0edea6837cc06e934abc 52
整理了一下源码
import java.util.Arrays;public class InviteCode {/*** 随机字符串*/private static final char[] CHARS = new char[] { 'F', 'L', 'G', 'W', '5', 'X', 'C', '3', '9', 'Z', 'M', '6', '7','Y', 'R', 'T', '2', 'H', 'S', '8', 'D', 'V', 'E', 'J', '4', 'K', 'Q', 'P', 'U', 'A', 'N', 'B' };private final static int CHARS_LENGTH = 32;/*** 邀请码长度*/private final static int CODE_LENGTH = 6;/*** 随机数据*/private final static long SLAT = 1234561L;/*** PRIME1 与 CHARS 的长度 L互质,可保证 ( id * PRIME1) % L 在 [0,L)上均匀分布*/private final static int PRIME1 = 3;/*** PRIME2 与 CODE_LENGTH 互质,可保证 ( index * PRIME2) % CODE_LENGTH 在* [0,CODE_LENGTH)上均匀分布*/private final static int PRIME2 = 11;/*** 生成邀请码** @param id 唯一的id主键* @return code*/public static String gen(Long id) {// 补位id = id * PRIME1 + SLAT;// 将 id 转换成32进制的值long[] b = new long[CODE_LENGTH];// 32进制数b[0] = id;for (int i = 0; i < CODE_LENGTH - 1; i++) {b[i + 1] = b[i] / CHARS_LENGTH;// 按位扩散b[i] = (b[i] + i * b[0]) % CHARS_LENGTH;}b[5] = (b[0] + b[1] + b[2] + b[3] + b[4]) * PRIME1 % CHARS_LENGTH;// 进行混淆long[] codeIndexArray = new long[CODE_LENGTH];for (int i = 0; i < CODE_LENGTH; i++) {codeIndexArray[i] = b[i * PRIME2 % CODE_LENGTH];}StringBuilder buffer = new StringBuilder();Arrays.stream(codeIndexArray).boxed().map(Long::intValue).map(t -> CHARS[t]).forEach(buffer::append);return buffer.toString();}/*** 将邀请码解密成原来的id** @param code 邀请码* @return id*/public static Long decode(String code) {if (code.length() != CODE_LENGTH) {return null;}// 将字符还原成对应数字long[] a = new long[CODE_LENGTH];for (int i = 0; i < CODE_LENGTH; i++) {char c = code.charAt(i);int index = findIndex(c);if (index == -1) {// 异常字符串return null;}a[i * PRIME2 % CODE_LENGTH] = index;}long[] b = new long[CODE_LENGTH];for (int i = CODE_LENGTH - 2; i >= 0; i--) {b[i] = (a[i] - a[0] * i + CHARS_LENGTH * i) % CHARS_LENGTH;}long res = 0;for (int i = CODE_LENGTH - 2; i >= 0; i--) {res += b[i];res *= (i > 0 ? CHARS_LENGTH : 1);}return (res - SLAT) / PRIME1;}/*** 查找对应字符的index** @param c 字符* @return index*/private static int findIndex(char c) {for (int i = 0; i < CHARS_LENGTH; i++) {if (CHARS[i] == c) {return i;}}return -1;}
}
趣谈唯一邀请码生成方法相关推荐
- java唯一码_唯一邀请码生成(Java版本)
前言 之前收到一个需求,甲方说,他们想给用户生成一个唯一的邀请码,然后用户量在xxx之类的,例如我这里就随便说个5kw个吧.这个嘛,听起来都觉得挺简单的,毕竟每个用户基本上都有自己的唯一用户id,用那 ...
- 唯一邀请码生成(Java版本)
前言 之前收到一个需求,甲方说,他们想给用户生成一个唯一的邀请码,然后用户量在xxx之类的,例如我这里就随便说个5kw个吧.这个嘛,听起来都觉得挺简单的,毕竟每个用户基本上都有自己的唯一用户id,用那 ...
- 用户ID生成唯一邀请码的几种方法
文章目录 1.需求描述 2.需求分析 3.字符集 4.方法一:随机数+唯一性判断(不可逆) 5.方法二:Hash+唯一性判断(不可逆) 6.方法三:进制法(可逆) 7.方法四:进制法+扩散.混淆(可逆 ...
- java生成一条唯一的邀请码_根据用户id生成一个唯一邀请码
需求描述:根据用户id生成与之对应的唯一邀请码,范围为'0-9A-Z'. 这个需求的重点在于加粗的部分,也就是要能够根据邀请码反推出用户ID,这样邀请码就不用入库了,在用户量很大的情况下,性能可以得到 ...
- 如何实现用户id生成一个唯一邀请码
一个10进制的数字短还是一个16进制的数字短? 肯定是16进制相对短一些,所以我们可以直接把用户id转成10+26=36进制的不就可以了吗?具体代码如下: function createCode($u ...
- php用户注册自动生成邀请码,PHP如何实现根据用户id生成一个唯一邀请码
根据用户id生成与之对应的唯一邀请码,范围为'0-9A-Z'.这个需求的重点在于加粗的部分,也就是要能够根据邀请码反推出用户ID,这样邀请码就不用入库了,在用户量很大的情况下,性能可以得到不小的提升. ...
- java生成一条唯一的邀请码_如何实现用户id生成一个唯一邀请码
根据用户id生成与之对应的唯一邀请码,范围为'0-9A-Z'.这个需求的重点在于加粗的部分,也就是要能够根据邀请码反推出用户ID,这样邀请码就不用入库了,在用户量很大的情况下,性能可以得到不小的提升. ...
- mysql 生成邀请码_如何实现用户id生成一个唯一邀请码
根据用户id生成与之对应的唯一邀请码,范围为'0-9A-Z'.这个需求的重点在于加粗的部分,也就是要能够根据邀请码反推出用户ID,这样邀请码就不用入库了,在用户量很大的情况下,性能可以得到不小的提升. ...
- 快递取件码生成软件_一种分布式的取件码生成方法技术
[技术实现步骤摘要] 本专利技术涉及样品柜取件码生成 ,特别涉及一种分布式的取件码生成方法. 技术介绍 在快递柜的领域中,快递员进行快件派送的时候,通常会遇到收件人无法立刻来取件的情况,这样就需要快递 ...
最新文章
- git stash和git stash pop
- 在C#中操作XM II
- 数据库建模模板、菜单显示出问题解决方案
- 你真的了解实时计算吗?
- 【HTML学习】——一个网页HTML编程的构成
- HDU1284——钱币兑换问题【dp】
- linux下环境变量PATH的用法
- Visio—如何画矩形虚线边框?
- 2014阿里实习生面试题——mysql如何实现的索引
- 118 Python程序中的线程操作-守护线程
- 开发用到的小功能、功能、网站等
- 网管工具 dstat
- python中if满足条件后退出程序_Python的流程控制:if条件判断
- 服务器系统怎么用主板做RAID,超微主板如何创建RAID磁盘阵列 服务器组建RAID0、RAID1教程(图文)...
- linux微内核,开源微内核seL4microkernel
- 图片去水印软件分享!这三个好用的软件不能错过!​
- 福大软工1816 · 团队现场编程实战(抽奖系统
- PowerBuilder 8/9 与 SCC的集成(1)
- JAVA 程序设计基础过关题库(50道)
- 傲腾+NVMe如何让VK节省数亿美元?