Pregunta ¿Cómo aleatorizar (mezclar) una matriz de JavaScript?


Tengo una matriz como esta:

var arr1 = ["a", "b", "c", "d"];

¿Cómo puedo aleatorizar / mezclar?


872
2018-03-15 22:37


origen


Respuestas:


El algoritmo aleatorio de facto de facto es el Shuffle de Fisher-Yates (también conocido como Knuth).

Ver https://github.com/coolaj86/knuth-shuffle

Puedes ver un gran visualización aquí (y la publicación original vinculado a esto)

function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

// Used like so
var arr = [2, 11, 37, 42];
arr = shuffle(arr);
console.log(arr);

Algo más información sobre el algoritmo usado.


1084
2017-09-28 20:20



Aquí hay una implementación de JavaScript de Durstenfeld baraja, una versión optimizada por computadora de Fisher-Yates:

/**
 * Randomize array element order in-place.
 * Using Durstenfeld shuffle algorithm.
 */
function shuffleArray(array) {
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

El algoritmo de Fisher-Yates funciona seleccionando un elemento aleatorio para cada elemento de matriz original, y luego excluyéndolo del próximo sorteo. Al igual que elegir al azar de una baraja de cartas.

Esta exclusión se realiza de una manera inteligente (inventada por Durstenfeld para el uso de computadoras) intercambiando el elemento elegido con el elemento actual, y luego seleccionando el siguiente elemento aleatorio del resto. Para una eficiencia óptima, el bucle se ejecuta hacia atrás para simplificar la selección aleatoria (siempre puede comenzar en 0), y omite el último elemento porque ya no hay más opciones.

El tiempo de ejecución de este algoritmo es O (n). Tenga en cuenta que la reproducción aleatoria se realiza en el lugar. Entonces, si no quiere modificar la matriz original, primero haga una copia con .slice(0).

Actualización a ES6 / ECMAScript 2015

El nuevo ES6 nos permite asignar dos variables a la vez. Esto es especialmente útil cuando queremos intercambiar los valores de dos variables, ya que podemos hacerlo en una línea de código. Aquí hay una forma más corta de la misma función, usando esta característica.

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]]; // eslint-disable-line no-param-reassign
    }
}

477
2017-09-06 04:55



[editar comunidad: esta respuesta es incorrecta; ver comentarios. Se deja aquí para referencia futura porque la idea no es tan rara.]

[1,2,3,4,5,6].sort(function() {
  return .5 - Math.random();
});

85
2018-04-13 13:59



Uno podría (o debería) usarlo como un prototipo de Array:

De ChristopheD:

Array.prototype.shuffle = function() {
  var i = this.length, j, temp;
  if ( i == 0 ) return this;
  while ( --i ) {
     j = Math.floor( Math.random() * ( i + 1 ) );
     temp = this[i];
     this[i] = this[j];
     this[j] = temp;
  }
  return this;
}

69
2018-03-31 05:29



Use la biblioteca underscore.js. El método _.shuffle() es bueno para este caso. Aquí hay un ejemplo con el método:

var _ = require("underscore");

var arr = [1,2,3,4,5,6];
// Testing _.shuffle
var testShuffle = function () {
  var indexOne = 0;
    var stObj = {
      '0': 0,
      '1': 1,
      '2': 2,
      '3': 3,
      '4': 4,
      '5': 5
    };
    for (var i = 0; i < 1000; i++) {
      arr = _.shuffle(arr);
      indexOne = _.indexOf(arr, 1);
      stObj[indexOne] ++;
    }
    console.log(stObj);
};
testShuffle();

56
2017-09-22 23:21



¡NUEVO!

Algoritmo aleatorio Fisher-Yates más corto y probablemente * más rápido

  1. usa mientras ---
  2. bitwise to floor (números hasta 10 dígitos decimales (32 bits))
  3. eliminado cierres innecesarios y otras cosas

function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
 c=a.length;while(c)b=Math.random()*(--c+1)|0,d=a[c],a[c]=a[b],a[b]=d
}

tamaño del script (con fy como nombre de la función): 90bytes

MANIFESTACIÓN http://jsfiddle.net/vvpoma8w/

* más rápido probablemente en todos los navegadores, excepto en cromo.

Si tiene alguna pregunta solo pregunte.

EDITAR

sí, es más rápido

ACTUACIÓN:  http://jsperf.com/fyshuffle

usando las funciones más votadas.

EDITAR  Hubo un cálculo en exceso (no es necesario --c + 1) y nadie se dio cuenta

más corto (4 bytes) y más rápido (¡pruébalo!).

function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
 c=a.length;while(c)b=Math.random()*c--|0,d=a[c],a[c]=a[b],a[b]=d
}

Almacenamiento en caché en otro lugar var rnd=Math.random y luego usa rnd() también aumentaría ligeramente el rendimiento en grandes arreglos.

http://jsfiddle.net/vvpoma8w/2/

Versión legible (use la versión original. Esto es más lento, los vars son inútiles, como los cierres y ";", el código en sí también es más corto ... tal vez lea esto Cómo "minificar" el código Javascript Por cierto, no puedes comprimir el siguiente código en un minimaps de JavaScript como el anterior.

function fisherYates( array ){
 var count = array.length,
     randomnumber,
     temp;
 while( count ){
  randomnumber = Math.random() * count-- | 0;
  temp = array[count];
  array[count] = array[randomnumber];
  array[randomnumber] = temp
 }
}

45
2018-04-05 15:38



Una forma muy simple para arreglos pequeños es simplemente esto:

const someArray = [1, 2, 3, 4, 5];

someArray.sort(() => Math.random() - 0.5);

Probablemente no sea muy eficiente, pero para arreglos pequeños esto funciona bien. Aquí hay un ejemplo para que pueda ver qué tan aleatorio (o no) es, y si se ajusta a su uso o no.

const resultsEl = document.querySelector('#results');
const buttonEl = document.querySelector('#trigger');

const generateArrayAndRandomize = () => {
  const someArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  someArray.sort(() => Math.random() - 0.5);
  return someArray;
};

const renderResultsToDom = (results, el) => {
  el.innerHTML = results.join(' ');
};

buttonEl.addEventListener('click', () => renderResultsToDom(generateArrayAndRandomize(), resultsEl));
<h1>Randomize!</h1>
<button id="trigger">Generate</button>
<p id="results">0 1 2 3 4 5 6 7 8 9</p>


23
2017-10-03 13:16



Puedes hacerlo fácilmente con el mapa y ordenar:

let unshuffled = ['hello', 'a', 't', 'q', 1, 2, 3, {cats: true}]

let shuffled = unshuffled
  .map((a) => ({sort: Math.random(), value: a}))
  .sort((a, b) => a.sort - b.sort)
  .map((a) => a.value)
  1. Colocamos cada elemento en la matriz en un objeto y le damos una clave de clasificación aleatoria
  2. Clasificamos usando la clave aleatoria
  3. Nos desasignar para obtener los objetos originales

Puede barajar matrices polimórficas, y el género es tan aleatorio como Math.random, que es lo suficientemente bueno para la mayoría de los propósitos.

Dado que los elementos se ordenan contra claves coherentes que no se regeneran en cada iteración, y cada comparación se extrae de la misma distribución, se cancela cualquier no aleatoriedad en la distribución de Math.random.


23
2018-04-01 21:23



Agregando a @Laurens Holsts respuesta. Esto es 50% comprimido.

function shuffleArray(d) {
  for (var c = d.length - 1; c > 0; c--) {
    var b = Math.floor(Math.random() * (c + 1));
    var a = d[c];
    d[c] = d[b];
    d[b] = a;
  }
  return d
};

19
2017-12-20 04:15



Con ES2015 puedes usar este:

Array.prototype.shuffle = function() {
  let m = this.length, i;
  while (m) {
    i = (Math.random() * m--) >>> 0;
    [this[m], this[i]] = [this[i], this[m]]
  }
  return this;
}

Uso:

[1, 2, 3, 4, 5, 6, 7].shuffle();

13
2017-08-09 15:37