¿Cómo puedo generar algunos números aleatorios únicos entre 1 y 100 usando JavaScript?
¿Cómo puedo generar algunos números aleatorios únicos entre 1 y 100 usando JavaScript?
Respuestas:
Por ejemplo: para generar 8 números aleatorios únicos y almacenarlos en una matriz, simplemente puede hacer esto:
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)
. Si the Math.random()
accidentalmente devuelve 0, Math.ceil(0)
también es 0, aunque la probabilidad es baja.
randlines file | head -10
.
Genere la permutación de 100 números y luego elija en serie.
Utilice el algoritmo Knuth Shuffle (también conocido 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 mejorado:
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:
Supongamos que tenemos una matriz de 100 números {por ejemplo, [1,2,3 ... 100]} y dejamos de intercambiar después de 8 intercambios; entonces la mayoría de las veces la matriz se verá como {1,2,3,76,5,6,7,8, ... los números aquí se mezclarán ... 10}.
Porque todos los números se intercambiarán con una probabilidad de 1/100, así que prob. de intercambiar los primeros 8 números es 8/100 mientras que prob. de intercambiar otros 92 es 92/100.
Pero si ejecutamos el algoritmo para una matriz completa, entonces estamos seguros de que (casi) todas las entradas se intercambian.
De lo contrario nos enfrentamos a una pregunta: ¿qué 8 números elegir?
Solución JS moderna usando Set (y caso promedio 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
en JS! Sin embargo, ¿no causaría esta solución una generación innecesaria de números hasta que uno cumpla con el requisito de unicidad, especialmente en las últimas iteraciones, si 8 estuviera más cerca de 100? Por lo tanto, creo que prefiero la respuesta también elegante a sort
continuación.
Las técnicas anteriores son buenas si desea evitar una biblioteca, pero dependiendo de si estaría bien con una biblioteca, le sugiero que consulte Chance para generar material aleatorio en JavaScript.
Específicamente para resolver su pregunta, usar Chance es tan fácil como:
// 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>
Descargo de responsabilidad, como autor de Chance, soy un poco parcial;)
var codes = chance.unique(chance.string, 8)
Si necesitas los códigos extraídos de un grupo de caracteres en particular, puedes especificarlo de esta manera: chance.unique(chance.string, 8, {pool: "abcd1234"})
donde abcd1234 puede ser cualquier carácter que quieras en el grupo. Ver chancejs.com/#string
chance.string({ length: 8 })
y si solo quieres que aparezcan ciertos caracteres en esa cadena, chance.string({ pool: 'abcd1234', length: 8 })
que devolvería una cadena aleatoria de 8 caracteres de los caracteres abcd1234, por ejemplo, "2c2c44bc" o "331141cc"
Para evitar cambios largos y poco fiables, haría lo siguiente ...
Voila, no hay números repetidos.
Puedo publicar algún código real más tarde, si alguien está interesado.
Editar: Probablemente sea la racha competitiva en mí, pero, habiendo visto la publicación de @Alsciende, no pude resistirme a publicar el código que prometí.
<!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>
Otro enfoque es generar una matriz de 100 elementos con números ascendentes y ordenarlos al azar. En realidad, esto conduce a un fragmento muy corto y (en mi opinión) simple.
const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));
sort
esté bien implementado, que estoy seguro).
Yo haría esto:
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 es una función muy genérica que he escrito para generar enteros aleatorios únicos / no únicos para una matriz. Suponga que el último parámetro es verdadero en este escenario para esta respuesta.
/* 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;
}
Aquí el 'tempObj' es un obj muy útil ya que cada número aleatorio generado verificará directamente en este tempObj si esa clave ya existe, si no, entonces reducimos la i en uno ya que necesitamos 1 ejecución adicional ya que el número aleatorio actual ya existe .
En su caso, ejecute lo siguiente
_arrayRandom(8, 1, 100, true);
Eso es todo.
min = (min) ? min : 1,
siempre devolverá 1. (por lo que nunca se seleccionará 0)
Mezclar los números del 1 al 100 es la estrategia básica correcta, pero si solo necesita 8 números mezclados, no es necesario mezclar los 100 números.
No conozco muy bien Javascript, pero creo que es fácil crear una matriz de 100 nulos rápidamente. Luego, durante 8 rondas, intercambia el n-ésimo elemento de la matriz (n comenzando en 0) con un elemento seleccionado al azar de n + 1 a 99. Por supuesto, cualquier elemento que aún no se haya completado significa que el elemento realmente habría sido el índice original más 1, por lo que es trivial factorizar. Cuando haya terminado con las 8 rondas, los primeros 8 elementos de su matriz tendrán sus 8 números mezclados.
El mismo algoritmo de permutación que The Machine Charmer, pero con una implementación prototipada. Se adapta mejor a una gran cantidad de picos. Utiliza la asignación de desestructuración js 1.7 si está disponible.
// 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);
Editar: Otra propuesta, más adecuada para una pequeña cantidad de selecciones, basada en la respuesta de belugabob. Para garantizar la unicidad, eliminamos los números seleccionados de la 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 matrices con agujeros como este [,2,,4,,6,7,,]
porque mi problema era llenar estos agujeros. Así que lo modifiqué según mi necesidad :)
la siguiente solución modificada funcionó para mí :)
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
La mejor respuesta anterior es la respuesta de sje397
. Obtendrá los mejores números aleatorios que pueda obtener, lo más rápido posible.
Mi solución es muy similar a su solución. Sin embargo, a veces quieres los números aleatorios en orden aleatorio, y es por eso que decidí publicar una respuesta. Además, proporciono una función general.
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));
Aquí está mi versión ES6 que improvisé. Seguro que se puede consolidar un poco más.
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);
¿Qué tal usar las propiedades del objeto como una tabla hash ? De esta manera, su mejor escenario es aleatorizar solo 8 veces. Solo sería efectivo si desea una pequeña parte del rango de números. También requiere mucha menos memoria que Fisher-Yates porque no tiene que asignar espacio para una matriz.
var ht={}, i=rands=8;
while ( i>0 || keys(ht).length<rands) ht[Math.ceil(Math.random()*100)]=i--;
alert(keys(ht));
Luego descubrí que Object.keys (obj) es una característica de ECMAScript 5, por lo que lo anterior es prácticamente inútil en Internet en este momento. No temas, porque lo hice compatible con ECMAScript 3 agregando una función 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;
}
}
si necesita más exclusivo, debe generar una 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;
}
El código anterior es más 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]
Añadiendo otra versión mejor del mismo código (respuesta aceptada) con la función indexOf de JavaScript 1.6. No es necesario recorrer toda la matriz cada vez que verifica el duplicado.
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;
}
La versión anterior de Javascript aún puede usar la versión en la parte superior
PD: Intenté sugerir una actualización de la wiki pero fue rechazada. Sigo pensando que puede ser útil para otros.
Esta es mi solución personal:
<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>
Genera aleatoriamente 8 valores de matriz únicos (entre 0 y 7), luego los muestra usando un cuadro 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;
}
Creo que este método es diferente de los métodos dados en la mayoría de las respuestas, así que pensé que podría agregar una respuesta aquí (aunque la pregunta se hizo hace 4 años).
Generamos 100 números aleatorios y etiquetamos cada uno de ellos con números del 1 al 100. Luego clasificamos estos números aleatorios etiquetados y las etiquetas se mezclan aleatoriamente. Alternativamente, según sea necesario en esta pregunta, se podría eliminar simplemente encontrar los 8 primeros números aleatorios etiquetados. Encontrar los 8 elementos principales es más barato que ordenar toda la matriz.
Hay que señalar aquí que el algoritmo de clasificación influye en este algoritmo. Si el algoritmo de clasificación utilizado es estable, existe un ligero sesgo a favor de números más pequeños. Idealmente, querríamos que el algoritmo de clasificación fuera inestable y ni siquiera sesgado hacia la estabilidad (o inestabilidad) para producir una respuesta con una distribución de probabilidad perfectamente uniforme.
Esto puede manejar la generación de un número aleatorio ÚNICO de hasta 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 solución utiliza el hash, que es mucho más eficaz O (1) que comprobar si reside en la matriz. También tiene controles de seguridad adicionales. Espero eso ayude.
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 esto como un generador hace que sea bastante agradable trabajar con él. Tenga en cuenta que esta implementación difiere de las que requieren que la matriz de entrada completa se mezcle primero.
Esta
sample
función funciona de manera perezosa, lo que le brinda 1 elemento aleatorio por iteración hasta losN
elementos que solicita. Esto es bueno porque si solo desea 3 elementos de una lista de 1000 , no tiene que tocar los 1000 elementos primero.
// 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]
Elegí implementar sample
de una manera que no mute la matriz de entrada, pero se podría argumentar fácilmente que una implementación mutante es favorable.
Por ejemplo, es posible que la shuffle
función desee mutar la matriz de entrada original. O tal vez desee tomar muestras de la misma entrada en varios momentos, actualizando la entrada 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
ya no es una función pura debido a la mutación de entrada de la matriz, pero en ciertas circunstancias (como se demostró anteriormente) podría tener más sentido.
Otra razón por la que elegí un generador en lugar de una función que solo devuelve una matriz es porque es posible que desee continuar muestreando hasta alguna condición específica.
Quizás quiero el primer número primo de una lista de 1.000.000 de números aleatorios.
Como estamos trabajando con un generador, esta tarea es trivial.
const randomPrimeNumber = listOfNumbers => {
for (let x of sample(Infinity) (listOfNumbers)) {
if (isPrime(x))
return x;
}
return NaN;
}
Esto muestreará continuamente 1 número aleatorio a la vez x
, verificará si es primo y luego devolverá x
si lo es. Si la lista de números se agota antes de encontrar un primo, NaN
se devuelve.
Nota:
Esta respuesta se compartió originalmente en otra pregunta que se cerró como un duplicado de esta. Debido a que es muy diferente de las otras soluciones proporcionadas aquí, he decidido compartirlo aquí también.
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
es la opción más rápida. Aquí hay una función genérica para obtener un aleatorio único que usa un generador de devolución de llamada. Ahora es rápido y reutilizable .
// 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 es una implementación de Fisher Yates / Durstenfeld Shuffle , pero sin la creación real de una matriz, lo que reduce la complejidad del espacio o la memoria necesaria, cuando el tamaño de selección es pequeño en comparación con la cantidad de elementos disponibles.
Para elegir 8 números de 100, no es necesario crear una matriz de 100 elementos.
Suponiendo que se crea una matriz,
rnd
) de 1 a 100 rnd
Si no se crea una matriz, se puede usar un hashMap para recordar las posiciones intercambiadas reales. Cuando el segundo número aleatorio generado es igual al de los números generados anteriormente, el mapa proporciona el valor actual en esa posición en lugar del 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)));
Aquí hay un ejemplo de 5 números aleatorios tomados de un rango de 0 a 100 (tanto 0 como 100 incluidos) sin duplicación.
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)
También puedes hacerlo con un trazador de líneas 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())]