虾搞,数字换算为固定长度的进制码

虾搞,数字换算为固定长度的进制码

类似短网址算法,把数字换算为固定长度的进制码。纯属个人兴趣研究,暂无实际论证,仅供思路参考。

采用语言,Javascript( ES6+ )。

短网址,相信这个已经不是什么陌生产物。经典的短网址示例,t.cn/abcdefg。

这里,不谈短网址具体的实现方案,就说说关于将数字转换成进制key的方案。

62进制,将数字0-9,小写a-z,大写A-Z;

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

自己写的也好,别人写的也好,或者找在线工具,都能进行换算;

例如数字"123",转换后,可以得到一个62进制数"1Z"。

我瞎搞的方法:

class Base {
    constructor() {
        this.char = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
        this.hex = this.char.length;
    }

    toKey(n) {
        if (!n) return this.char[0];
        const num = n * 1;
        const a = Math.floor(num / this.hex);
        const b = num % this.hex;
        let str = '';
        if (a > 0) {
            str += this.toKey(a);
        }
        str += this.char[b];
        return str;
    }
    
    toNum(s) {
        if (!s) return 0;
        const str = s.toString();
        const len = str.length;
        const list = str.split('');
        let num = 0;
        list.forEach((i, key) => {
            let index = this.char.indexOf(i);
            if (index == -1) index = 0;
            const now = index * Math.pow(this.hex, len - key - 1);
            num += now;
        });
        return num;
    }
}

但可以看到,123转换后,进制数1Z,长度只有2位。

如果想要得到一个固定长度key,可能就需要将数字的起始数做大一点;

例如62进制,想要一个长度为6位的key,至少需要从916132832后开始。

这是一个相对的方案,算是满足了6位长度。

而我期望的是,进制码长度固定,数字ID也不能从这么大就开始。如果可以,最好前后数字得到的进制码,没有一眼看穿的规律。

这类方案就可以用于邀请码、兑换码等功能的使用。

一、可以避免重复性;

二、相对系统可以反向得到出数字ID,而不是查询数据库得到对应的;

具体,在设计对应功能的时候再考虑。

如何实现长度固定位数?假设为6位。

我的想法与方案:

把62进制的char,拆成两部分。

从62位中,随机抽取出固定长度部分,做为填充替换符号。假设抽取了10位为替换符号,那么就变成了52位进制法。

1、先用数字ID转换为52进制码,在没有超过最低数字的情况下,长度会不足6位。

2、得到进制码长度x,从替换符号中,随机获得(6-x)长度的字符。

3、在进制码随意位置,循环随机插入替换符号,得到一个长度为6的特殊进制码。

如何反向推算数字?

1、获得特殊进制码,字符串替换方法,将替换符号全部剔除;

2、正常的进制码,反向推算数字;

示例代码:

class Base {
    constructor(len = 6) {
        this.char = '456789defghijklmnopqrstuvwxyzDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
        this.symbol = '0123abcABC'.split('');
        this.hex = this.char.length;
        this.symbolHex = this.symbol.length;
        this.len = len; // 固定长度
    }

    toKey(n) {
        if (!n) return this.char[0];
        const num = n * 1;
        const a = Math.floor(num / this.hex);
        const b = num % this.hex;
        let s = '';
        if (a > 0) {
            s += this.toKey(a);
        }
        s += this.char[b];
        return s;
    }
    
    toNum(s) {
        if (!s) return 0;
        const str = s.toString();
        const len = str.length - 1;
        let n = 0;
        str.split('').forEach((i, key) => {
            let index = this.char.indexOf(i);
            if (index == -1) index = 0;
            const now = index * Math.pow(this.hex, len - key);
            n += now;
        });
        return n;
    }
    
    /**
     * 随机数
     */
    random(a, b) {
        return Math.floor(Math.random() * (a - b)) + b;
    }
    
    /**
     * 在字符串中随机插入一个字符
     * 前提是str.length > 1
     * 
     * @param string str
     * @param string ins
     */
    randomInsert(str, ins) {
        if (str.length == 1) {
            return `${str}${ins}`;
        }
        const r = this.random(0, str.length + 1);
        const s1 = str.substring(0, r);
        const s2 = str.substring(r);
        return `${s1}${ins}${s2}`;
    }
    
    /**
     * 把KEY转出固定位数
     * 不足部分混入替补符号
     * 剔除替补符号以后,原字符串保持不变
     */
    toFixed(key) {
        if (key.length >= this.len) return key;
        const num = this.len - key.length;
        let symbol = [];
        symbol.length = num;
        symbol.fill(1, 0);
        symbol = symbol.map(() => this.symbol[this.random(0, num)]);
        if (num < 2) {
            return this.randomInsert(symbol.join(''), key);
        }
        let newKey = key;
        symbol.forEach(i => newKey = this.randomInsert(newKey, i));
        return newKey;
    }
    
    /**
     * 清除key中替补符号
     */
    clearFixed(key) {
        const regexp = new RegExp(`[${this.symbol.join('')}]`, 'g');
        return key.replace(regexp, '');
    }
    
    /**
     * 获取keyId
     */
    getKeyId(n) {
        const key = this.toKey(n);
        return this.toFixed(key);
    }

    /**
     * 获取NumId
     */
    getNumId(s) {
        const key = this.clearFixed(s);
        return this.toNum(key);
    }
}

为了增加混淆性,还可以将进制码的char字符串先自行随机一次。