简体   繁体   English

如何根据javascript中的固定字符集生成随机数

[英]How to generate a random number based on fixed character set in javascript

I'm generating a number based on a fixed character set.我正在根据固定字符集生成一个数字。

function generator()
{
     var text = "";
     var char_list = "LEDGJR", number_list = "0123456789";
     for(var i=0; i < 2; i++ )
     {  
          text += char_list.charAt(Math.floor(Math.random() * char_list.length));
     }

     for(var j=0; j < 2; j++ )
     {  
          text += number_list.charAt(Math.floor(Math.random() *             
                              number_list.length));
     }

     return text;
}

Result : RE39, JE12 etc...结果: RE39、JE12 等...

Once all the permutation related to the above sequence is done, then the generator should generate string as RE391, JE125 means adding one more number to the complete number.一旦与上述序列相关的所有排列完成,则生成器应生成字符串为RE391,JE125表示在完整数字上再增加一个数字。

How can I get the permutation count of sequence?如何获得序列的排列计数?

For simplicity consider the case where:为简单起见,请考虑以下情况:

chars = "AB"
nums = "123";

and we want to generate a 4-digit sequence of two chars and two numbers.我们想要生成一个由两个字符和两个数字组成的 4 位序列。

We define these variables我们定义这些变量

rows = [chars, chars, nums, nums]
rowSizes = rows.map(row => row.length) // [2, 2, 3, 3]

It's easy to see the set size of all possible permuations equals:很容易看出所有可能排列的集合大小等于:

spaceSize = rowSizes.reduce((m, n) => m * n, 1) // 2*2*3*3 = 36

And we define two set of utility functions, usage of which I'll explain in detail later.并且我们定义了两组效用函数,我将在后面详细解释它们的用法。

  1. decodeIndex() which gives us uniqueness decodeIndex()这给了我们唯一性
function euclideanDivision(a, b) {
  const remainder = a % b;
  const quotient =  (a - remainder) / b;
  return [quotient, remainder]
}

function decodeIndex(index, rowSizes) {
  const rowIndexes = []
  let dividend = index
  for (let i = 0; i < rowSizes.length; i++) {
    const [quotient, remainder] = euclideanDivision(dividend, rowSizes[i])
    rowIndexes[i] = remainder
    dividend = quotient
  }
  return rowIndexes
}
  1. getNextIndex() which gives us pseudo-randomness getNextIndex()为我们提供了伪随机性
function isPrime(n) {
  if (n <= 1) return false;
  if (n <= 3) return true;

  if (n % 2 == 0 || n % 3 == 0) return false;

  for (let i = 5; i * i <= n; i = i + 6) {
    if (n % i == 0 || n % (i + 2) == 0) return false;
  }
  return true;
}

function findNextPrime(n) {
  if (n <= 1) return 2;
  let prime = n;

  while (true) {
    prime++;
    if (isPrime(prime)) return prime;
  }
}

function getIndexGeneratorParams(spaceSize) {
  const N = spaceSize;
  const Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
  const firstIndex = Math.floor(Math.random() * spaceSize);
  
  return [firstIndex, N, Q]
}

function getNextIndex(prevIndex, N, Q) {
  return (prevIndex + Q) % N
}

Uniqueness唯一性

Like mentioned above, spaceSize is the number of all possible permutations, thus each index in range(0, spaceSize) uniquely maps to one permutation.如上所述, spaceSize是所有可能排列的数量,因此range(0, spaceSize)每个index唯一地映射到一个排列。 decodeIndex helps with this mapping, you can get the corresponding permutation to an index by: decodeIndex有助于此映射,您可以通过以下方式获得index的相应排列:

function getSequenceAtIndex(index) {
  const tuple = decodeIndex(index, rowSizes)
  return rows.map((row, i) => row[tuple[i]]).join('')
}

Pseudo-Randomness伪随机性

(Credit to this question . I just port that code into JS.) (归功于这个问题。我只是将该代码移植到 JS 中。)

We get pseudo-randomness by polling a "full cycle iterator" .我们通过轮询“完整循环迭代器” † 来获得伪随机性。 The idea is simple:这个想法很简单:

  1. have the indexes 0..35 layout in a circle, denote upperbound as N=36将索引0..35布局成一个圆圈,将0..35表示为N=36
  2. decide a step size, denoted as Q ( Q=23 in this case) given by this formula 决定一个步长,表示为Q (在这种情况下Q=23 )由这个公式给出
    Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
  3. randomly decide a starting point, eg number 5随机决定一个起点,例如数字5
  4. start generating seemingly random nextIndex from prevIndex , by开始产生看似随机nextIndexprevIndex ,由
    nextIndex = (prevIndex + Q) % N

So if we put 5 in we get (5 + 23) % 36 == 28 .所以如果我们把5放进去,我们会得到(5 + 23) % 36 == 28 Put 28 in we get (28 + 23) % 36 == 15 .28放入我们得到(28 + 23) % 36 == 15

This process will go through every number in circle (jump back and forth among points on the circle), it will pick each number only once, without repeating.这个过程会遍历圈中的每个数字(在圈上的点之间来回跳跃),每个数字只会选择一次,不重复。 When we get back to our starting point 5 , we know we've reach the end.当我们回到起点5 ,我们知道我们已经到达终点。

†: I'm not sure about this term, just quoting from this answer †:我不确定这个词,只是引用这个答案
‡: This formula only gives a nice step size that will make things look more "random", the only requirement for Q is it must be coprime to N ‡:这个公式只给出了一个很好的步长,这会让事情看起来更“随机”,对Q的唯一要求是它必须与N互质

Full Solution完整解决方案

Now let's put all the pieces together.现在让我们把所有的部分放在一起。 Run the snippet to see result.运行代码片段以查看结果。

I've also includes the a counter before each log.我还在每个日志之前包含了一个计数器。 For your case with char_list="LEDGJR", number_list="0123456789" , the spaceSize for 4-digit sequence should be 6*6*10*10 = 3600对于char_list="LEDGJR", number_list="0123456789"spaceSize ,4 位序列的spaceSize应为6*6*10*10 = 3600

You'll observe the log bump to 5-digit sequence at 3601 😉你会在 3601 处观察到对数增加到 5 位序列😉

 function euclideanDivision(a, b) { const remainder = a % b; const quotient = (a - remainder) / b; return [quotient, remainder]; } function decodeIndex(index, rowSizes) { const rowIndexes = []; let divident = index; for (let i = 0; i < rowSizes.length; i++) { const [quotient, remainder] = euclideanDivision(divident, rowSizes[i]); rowIndexes[i] = remainder; divident = quotient; } return rowIndexes; } function isPrime(n) { if (n <= 1) return false; if (n <= 3) return true; if (n % 2 == 0 || n % 3 == 0) return false; for (let i = 5; i * i <= n; i = i + 6) { if (n % i == 0 || n % (i + 2) == 0) return false; } return true; } function findNextPrime(n) { if (n <= 1) return 2; let prime = n; while (true) { prime++; if (isPrime(prime)) return prime; } } function getIndexGeneratorParams(spaceSize) { const N = spaceSize; const Q = findNextPrime(Math.floor((2 * N) / (1 + Math.sqrt(5)))); const firstIndex = Math.floor(Math.random() * spaceSize); return [firstIndex, N, Q]; } function getNextIndex(prevIndex, N, Q) { return (prevIndex + Q) % N; } function generatorFactory(rows) { const rowSizes = rows.map((row) => row.length); function getSequenceAtIndex(index) { const tuple = decodeIndex(index, rowSizes); return rows.map((row, i) => row[tuple[i]]).join(""); } const spaceSize = rowSizes.reduce((m, n) => m * n, 1); const [firstIndex, N, Q] = getIndexGeneratorParams(spaceSize); let currentIndex = firstIndex; let exhausted = false; function generator() { if (exhausted) return null; const sequence = getSequenceAtIndex(currentIndex); currentIndex = getNextIndex(currentIndex, N, Q); if (currentIndex === firstIndex) exhausted = true; return sequence; } return generator; } function getRows(chars, nums, rowsOfChars, rowsOfNums) { const rows = []; while (rowsOfChars--) { rows.push(chars); } while (rowsOfNums--) { rows.push(nums); } return rows; } function autoRenewGeneratorFactory(chars, nums, initRowsOfChars, initRowsOfNums) { let realGenerator; let currentRowOfNums = initRowsOfNums; function createRealGenerator() { const rows = getRows(chars, nums, initRowsOfChars, currentRowOfNums); const generator = generatorFactory(rows); currentRowOfNums++; return generator; } realGenerator = createRealGenerator(); function proxyGenerator() { const sequence = realGenerator(); if (sequence === null) { realGenerator = createRealGenerator(); return realGenerator(); } else { return sequence; } } return proxyGenerator; } function main() { const char_list = "LEDGJR" const number_list = "0123456789"; const generator = autoRenewGeneratorFactory(char_list, number_list, 2, 2); let couter = 0 setInterval(() => { console.log(++couter, generator()) }, 10); } main();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM