Como posso gerar alguns números aleatórios exclusivos entre 1 e 100 usando JavaScript?
Como posso gerar alguns números aleatórios exclusivos entre 1 e 100 usando JavaScript?
Respostas:
Por exemplo: Para gerar 8 números aleatórios exclusivos e armazená-los em uma matriz, você pode simplesmente fazer isso:
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)
. Se the Math.random()
acidentalmente retornar 0, o Math.ceil(0)
também será 0, embora a chance seja baixa.
randlines file | head -10
.
Gerar permutação de 100 números e depois escolha em série.
Use o algoritmo Knuth Shuffle (também conhecido como 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;
}
}
EDITAR :
Código aprimorado:
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);
}
Problema potencial:
Suponha que temos um array de 100 números {por exemplo, [1,2,3 ... 100]} e paramos de trocar após 8 trocas; então, na maioria das vezes, a matriz será semelhante a {1,2,3,76,5,6,7,8, ... os números aqui serão embaralhados ... 10}.
Porque todos os números serão trocados com probabilidade 1/100, então prob. de trocar os primeiros 8 números é 8/100, enquanto prob. de trocar outros 92 é 92/100.
Mas se executarmos o algoritmo para o array completo, teremos certeza de que (quase) todas as entradas são trocadas.
Caso contrário, nos deparamos com uma pergunta: quais 8 números escolher?
Solução JS moderna usando Set (e caso médio 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
em JS! Porém, essa solução não causaria geração desnecessária de números até que um se encaixasse no requisito de unicidade, especialmente nas últimas iterações, se 8 fosse mais próximo de 100? Assim, acho que prefiro a também elegante resposta sort
abaixo.
As técnicas acima são boas se você quiser evitar uma biblioteca, mas dependendo se você não tiver problemas com uma biblioteca, eu sugiro que você dê uma olhada no Chance para gerar coisas aleatórias em JavaScript.
Especificamente para resolver sua dúvida, usar o Chance é tão fácil quanto:
// 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>
Isenção de responsabilidade, como autor de Chance, sou um pouco tendencioso;)
var codes = chance.unique(chance.string, 8)
Se você precisa dos códigos extraídos de um pool de caracteres específico, pode especificar assim: chance.unique(chance.string, 8, {pool: "abcd1234"})
onde abcd1234 pode ser qualquer caractere que você quiser no pool. Veja chancejs.com/#string
chance.string({ length: 8 })
e se você quiser que apenas alguns caracteres apareçam nessa string, chance.string({ pool: 'abcd1234', length: 8 })
que retornaria uma string aleatória de 8 caracteres dos caracteres abcd1234, por exemplo "2c2c44bc" ou "331141cc"
Para evitar embaralhamento longo e não confiável, eu faria o seguinte ...
Voila - sem números repetidos.
Posso postar algum código real mais tarde, se alguém estiver interessado.
Edit: É provavelmente a minha veia competitiva, mas, tendo visto o post de @Alsciende, não pude resistir a postar o código que prometi.
<!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>
Outra abordagem é gerar uma matriz de 100 itens com números crescentes e classificá-la aleatoriamente. Na verdade, isso leva a um trecho muito curto e (na minha opinião) simples.
const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));
sort
seja bem implementado, o que tenho certeza que é).
Eu faria isso:
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;
Esta é uma função muito genérica que escrevi para gerar inteiros únicos / não únicos aleatórios para uma matriz. Suponha que o último parâmetro seja verdadeiro neste cenário para esta resposta.
/* 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;
}
Aqui, o 'tempObj' é um obj muito útil uma vez que cada número aleatório gerado irá verificar diretamente neste tempObj se essa chave já existe, se não, então reduzimos o i em um, pois precisamos de 1 execução extra, uma vez que o número aleatório atual já existe .
No seu caso, execute o seguinte
_arrayRandom(8, 1, 100, true);
Isso é tudo.
min = (min) ? min : 1,
sempre retornará 1. (portanto, 0 nunca será selecionado)
Embaralhar os números de 1 a 100 é a estratégia básica correta, mas se você precisar de apenas 8 números embaralhados, não há necessidade de embaralhar todos os 100 números.
Não conheço Javascript muito bem, mas acredito que seja fácil criar um array de 100 nulos rapidamente. Então, por 8 rodadas, você troca o enésimo elemento da matriz (n começando em 0) por um elemento selecionado aleatoriamente de n + 1 a 99. Claro, quaisquer elementos não preenchidos ainda significam que o elemento realmente teria sido o índice original mais 1, então isso é trivial para fatorar. Quando você terminar com as 8 rodadas, os primeiros 8 elementos do seu array terão seus 8 números embaralhados.
Mesmo algoritmo de permutação que The Machine Charmer, mas com uma implementação prototipada. Mais adequado para um grande número de escolhas. Usa atribuição de desestruturação js 1.7, se disponível.
// 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);
Edit: Outra proposição, mais adequada para um pequeno número de escolhas, com base na resposta do belugabob. Para garantir a exclusividade, removemos os números escolhidos da matriz.
// 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);
para matrizes com buracos como este [,2,,4,,6,7,,]
porque meu problema era preencher esses buracos. Então eu modifiquei de acordo com minha necessidade :)
a seguinte solução modificada funcionou para mim :)
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
A melhor resposta inicial é responder por sje397
. Você obterá os melhores números aleatórios que puder, o mais rápido possível.
Minha solução é muito semelhante à solução dele. No entanto, às vezes você quer os números aleatórios em ordem aleatória, e é por isso que decidi postar uma resposta. Além disso, forneço uma função geral.
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));
Aqui está minha versão do ES6 que construí. Tenho certeza que pode ser um pouco mais consolidado.
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);
Que tal usar as propriedades do objeto como uma tabela hash ? Dessa forma, seu melhor cenário é randomizar apenas 8 vezes. Só será eficaz se você quiser uma pequena parte do intervalo de números. Também consome muito menos memória do que Fisher-Yates, pois você não precisa alocar espaço para um array.
var ht={}, i=rands=8;
while ( i>0 || keys(ht).length<rands) ht[Math.ceil(Math.random()*100)]=i--;
alert(keys(ht));
Então descobri que Object.keys (obj) é um recurso ECMAScript 5, então o acima é praticamente inútil na internet agora. Não tenha medo, porque eu o tornei compatível com o ECMAScript 3 adicionando uma função de teclas como esta.
if (typeof keys == "undefined")
{
var keys = function(obj)
{
props=[];
for (k in ht) if (ht.hasOwnProperty(k)) props.push(k);
return props;
}
}
se precisar de mais exclusivo, você deve gerar uma matriz (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;
}
o código acima é mais rápido:
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]
Adicionando outra versão melhor do mesmo código (resposta aceita) com a função indexOf do JavaScript 1.6. Não é necessário fazer um loop por todo o array toda vez que você estiver verificando a duplicata.
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;
}
A versão mais antiga do Javascript ainda pode usar a versão superior
PS: Tentei sugerir uma atualização para o wiki, mas foi rejeitada. Ainda acho que pode ser útil para outros.
Esta é a minha solução pessoal:
<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>
Ele gera aleatoriamente 8 valores de array exclusivos (entre 0 e 7) e os exibe usando uma caixa de alerta.
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;
}
Acho que esse método é diferente dos métodos dados na maioria das respostas, então pensei em adicionar uma resposta aqui (embora a pergunta tenha sido feita há 4 anos).
Geramos 100 números aleatórios e marcamos cada um deles com números de 1 a 100. Em seguida, classificamos esses números aleatórios marcados e as marcações são embaralhadas aleatoriamente. Como alternativa, conforme necessário nesta pergunta, pode-se dispensar apenas encontrar os 8 primeiros números aleatórios marcados. Encontrar os 8 itens principais é mais barato do que classificar toda a variedade.
Deve-se notar aqui que o algoritmo de classificação influencia este algoritmo. Se o algoritmo de classificação usado for estável, há uma ligeira tendência a favor de números menores. Idealmente, gostaríamos que o algoritmo de classificação fosse instável e nem mesmo inclinado para a estabilidade (ou instabilidade) para produzir uma resposta com distribuição de probabilidade perfeitamente uniforme.
Isso pode lidar com a geração de número aleatório UNIQUE de até 20 dígitos
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);
Esta solução usa o hash, que tem muito mais desempenho O (1) do que verificar se reside no array. Ele tem verificações de segurança extras também. Espero que ajude.
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))
Implementar isso como um gerador torna muito bom trabalhar com ele. Observe que essa implementação difere daquelas que exigem que toda a matriz de entrada seja embaralhada primeiro.
Esta
sample
função funciona preguiçosamente, dando a você 1 item aleatório por iteração até osN
itens que você solicitar. Isso é bom porque se você quiser apenas 3 itens de uma lista de 1000 , não precisará tocar em todos os 1000 itens primeiro.
// 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]
Eu escolhi implementar sample
de uma forma que não modifique o array de entrada, mas você poderia facilmente argumentar que uma implementação mutante é favorável.
Por exemplo, a shuffle
função pode desejar alterar a matriz de entrada original. Ou você pode desejar amostrar da mesma entrada em vários momentos, atualizando a entrada a cada vez.
// 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
não é mais uma função pura por causa da mutação de entrada do array, mas em certas circunstâncias (demonstradas acima) pode fazer mais sentido.
Outra razão pela qual escolhi um gerador em vez de uma função que apenas retorna um array é porque você pode querer continuar a amostragem até alguma condição específica.
Talvez eu queira o primeiro número primo de uma lista de 1.000.000 de números aleatórios.
Como estamos trabalhando com um gerador, essa tarefa é trivial
const randomPrimeNumber = listOfNumbers => {
for (let x of sample(Infinity) (listOfNumbers)) {
if (isPrime(x))
return x;
}
return NaN;
}
Isso irá amostrar continuamente 1 número aleatório de cada vez x
, verificar se é primo e retornar x
se for. Se a lista de números se esgotar antes que um primo seja encontrado, NaN
será retornado.
Nota:
Esta resposta foi compartilhada originalmente em outra pergunta que foi fechada como uma duplicata desta. Por ser muito diferente das outras soluções fornecidas aqui, decidi compartilhá-lo aqui também
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
}
Usar a Set
é a opção mais rápida. Aqui está uma função genérica para obter um aleatório exclusivo que usa um gerador de callback. Agora é rápido e reutilizável .
// 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))
Esta é uma implementação do Fisher Yates / Durstenfeld Shuffle , mas sem a criação real de um array, reduzindo assim a complexidade do espaço ou da memória necessária, quando o tamanho da seleção é pequeno em comparação com o número de elementos disponíveis.
Para escolher 8 números de 100, não é necessário criar uma matriz de 100 elementos.
Supondo que uma matriz seja criada,
rnd
) de 1 a 100 rnd
Se uma matriz não for criada, um hashMap pode ser usado para lembrar as posições reais trocadas. Quando o segundo número aleatório gerado é igual a um dos números gerados anteriormente, o mapa fornece o valor atual naquela posição ao invés do valor real.
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)));
Aqui está um exemplo de 5 números aleatórios retirados de um intervalo de 0 a 100 (ambos 0 e 100 incluídos) sem duplicação.
let finals = [];
const count = 5; // Considering 5 numbers
const max = 100;
for(let i = 0; i < max; i++){
const rand = Math.round(Math.random() * max);
!finals.includes(rand) && finals.push(rand)
}
finals = finals.slice(0, count)
Você também pode fazer isso com um revestimento como este:
[...((add, set) => add(set, add))((set, add) => set.size < 8 ? add(set.add(Math.floor(Math.random()*100) + 1), add) : set, new Set())]