本文参照护照编码的规则实现类似算法,仅供学习研究使用
先看一下护照的编码规则 :
例如:E00000001、E00000002、E00000003...E99999999、EA0000001
【规则分析】
可以发现,固定E开头,后面的数字按顺序增加,到达99999999时高位开始变成字母从A开始
也就是说先使用8位数十进制开始顺序编码,十进制数字用完以后由高位至低位逐渐变成36进制(10个数字+26个大写字母)
【实现解析】
如果直接使用36进制去编码,显然不对,就会出现 00000A 这样的编码。而需要让字母在高位 必须先使用10进制,10进制用完再改变高位的进制类型,也就是说不同位数使用不同的进制来处理,到达该进制最大值时升级进制。
以下是实现效果和完整的代码,先看效果,再讲解代码:
【效果展示】
* 为方便查看结果,使用3位长度测试
图1:开始使用十进制编码
图2:达到十进制最大值999以后高位变成36进制
图3:到达36进制3位数最大值ZZZ后报错
【代码】
// 不同进制配置
let num36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let num10 = '0123456789';
// val=当前值 ,numList=进制列表(支持不同位使用不同的进制)
function plusOne(val, numList) {
// 拆分成单个位数,反转后让索引为0的=低位
let valArr = val.split('').reverse();
numList.reverse();
// 指定进制,在当前位的基础上加1位,返回'0'时表示需要向前进一位
function add(num, cur) {
let nexIndex = num.indexOf(cur) + 1;
if (nexIndex === num.length) nexIndex = 0;
return num[nexIndex];
}
// 从低位到高位处理
for (let i = 0; i < valArr.length; i++) {
let c = add(numList[i] || num10, valArr[i]);
valArr[i] = c;
// 没有达到进位要求
if (c !== '0') break;
// 等于0时需要在高一位的位置加1,push增加一个高位
valArr.push('');
}
return valArr.reverse().join('');
}
// 获取当前数值下一个值,限制总位数
function nextVal(curVal, len) {
// 计算不同位的进制
let numList = [];
// 处理不同位的进制, 总位数达到设置的长度才开始升进制
if (curVal.length === len) {
let curValArr = curVal.split('');
numList = curValArr.map((a) => {
// 当前位本来就是36进制的
return num10.indexOf(a) === -1 ? num36 : num10;
});
// 将36进制位的后一位到达9的10进制位变成36进制
for (let i = 0; i < curValArr.length; i++) {
if (numList[i].length === num36.length) continue;
// 最高位达到9升36进制,非最高位达到9并且它的上一位到达36进制最后一个值也升36进制
if (num10.indexOf(curValArr[i]) === num10.length - 1 && (i === 0 || curValArr[i - 1] === num36[num36.length - 1])) {
numList[i] = num36;
}
}
}
let nextVal = plusOne(curVal, numList);
// 超出最大值时报错
if (nextVal.length > len) throw new Error('Digital exceeds the maximum');
// 高位补0并返回
return (new Array(len).join('0') + nextVal).substr(-1 * len);
}
// 计算指定位数可生成的最大编码量
function allCount(len) {
let max = 0;
// 计算超过10进制最大值后的数量
for (let i = 0; i < len; i++) {
let tmp = 26;
for (let j = 0; j < len - i - 1; j++) {
tmp = tmp * 10;
}
max += tmp;
}
// 10进制最大值
max += Math.pow(10, len);
console.log(max);
}
allCount(3);
// 测试
let testNum = '0';
for (let i = 0; i < 20000; i++) {
testNum = nextVal(testNum, 3);
console.log(testNum);
}
* 直接使用nodejs跑这段代码即可看到输出结果
【代码解析】
代码中共出现3个方法,生成编码主要使用了其中两个方法:plusOne、nextVal,计算指定长度最大生成编码数量方法:allCount
plusOne 负责通过指定当前值和进制方案增加自然数1返回结果
nextVal 负责指定当前值和编码长度(数字位数)生成进制方案并调用plusOne获得结果并对结果进行范围判断(超过指定位数允许的最大值时将throw异常)、高位补0操作
allCount 传递编码长度计算可生成的最大编码数量,以便根据业务设定规则
致读者:感谢你阅读本文,请随手点击右下角的推荐或分享,谢谢!