生成邀请码算法 - 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);
    }