生成邀请码算法 - TFdream/blog GitHub Wiki
1、采用类似snowflake算法
核心算法如下:
private final char[] dict = new char[]{'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z'};
private static final int DICT_BIT_LEN = 5;
private static final int DICT_MASK = (1 << 5) - 1;
private static final int USER_LEFT_SHIFT_BITS = 32;
private static final int CODE_LENGTH = 12;
/**
* 不足则填充 零
*/
private static final char PADDING_CHAR = '0';
public String genCode(long userId) {
long num = (userId << USER_LEFT_SHIFT_BITS | (System.currentTimeMillis()/1000));
StringBuilder sb = new StringBuilder(12);
while (num != 0) {
int i = (int) (num & DICT_MASK);
sb.append(dict[i]);
num = num >> DICT_BIT_LEN;
}
StringBuilder rs = sb.reverse();
if (rs.length() < CODE_LENGTH) { //填充
int dist = CODE_LENGTH - rs.length();
for (int i=0; i<dist; i++) {
rs.insert(0, PADDING_CHAR);
}
}
return rs.toString();
}
2、十进制转N进制
核心流程: 1.先通过 发号器 生成全局唯一64位id(long型); 2.然后将十进制数转换为N进制形式(N取决于字典表数量,例如 A~Z,0~9共36个字母 则转换为36进制)。
发号器 可以采用 基于redis 生成、Twitter snowflake算法、美团Leaf 等算法。
进制转换代码如下:
/**
* 进制对应的字典表
*/
private final char[] dictionary;
/**
* 进制基数,例如十六进制则值为16
*/
private final int baseNum;
/**
* 该进制对应的字符串最大长度
*/
private final int maxNumLength;
/**
* 编码(将十进制数转为对应的N进制)
* 思路:十进制转N进制数,将其不断除以N取余数,然后倒序。
* @param num
* @return
*/
public String encode(long num) {
if (num < 0) {
throw new IllegalArgumentException("must be non-negative number");
}
if (num==0) {
//取码表第一个字符
return new String(new char[]{dictionary[0]});
}
StringBuilder sb = new StringBuilder(maxNumLength);
long val = num;
while (val > 0) {
int mod = (int)(val % baseNum);
sb.append(getCharByIndex(mod));
val = val / baseNum;
}
//倒序后返回
return sb.reverse().toString();
}
/**
* 解码(将X进制字符串转换为对应的十进制数)
* 思路:X进制转十进制数,从右往左每个数 乘以X的N次方,N从0开始。
* @param data N进制字符串
* @return
*/
public long decode(String data) {
if (StringUtils.isEmpty(data)) {
throw new IllegalArgumentException("null or empty:"+data);
}
char[] chars = data.toCharArray();
long num = 0;
int pow = 0;
//从右边开始
for (int i=chars.length-1; i>=0; i--) {
char item = chars[i];
int index = getIndexByChar(item);
num += getRepresentNum(index, pow);
pow++;
}
return num;
}
//---------
protected char getCharByIndex(int index) {
return dictionary[index];
}
protected int getIndexByChar(char ch) {
for (int i=0; i<baseNum; i++) {
char item = dictionary[i];
if (ch == item) {
return i;
}
}
return -1;
}
protected long getRepresentNum(int num, int pos) {
return num * (long) Math.pow(baseNum, pos);
}