[英]Array of random numbers optimization
我有一个生成随机数数组的函数。 它可以工作,但是我认为对大数字它可能工作缓慢。 有没有一种方法可以对其进行优化?
function renerateRandomNumbers(maxNumber, randomNumbersCount) { let i; const arrResult = []; for (i = 0; i < randomNumbersCount; i++) { let rand = Math.random() * (maxNumber); rand = Math.round(rand); if (arrResult.indexOf(rand) === -1 ) { arrResult.push(rand); } else { i--; } } return arrResult; }
您的代码的问题在于,复杂度会以几何方式增长,因为它有机会生成已经被多次选择的数字。
我们需要实现的是在每次迭代中获取数字,以使迭代计数等于randomNumbersCount
。
如何避免多个相同的随机数?
假设您要有0-10个范围内的5个随机数
第一次迭代
创建具有值var candidates = [0,1...10]
的数组
产生随机数,假设为0
在结果中存储candidates[0]
数字candidates[0]
从candidates
删除0
。 为了对candidates
数组进行有效的重新索引,我们将把candidates[candidates.length - 1]
放入candidates[0]
并删除candidates[candidates.length - 1]
然后将执行此操作randomNumbersCount
次。
第二次迭代
我们的候选数组现在为[10,1,2,3,4,5,6,7,8,9]
生成随机数,再说一次0
。 哇,我们生成了相似的随机数,但是那又如何呢?
我们的结果中有0
,但是candidates[0]
不再是0
candidates[0]
现在是10
因此我们挑选candidates[0]
是10
,并且将其存储,并从候选中移除。 将candidates[candidates.length - 1]
( 9
)放入candidates[0]
并删除candidates[candidates.length - 1]
我们的结果是[0,10]
第三次迭代
我们的candidates
现在是[9,1,2,3,4,5,6,7,8]
产生随机数,假设为0
我们不再担心,因为我们知道candidates[0]
是9
添加candidates[0]
(女巫为9
),我们正在保存结果,并将其从candidates
删除
我们的结果是[0,10,9]
, candidates
是[8,1,2,3,4,5,6,7]
等等
BTW的实现比解释要短得多:
function renerateRandomNumbers(maxNumber, randomNumbersCount) { var candidates = [...Array(maxNumber).keys()]; return Array(randomNumbersCount).fill() .map(() => { const randomIndex = Math.floor(Math.random() * candidates.length) const n = candidates[randomIndex] candidates[randomIndex] = candidates[candidates.length - 1] candidates.length = candidates.length - 1 return n }) .sort((a, b) => a - b) // sort if needed } console.log (renerateRandomNumbers(10, 5))
编辑 -对于任何未来的用户,@ ScottSauyet的解决方案应该是公认的答案。 它是比我的解决方案更有效的解决方案。
我认为解决该问题的算法上最有效的方法是从0-maxNumber
生成所有可能数字的列表,对数组( O(n)
)进行混洗,然后从混randomNumbersCount
的数组中获取第一个randomNumbersCount
数字。 它看起来像下面的样子:
function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } } function generateRandomNumbers(maxNumber, randomNumbersCount) { var possibleNumbers = []; // populate array with all possible values for (var i = 0; i <= maxNumber; i++) { possibleNumbers.push(i); } // shuffle the array to get a random order of the possible numbers O(n) shuffleArray(possibleNumbers); // trim the array down to only the first n numbers where n = randomNumbersCount possibleNumbers.length = randomNumbersCount; return possibleNumbers; } console.log (generateRandomNumbers(10, 5)); console.log (generateRandomNumbers(10, 5)); console.log (generateRandomNumbers(10, 5));
方案的解决方案是相当有效的,但仅当所寻求的数量非常接近最大数量时才有效。 如果您的计数明显较小,则可能会出现问题,因为解决方案为O(m + n)
,其中m
为最大值, n
为所需计数。 在空间上也是O(m)
。 如果m
大,这可能是个问题。
一个变体可以通过做相同的事情来使它在时间和空间上大约为O(n)
,但是当我们到达count
项时,并且不预先填充数组而是默认为其索引,就可以停止随机播放。
function venerateRandomNumbers(max, count) { // todo: error if count > max const arr = new Array(max + 1) for (let i = max; i > max - count; i--) { const j = Math.floor(Math.random() * (i + 1)) const temp = arr[j] || j arr[j] = arr[i] || i arr[i] = temp } return arr.slice(-count) } console.log(venerateRandomNumbers(1000000, 10))
您可以在repl.it上查看性能比较
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.