趣谈唯一邀请码生成方法

前段时间项目上需要生成唯一邀请码!嘿嘿,多简单的一件事,心里就已默默将代码写了一遍。但小小的邀请码生成却也小有乾坤,这就是后话了。

一、最简单的实现

很多人 肯定都和我一开始一样!嘿嘿,这还不简单,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;}
}

趣谈唯一邀请码生成方法相关推荐

  1. java唯一码_唯一邀请码生成(Java版本)

    前言 之前收到一个需求,甲方说,他们想给用户生成一个唯一的邀请码,然后用户量在xxx之类的,例如我这里就随便说个5kw个吧.这个嘛,听起来都觉得挺简单的,毕竟每个用户基本上都有自己的唯一用户id,用那 ...

  2. 唯一邀请码生成(Java版本)

    前言 之前收到一个需求,甲方说,他们想给用户生成一个唯一的邀请码,然后用户量在xxx之类的,例如我这里就随便说个5kw个吧.这个嘛,听起来都觉得挺简单的,毕竟每个用户基本上都有自己的唯一用户id,用那 ...

  3. 用户ID生成唯一邀请码的几种方法

    文章目录 1.需求描述 2.需求分析 3.字符集 4.方法一:随机数+唯一性判断(不可逆) 5.方法二:Hash+唯一性判断(不可逆) 6.方法三:进制法(可逆) 7.方法四:进制法+扩散.混淆(可逆 ...

  4. java生成一条唯一的邀请码_根据用户id生成一个唯一邀请码

    需求描述:根据用户id生成与之对应的唯一邀请码,范围为'0-9A-Z'. 这个需求的重点在于加粗的部分,也就是要能够根据邀请码反推出用户ID,这样邀请码就不用入库了,在用户量很大的情况下,性能可以得到 ...

  5. 如何实现用户id生成一个唯一邀请码

    一个10进制的数字短还是一个16进制的数字短? 肯定是16进制相对短一些,所以我们可以直接把用户id转成10+26=36进制的不就可以了吗?具体代码如下: function createCode($u ...

  6. php用户注册自动生成邀请码,PHP如何实现根据用户id生成一个唯一邀请码

    根据用户id生成与之对应的唯一邀请码,范围为'0-9A-Z'.这个需求的重点在于加粗的部分,也就是要能够根据邀请码反推出用户ID,这样邀请码就不用入库了,在用户量很大的情况下,性能可以得到不小的提升. ...

  7. java生成一条唯一的邀请码_如何实现用户id生成一个唯一邀请码

    根据用户id生成与之对应的唯一邀请码,范围为'0-9A-Z'.这个需求的重点在于加粗的部分,也就是要能够根据邀请码反推出用户ID,这样邀请码就不用入库了,在用户量很大的情况下,性能可以得到不小的提升. ...

  8. mysql 生成邀请码_如何实现用户id生成一个唯一邀请码

    根据用户id生成与之对应的唯一邀请码,范围为'0-9A-Z'.这个需求的重点在于加粗的部分,也就是要能够根据邀请码反推出用户ID,这样邀请码就不用入库了,在用户量很大的情况下,性能可以得到不小的提升. ...

  9. 快递取件码生成软件_一种分布式的取件码生成方法技术

    [技术实现步骤摘要] 本专利技术涉及样品柜取件码生成 ,特别涉及一种分布式的取件码生成方法. 技术介绍 在快递柜的领域中,快递员进行快件派送的时候,通常会遇到收件人无法立刻来取件的情况,这样就需要快递 ...

最新文章

  1. git stash和git stash pop
  2. 在C#中操作XM II
  3. 数据库建模模板、菜单显示出问题解决方案
  4. 你真的了解实时计算吗?
  5. 【HTML学习】——一个网页HTML编程的构成
  6. HDU1284——钱币兑换问题【dp】
  7. linux下环境变量PATH的用法
  8. Visio—如何画矩形虚线边框?
  9. 2014阿里实习生面试题——mysql如何实现的索引
  10. 118 Python程序中的线程操作-守护线程
  11. 开发用到的小功能、功能、网站等
  12. 网管工具 dstat
  13. python中if满足条件后退出程序_Python的流程控制:if条件判断
  14. 服务器系统怎么用主板做RAID,超微主板如何创建RAID磁盘阵列 服务器组建RAID0、RAID1教程(图文)...
  15. linux微内核,开源微内核seL4microkernel
  16. 图片去水印软件分享!这三个好用的软件不能错过!​
  17. 福大软工1816 · 团队现场编程实战(抽奖系统
  18. PowerBuilder 8/9 与 SCC的集成(1)
  19. JAVA 程序设计基础过关题库(50道)
  20. 傲腾+NVMe如何让VK节省数亿美元?

热门文章

  1. JS document方法
  2. 汽车门阈值防抱死系统abs
  3. 涨薪 50%,从小厂逆袭,坐上美团 L8 技术专家(面经 + 心得)
  4. C#开发之——控件简介(12.6)
  5. 台式计算机cpu功率,如何计算主机所需的功率? ?
  6. 新浪顶级架构师保驾护航,国内首本大型分布式架构笔记浴火新生
  7. cst和ansys_HFSS -CST-FEKO
  8. CodeTop 1-20
  9. ubuntu18.04 安装Adobe Flash Player
  10. apt安装golang