如何使用JavaScript 生成1到100之间的一些唯一随机数?
如何使用JavaScript 生成1到100之间的一些唯一随机数?
Answers:
例如:要生成8个唯一的随机数并将其存储到数组中,只需执行以下操作:
var arr = [];
while(arr.length < 8){
var r = Math.floor(Math.random() * 100) + 1;
if(arr.indexOf(r) === -1) arr.push(r);
}
console.log(arr);
Returns a random number between 0 (inclusive) and 1 (exclusive)
。如果the Math.random()
偶然返回0,则Math.ceil(0)
s也为0,尽管机会很小。
randlines file | head -10
。
生成100个数字的排列,然后顺序选择。
使用Knuth Shuffle(又名Fisher-Yates shuffle)算法。
JavaScript:
function fisherYates ( myArray,stop_count ) {
var i = myArray.length;
if ( i == 0 ) return false;
int c = 0;
while ( --i ) {
var j = Math.floor( Math.random() * ( i + 1 ) );
var tempi = myArray[i];
var tempj = myArray[j];
myArray[i] = tempj;
myArray[j] = tempi;
// Edited thanks to Frerich Raabe
c++;
if(c == stop_count)return;
}
}
编辑:
改进的代码:
function fisherYates(myArray,nb_picks)
{
for (i = myArray.length-1; i > 1 ; i--)
{
var r = Math.floor(Math.random()*i);
var t = myArray[i];
myArray[i] = myArray[r];
myArray[r] = t;
}
return myArray.slice(0,nb_picks);
}
潜在问题:
假设我们有100个数字的数组{例如[1,2,3 ... 100]},并且在8次交换后我们停止交换;那么大多数时候数组看起来像{1,2,3,76,5,6,7,8,...这里的数字将被洗牌... 10}。
因为每个数字都会以1/100的概率被交换,所以概率很高。交换前8个数字的概率是8/100,而概率是。交换其他92的总和为92/100。
但是,如果我们针对完整数组运行算法,则可以确保(几乎)每一项都被交换。
否则,我们将面临一个问题:要选择哪个8个数字?
使用Set的现代JS解决方案(以及平均情况O(n))
const nums = new Set();
while(nums.size !== 8) {
nums.add(Math.floor(Math.random() * 100) + 1);
}
console.log([...nums]);
Math.floor(Math.random()*100) + 1
Set
在JS中发现很酷!但是,这种解决方案会不会导致不必要的数字生成,直到一个满足唯一性要求,尤其是在最后一次迭代中,如果8接近100呢?因此,我认为我更喜欢sort
下面的答案。
如果您想避免使用库,上述技术是很好的方法,但是根据您是否对库的了解,我建议您检查Chance在JavaScript中生成随机内容的机会。
专门为解决您的问题,使用Chance就像这样简单:
// One line!
var uniques = chance.unique(chance.natural, 8, {min: 1, max: 100});
// Print it out to the document for this snippet so we can see it in action
document.write(JSON.stringify(uniques));
<script src="http://chancejs.com/chance.min.js"></script>
免责声明,作为Chance的作者,我有点偏颇;)
var codes = chance.unique(chance.string, 8)
您需要从特定字符池中提取的代码,就可以这样指定:chance.unique(chance.string, 8, {pool: "abcd1234"})
其中abcd1234可以是池中想要的任何字符。参见偶然机会js.com/#string
chance.string({ length: 8 })
,如果您只希望某些字符出现在该字符串中,chance.string({ pool: 'abcd1234', length: 8 })
则它将从字符abcd1234返回一个随机的8个字符串,例如“ 2c2c44bc”或“ 331141cc”
为了避免长时间不可靠的洗牌,我将执行以下操作...
瞧-没有重复的数字。
如果有人感兴趣,我可能会在以后发布一些实际代码。
编辑:这可能是我的竞争优势,但是在看完@Alsciende的帖子后,我忍不住发布了我承诺的代码。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>8 unique random number between 1 and 100</title>
<script type="text/javascript" language="Javascript">
function pick(n, min, max){
var values = [], i = max;
while(i >= min) values.push(i--);
var results = [];
var maxIndex = max;
for(i=1; i <= n; i++){
maxIndex--;
var index = Math.floor(maxIndex * Math.random());
results.push(values[index]);
values[index] = values[maxIndex];
}
return results;
}
function go(){
var running = true;
do{
if(!confirm(pick(8, 1, 100).sort(function(a,b){return a - b;}))){
running = false;
}
}while(running)
}
</script>
</head>
<body>
<h1>8 unique random number between 1 and 100</h1>
<p><button onclick="go()">Click me</button> to start generating numbers.</p>
<p>When the numbers appear, click OK to generate another set, or Cancel to stop.</p>
</body>
另一种方法是生成一个100个具有升序编号的数组并将其随机排序。这实际上导致了一个非常简短的片段(在我看来)。
const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));
sort
的实现很好,我敢肯定)。
我会这样做:
function randomInt(min, max) {
return Math.round(min + Math.random()*(max-min));
}
var index = {}, numbers = [];
for (var i=0; i<8; ++i) {
var number;
do {
number = randomInt(1, 100);
} while (index.hasOwnProperty("_"+number));
index["_"+number] = true;
numbers.push(number);
}
delete index;
这是我编写的非常通用的函数,用于为数组生成随机的唯一/非唯一整数。假定在这种情况下该答案的最后一个参数为true。
/* Creates an array of random integers between the range specified
len = length of the array you want to generate
min = min value you require
max = max value you require
unique = whether you want unique or not (assume 'true' for this answer)
*/
function _arrayRandom(len, min, max, unique) {
var len = (len) ? len : 10,
min = (min !== undefined) ? min : 1,
max = (max !== undefined) ? max : 100,
unique = (unique) ? unique : false,
toReturn = [], tempObj = {}, i = 0;
if(unique === true) {
for(; i < len; i++) {
var randomInt = Math.floor(Math.random() * ((max - min) + min));
if(tempObj['key_'+ randomInt] === undefined) {
tempObj['key_'+ randomInt] = randomInt;
toReturn.push(randomInt);
} else {
i--;
}
}
} else {
for(; i < len; i++) {
toReturn.push(Math.floor(Math.random() * ((max - min) + min)));
}
}
return toReturn;
}
这里的'tempObj'是一个非常有用的obj,因为生成的每个随机数都会直接在此tempObj中检查该键是否已经存在,否则,我们将i减一,因为当前的随机数已经存在,因此需要额外运行1次。
您的情况下,请运行以下命令
_arrayRandom(8, 1, 100, true);
就这样。
min = (min) ? min : 1,
始终返回1。(因此永远不会选择0)
将数字从1改到100是正确的基本策略,但是如果您只需要8个改组的数字,则无需对所有100个数字进行改组。
我不太了解Javascript,但是我相信快速创建100个null数组很容易。然后,在8个回合中,将数组的第n个元素(n从0开始)与从n + 1到99的随机选择的元素交换。当然,任何未填充的元素都意味着该元素实际上已经原始索引加1,因此可以忽略不计。完成8回合后,数组的前8个元素将具有8个随机数字。
与The Machine Charmer相同的置换算法,但具有原型实现。更适合大量选秀权。使用js 1.7解构分配(如果有)。
// swaps elements at index i and j in array this
// swapping is easy on js 1.7 (feature detection)
Array.prototype.swap = (function () {
var i=0, j=1;
try { [i,j]=[j,i]; }
catch (e) {}
if(i) {
return function(i,j) {
[this[i],this[j]] = [this[j],this[i]];
return this;
}
} else {
return function(i,j) {
var temp = this[i];
this[i] = this[j];
this[j] = temp;
return this;
}
}
})();
// shuffles array this
Array.prototype.shuffle = function() {
for(var i=this.length; i>1; i--) {
this.swap(i-1, Math.floor(i*Math.random()));
}
return this;
}
// returns n unique random numbers between min and max
function pick(n, min, max) {
var a = [], i = max;
while(i >= min) a.push(i--);
return a.shuffle().slice(0,n);
}
pick(8,1,100);
编辑:基于belugabob的答案的另一个命题,更适合少量选择。为了保证唯一性,我们从数组中删除了选取的数字。
// removes n random elements from array this
// and returns them
Array.prototype.pick = function(n) {
if(!n || !this.length) return [];
var i = Math.floor(this.length*Math.random());
return this.splice(i,1).concat(this.pick(n-1));
}
// returns n unique random numbers between min and max
function pick(n, min, max) {
var a = [], i = max;
while(i >= min) a.push(i--);
return a.pick(n);
}
pick(8,1,100);
对于带有这样的孔的阵列 [,2,,4,,6,7,,]
此类孔的因为我的问题是填充这些孔。所以我根据需要修改了它:)
以下修改后的解决方案为我工作:)
var arr = [,2,,4,,6,7,,]; //example
while(arr.length < 9){
var randomnumber=Math.floor(Math.random()*9+1);
var found=false;
for(var i=0;i<arr.length;i++){
if(arr[i]==randomnumber){found=true;break;}
}
if(!found)
for(k=0;k<9;k++)
{if(!arr[k]) //if it's empty !!MODIFICATION
{arr[k]=randomnumber; break;}}
}
alert(arr); //outputs on the screen
最好的较早答案是的答案sje397
。您将尽快获得尽可能好的随机数。
我的解决方案与他的解决方案非常相似。但是,有时您希望随机数按随机顺序排列,这就是为什么我决定发布答案的原因。另外,我提供了一般功能。
function selectKOutOfN(k, n) {
if (k>n) throw "k>n";
var selection = [];
var sorted = [];
for (var i = 0; i < k; i++) {
var rand = Math.floor(Math.random()*(n - i));
for (var j = 0; j < i; j++) {
if (sorted[j]<=rand)
rand++;
else
break;
}
selection.push(rand);
sorted.splice(j, 0, rand);
}
return selection;
}
alert(selectKOutOfN(8, 100));
这是我拼凑的ES6版本。我敢肯定它可以更巩固一点。
function randomArray(i, min, max) {
min = Math.ceil(min);
max = Math.floor(max);
let arr = Array.from({length: i}, () => Math.floor(Math.random()* (max - min)) + min);
return arr.sort();
}
let uniqueItems = [...new Set(randomArray(8, 0, 100))]
console.log(uniqueItems);
如何将对象属性用作哈希表?这样,最好的情况是只随机化8次。仅当您希望数字范围的一小部分时才有效。它也比Fisher-Yates的内存占用少得多,因为您不必为数组分配空间。
var ht={}, i=rands=8;
while ( i>0 || keys(ht).length<rands) ht[Math.ceil(Math.random()*100)]=i--;
alert(keys(ht));
然后我发现Object.keys(obj)是ECMAScript 5功能,因此上述内容在互联网上现在几乎没有用。不用担心,因为我通过添加这样的键功能使它兼容ECMAScript 3。
if (typeof keys == "undefined")
{
var keys = function(obj)
{
props=[];
for (k in ht) if (ht.hasOwnProperty(k)) props.push(k);
return props;
}
}
如果需要更多唯一性,则必须生成一个array(1..100)。
var arr=[];
function generateRandoms(){
for(var i=1;i<=100;i++) arr.push(i);
}
function extractUniqueRandom()
{
if (arr.length==0) generateRandoms();
var randIndex=Math.floor(arr.length*Math.random());
var result=arr[randIndex];
arr.splice(randIndex,1);
return result;
}
function extractUniqueRandomArray(n)
{
var resultArr=[];
for(var i=0;i<n;i++) resultArr.push(extractUniqueRandom());
return resultArr;
}
上面的代码更快:
extractUniqueRandomArray(50)=> [2,79,38,59,63,42,52,22,78,50,39,77,1,88,40,23,48,84,91, 49、4、54、93、36、100、82、62、41、89、12、24、31、86、92、64、75、70、61、67、98、76、80、56、90, 83、44、43、47、7、53]
使用JavaScript 1.6 indexOf函数添加相同代码的另一个更好版本(可接受的答案)。不需要在每次检查重复项时都遍历整个数组。
var arr = []
while(arr.length < 8){
var randomnumber=Math.ceil(Math.random()*100)
var found=false;
if(arr.indexOf(randomnumber) > -1){found=true;}
if(!found)arr[arr.length]=randomnumber;
}
旧版本的Javascript仍可以使用顶部的版本
附言:试图建议对Wiki进行更新,但遭到拒绝。我仍然认为这可能对其他人有用。
这是我的个人解决方案:
<script>
var i, k;
var numbers = new Array();
k = Math.floor((Math.random()*8));
numbers[0]=k;
for (var j=1;j<8;j++){
k = Math.floor((Math.random()*8));
i=0;
while (i < numbers.length){
if (numbers[i] == k){
k = Math.floor((Math.random()*8));
i=0;
}else {i++;}
}
numbers[j]=k;
}
for (var j=0;j<8;j++){
alert (numbers[j]);
}
</script>
它随机生成8个唯一的数组值(介于0和7之间),然后使用警报框显示它们。
function getUniqueRandomNos() {
var indexedArrayOfRandomNo = [];
for (var i = 0; i < 100; i++) {
var randNo = Math.random();
indexedArrayOfRandomNo.push([i, randNo]);
}
indexedArrayOfRandomNo.sort(function (arr1, arr2) {
return arr1[1] - arr2[1]
});
var uniqueRandNoArray = [];
for (i = 0; i < 8; i++) {
uniqueRandNoArray.push(indexedArrayOfRandomNo[i][0]);
}
return uniqueRandNoArray;
}
我认为这种方法与大多数答案中给出的方法不同,所以我想我可以在这里添加一个答案(尽管这个问题是4年前提出的)。
我们生成100个随机数,并使用1到100的数字对其进行标记。然后,对这些标记的随机数进行排序,然后随机地对这些标记进行混洗。或者,根据这个问题的需要,可以只查找标记的随机数的前8个就可以了。查找前8个项目比对整个数组进行排序要便宜。
这里必须注意,排序算法会影响该算法。如果使用的排序算法是稳定的,则偏向于较小的数字。理想情况下,我们希望排序算法不稳定,甚至不偏重于稳定性(或不稳定性),以产生具有完全一致的概率分布的答案。
最多可以处理20位UNIQUE随机数
JS
var generatedNumbers = [];
function generateRandomNumber(precision) { // input --> number precision in integer
if (precision <= 20) {
var randomNum = Math.round(Math.random().toFixed(precision) * Math.pow(10, precision));
if (generatedNumbers.indexOf(randomNum) > -1) {
if (generatedNumbers.length == Math.pow(10, precision))
return "Generated all values with this precision";
return generateRandomNumber(precision);
} else {
generatedNumbers.push(randomNum);
return randomNum;
}
} else
return "Number Precision shoould not exceed 20";
}
generateRandomNumber(1);
该解决方案使用的散列比检查其是否驻留在数组中的性能要高出O(1)。它也有额外的安全检查。希望能帮助到你。
function uniqueArray(minRange, maxRange, arrayLength) {
var arrayLength = (arrayLength) ? arrayLength : 10
var minRange = (minRange !== undefined) ? minRange : 1
var maxRange = (maxRange !== undefined) ? maxRange : 100
var numberOfItemsInArray = 0
var hash = {}
var array = []
if ( arrayLength > (maxRange - minRange) ) throw new Error('Cannot generate unique array: Array length too high')
while(numberOfItemsInArray < arrayLength){
// var randomNumber = Math.floor(Math.random() * (maxRange - minRange + 1) + minRange)
// following line used for performance benefits
var randomNumber = (Math.random() * (maxRange - minRange + 1) + minRange) << 0
if (!hash[randomNumber]) {
hash[randomNumber] = true
array.push(randomNumber)
numberOfItemsInArray++
}
}
return array
}
document.write(uniqueArray(1, 100, 8))
将其实现为生成器使其非常好用。请注意,此实现不同于要求首先对整个输入数组进行混洗的实现。
此
sample
函数懒惰地工作,每次迭代给您1个随机项,最多为N
您要的项。这很不错,因为如果您只想要1000个列表中的3个项目,则不必先触摸全部1000个项目。
// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
let ys = xs.slice(0);
let len = xs.length;
while (n > 0 && len > 0) {
let i = (Math.random() * len) >> 0;
yield ys.splice(i,1)[0];
n--; len--;
}
}
// example inputs
let items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// get 3 random items
for (let i of sample(3) (items))
console.log(i); // f g c
// partial application
const lotto = sample(3);
for (let i of lotto(numbers))
console.log(i); // 3 8 7
// shuffle an array
const shuffle = xs => Array.from(sample (Infinity) (xs))
console.log(shuffle(items)) // [b c g f d e a]
我选择以sample
不改变输入数组的方式实现,但是您可以轻松地认为,实现变异是有利的。
例如,该shuffle
函数可能希望更改原始输入数组。或者,您可能希望在不同的时间从相同的输入中进行采样,从而每次都更新输入。
// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
let len = xs.length;
while (n > 0 && len > 0) {
let i = (Math.random() * len) >> 0;
yield xs.splice(i,1)[0];
n--; len--;
}
}
// deal :: [Card] -> [Card]
const deal = xs => Array.from(sample (2) (xs));
// setup a deck of cards (13 in this case)
// cards :: [Card]
let cards = 'A234567890JQK'.split('');
// deal 6 players 2 cards each
// players :: [[Card]]
let players = Array.from(Array(6), $=> deal(cards))
console.log(players);
// [K, J], [6, 0], [2, 8], [Q, 7], [5, 4], [9, A]
// `cards` has been mutated. only 1 card remains in the deck
console.log(cards);
// [3]
sample
由于数组输入突变,它不再是纯函数,但在某些情况下(如上所示),它可能更有意义。
我选择生成器而不是仅返回数组的函数的另一个原因是,您可能要继续采样直到某些特定条件。
也许我想要1,000,000个随机数列表中的第一个素数。
因为我们正在使用发电机,所以此任务很简单
const randomPrimeNumber = listOfNumbers => {
for (let x of sample(Infinity) (listOfNumbers)) {
if (isPrime(x))
return x;
}
return NaN;
}
这将一次连续采样一个随机数x
,检查其是否为质数,然后返回x
为质数。如果在找到素数之前已用尽数字列表,NaN
则返回。
注意:
此答案最初是在另一个问题上共享的,该问题已作为该问题的重复而关闭。由于它与此处提供的其他解决方案非常不同,因此我决定在此处也共享
getRandom (min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
getNRandom (min, max, n) {
const numbers = []
if (min > max) {
return new Error('Max is gt min')
}
if (min === max) {
return [min]
}
if ((max - min) >= n) {
while (numbers.length < n) {
let rand = this.getRandom(min, max + 1)
if (numbers.indexOf(rand) === -1) {
numbers.push(rand)
}
}
}
if ((max - min) < n) {
for (let i = min; i <= max; i++) {
numbers.push(i)
}
}
return numbers
}
使用a Set
是最快的选择。这是获取使用回调生成器的唯一随机数的通用函数。现在它是快速且可重用的。
// Get a unique 'anything'
let unique = new Set()
function getUnique(generator) {
let number = generator()
while (!unique.add(number)) {
number = generator()
}
return number;
}
// The generator. Return anything, not just numbers.
const between_1_100 = () => 1 + Math.floor(Math.random() * 100)
// Test it
for (var i = 0; i < 8; i++) {
const aNumber = getUnique(between_1_100)
}
// Dump the 'stored numbers'
console.log(Array.from(unique))
这是Fisher Yates / Durstenfeld Shuffle的一种实现 ,但是当拾取大小与可用元素数量相比较小时,无需实际创建数组,从而降低了空间复杂性或所需的内存。
要从100个数字中选择8个数字,就不必创建100个元素的数组。
假设创建了一个数组,
rnd
1到100之间的随机数() rnd
如果未创建数组,则可以使用hashMap记住实际交换的位置。当生成的第二随机数等于先前生成的数之一时,映射将提供该位置的当前值,而不是实际值。
const getRandom_ = (start, end) => {
return Math.floor(Math.random() * (end - start + 1)) + start;
};
const getRealValue_ = (map, rnd) => {
if (map.has(rnd)) {
return getRealValue_(map, map.get(rnd));
} else {
return rnd;
}
};
const getRandomNumbers = (n, start, end) => {
const out = new Map();
while (n--) {
const rnd = getRandom_(start, end--);
out.set(getRealValue_(out, rnd), end + 1);
}
return [...out.keys()];
};
console.info(getRandomNumbers(8, 1, 100));
console.info(getRandomNumbers(8, 1, Math.pow(10, 12)));
console.info(getRandomNumbers(800000, 1, Math.pow(10, 15)));
您也可以像这样用一根衬纸来做:
[...((add, set) => add(set, add))((set, add) => set.size < 8 ? add(set.add(Math.floor(Math.random()*100) + 1), add) : set, new Set())]