1
头图

起因

关于随机不重复字符串,如果没有长度限制,那么最简单的方法当前时间戳 + 固定位数随机字符串的形式完全可以满足(仍有概率重复,但几乎可以忽略),至于长度,有很多办法解决,但最终也无法做到很短。

最近公司有个小需求,把邀请码降低到四位,四位不重复邀请码的实现方式就不那么随意了,直观的想法是通过10个数字(0-9) + 26个小写字母(a-z)排列组合实现,即所谓的排列可重复问题。

这类问题公式如下,每次n种选择,选择r次的排列共有:n的r次幂。这很好理解,一次有n种选择,第二次有n∗n种选择,……,第r次有nr种选择。

对应我们的需求,也就是可以有36的4次幂 = 1679616种组合,完全可以满足一个小型项目的需求。如果项目比较大,完全可以通过加入其他项(比如特殊字符、大写字母等等)到可择列表实现,最终还可以提升邀请码位数进一步扩展。

思路比较直观简单,但也比较实用,欢迎大家学习交流。

实现

思路:

1、首先要确定随机数的依据,采用递增数字(可以对应数据库id)作为基础,创建随机串。
2、打乱10个数字 + 26个字母的组合,这样随机数看起来会舒服一些。
3、把基数转换成36进制数字,转换后不足4位则补充0位。
4、把四位数字的每一位作为下标对应打乱后10个数字 + 26个字母的组合的列表。
5、组合成4位唯一随机字符串。

下面是代码:

exports.random = (number) => {
  // 打乱的10个数字 + 26个字母
  const arr = ["m","0","j","f","8","o","z","w","5","t","p","a","1","d","s","h","v","x","9","b","r","y","2","e","7","4","3","q","6","n","u","l","c","g","i","k"];
  // 把number由十进制数转换成36进制数
  const transNumber = binaryConversion(+number, 10, 36);
  // 排除大于4位的情况
  const len = transNumber.toString().length;
  if (len > 4) {
    console.log('数字过大');
    return;
  }
  // 转换后的数字不足4位则补充0位,并按字符转换成数组
  const list = prefixInteger(transNumber, 4).toString().split('');
  const len4Arr = [];
  for (const num of list) {
    // 判断当前字符是数字还是字母
    const type = checkStrType(num);
    // 如果是数字不处理,如果是字母则转换成对应的数组(10 - 35)
    const index = type === 'string' ? stringTonum(num) + 9 : num;
    len4Arr.push(arr[+index]);
  }

  // 返回结果
  return len4Arr.join('');
}

补充

缺点:基数数字低于36无法创建,比如起始数字大于36。

欢迎大家提出高效思路,不足之处请指出。

其他代码如下:


function binaryConversion(num, m ,n) {
  return parseInt(String(num), m).toString(n);
}
function prefixInteger(num, length) {
 return (Array(length).join('0') + num).slice(-length);
}
function stringTonum(a) {
  const str = a.toLowerCase().split('');
  const al = str.length;
  const getCharNumber = charx => charx.charCodeAt() - 96;
  let numout = 0;
  let charnum = 0;
  for (let i = 0; i < al; i++) {
      charnum = getCharNumber(str[i]);
      numout += charnum * Math.pow(26, al - i - 1);
  };

  return numout;
}

function checkStrType(str) {
  //验证是否是英文
  var pattern = new RegExp("[A-Za-z]+");
  if (pattern.test(str)) return 'string';
  //验证是否是数字
  var patternNumber = new RegExp("[0-9]+");
  if (patternNumber.test(str)) return 'number';

  return '';
}

小磊
352 声望884 粉丝

以一颗更加开放,更加多元,更加包容的心走进别人的世界