Tengo una matriz como esta:
var arr1 = ["a", "b", "c", "d"];
¿Cómo puedo aleatorizarlo / barajarlo?
Tengo una matriz como esta:
var arr1 = ["a", "b", "c", "d"];
¿Cómo puedo aleatorizarlo / barajarlo?
Respuestas:
El algoritmo de shuffle imparcial de facto es el Shuffle Fisher-Yates (también conocido como Knuth).
Ver https://github.com/coolaj86/knuth-shuffle
Puedes ver una gran visualización aquí (y la publicación original vinculada 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];
shuffle(arr);
console.log(arr);
Más información sobre el algoritmo utilizado.
i--
no --i
. Además, la prueba if (i==0)...
es superflua, ya que si i == 0
el tiempo nunca será entró en bucle. La llamada a Math.floor
se puede hacer más rápido usando ...| 0
. Se pueden eliminar tempi o tempj y el valor se puede asignar directamente a myArray [i] o j según corresponda.
0 !== currentIndex
).
Aquí hay una implementación de JavaScript del Durstenfeld shuffle , una versión optimizada de Fisher-Yates:
/* Randomize array 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;
}
}
Elige un elemento aleatorio para cada elemento de matriz original y lo excluye del siguiente sorteo, como elegir aleatoriamente de una baraja de cartas.
Esta inteligente exclusión intercambia el elemento seleccionado con el actual, luego selecciona el siguiente elemento aleatorio del resto, haciendo un bucle hacia atrás para una eficiencia óptima, asegurando que la selección aleatoria se simplifica (siempre puede comenzar en 0) y, por lo tanto, omite el elemento final.
El tiempo de ejecución del algoritmo es O(n)
. Tenga en cuenta que la reproducción aleatoria se realiza en el lugar, por lo que si no desea modificar la matriz original, primero haga una copia con ella .slice(0)
.
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, utilizando esta función.
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]];
}
}
return array
ya que JavaScript pasa las matrices por referencia cuando se usa como argumentos de función. Supongo que esto es para ahorrar espacio en la pila, pero es una pequeña característica interesante. Al realizar la reproducción aleatoria en la matriz, se barajará la matriz original.
Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () `. Ver ¿ Generar números enteros aleatorios en JavaScript en un rango específico? para una explicación muy completa
¡Advertencia!
Es el uso de este algoritmo no es recomendable , ya que es poco eficiente y fuertemente sesgado ; 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();
});
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;
}
for...in
bucles para iterar sobre matrices.
Puede 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)
Puede barajar matrices polimórficas, y el orden 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 según claves consistentes 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.
Velocidad
La complejidad temporal es O (N log N), igual que la ordenación rápida. La complejidad del espacio es O (N). Esto no es tan eficiente como un shuffle de Fischer Yates pero, en mi opinión, el código es significativamente más corto y más funcional. Si tiene una gran variedad, sin duda debería usar Fischer Yates. Si tiene una pequeña matriz con unos cientos de elementos, puede hacer esto.
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();
shuffle
función.
¡NUEVO!
Algoritmo aleatorio Fisher-Yates más corto y probablemente * más rápido
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 Chrome.
Si tiene alguna pregunta solo pregunte.
EDITAR
si es mas rapido
ACTUACIÓN: http://jsperf.com/fyshuffle
utilizando las funciones más votadas.
EDITAR Hubo un cálculo en exceso (no necesita --c + 1) y nadie se dio cuenta
más corto (4bytes) 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
}
El almacenamiento en caché en otro lugar var rnd=Math.random
y luego el uso rnd()
también aumentaría ligeramente el rendimiento en grandes matrices.
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 & ";", 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 puede comprima el siguiente código en un minifier 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
}
}
fy
y shuffle prototype
, me sale fy
constantemente en la parte inferior en Chrome 37 en OS X 10.9.5 (81% más lento ~ 20k ops en comparación con ~ 100k) y Safari 7.1 es hasta ~ 8% más lento. YMMV, pero no siempre es más rápido. jsperf.com/fyshuffle/3
Editar: esta respuesta es incorrecta
Ver comentarios y https://stackoverflow.com/a/18650169/28234 . Se deja aquí como referencia porque la idea no es rara.
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 cuán aleatorio (o no) es, y si se ajusta a su caso de 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>
Algunas soluciones en esta página no son confiables (solo aleatorizan parcialmente la matriz). Otras soluciones son significativamente menos eficientes. Con testShuffleArrayFun
(ver más abajo) podemos probar las funciones de combinación aleatoria de la matriz en cuanto a confiabilidad y rendimiento. Las siguientes soluciones son: confiables, eficientes y cortas (usando la sintaxis ES6)
[Las pruebas de comparación se realizaron usando testShuffleArrayFun
otras soluciones, en Google Chrome]
Mezclar matriz en su lugar
function getShuffledArr (array){
for (var i = array.length - 1; i > 0; i--) {
var rand = Math.floor(Math.random() * (i + 1));
[array[i], array[rand]] = [array[rand], array[i]]
}
}
ES6 puro, iterativo
const getShuffledArr = arr => {
const newArr = arr.slice()
for (let i = newArr.length - 1; i > 0; i--) {
const rand = Math.floor(Math.random() * (i + 1));
[newArr[i], newArr[rand]] = [newArr[rand], newArr[i]];
}
return newArr
};
Como puede ver en esta página, se han ofrecido soluciones incorrectas aquí en el pasado. Escribí y he usado la siguiente función para probar cualquier función aleatoria de matriz pura (sin efectos secundarios).
function testShuffleArrayFun(getShuffledArrayFun){
const arr = [0,1,2,3,4,5,6,7,8,9]
var countArr = arr.map(el=>{
return arr.map(
el=> 0
)
}) // For each possible position in the shuffledArr and for
// each possible value, we'll create a counter.
const t0 = performance.now()
const n = 1000000
for (var i=0 ; i<n ; i++){
// We'll call getShuffledArrayFun n times.
// And for each iteration, we'll increment the counter.
var shuffledArr = getShuffledArrayFun(arr)
shuffledArr.forEach(
(value,key)=>{countArr[key][value]++}
)
}
const t1 = performance.now()
console.log(`Count Values in position`)
console.table(countArr)
const frequencyArr = countArr.map( positionArr => (
positionArr.map(
count => count/n
)
))
console.log("Frequency of value in position")
console.table(frequencyArr)
console.log(`total time: ${t1-t0}`)
}
Otras soluciones solo por diversión.
ES6 puro, recursivo
const getShuffledArr = arr => {
if (arr.length === 1) {return arr};
const rand = Math.floor(Math.random() * arr.length);
return [arr[rand], ...getShuffledArr(arr.filter((_, i) => i != rand))];
};
ES6 Pure usando array.map
function getShuffledArr (arr){
return [...arr].map( (_, i, arrCopy) => {
var rand = i + ( Math.floor( Math.random() * (arrCopy.length - i) ) );
[arrCopy[rand], arrCopy[i]] = [arrCopy[i], arrCopy[rand]]
return arrCopy[i]
})
}
ES6 Pure usando array.reduce
function getShuffledArr (arr){
return arr.reduce(
(newArr, _, i) => {
var rand = i + ( Math.floor( Math.random() * (newArr.length - i) ) );
[newArr[rand], newArr[i]] = [newArr[i], newArr[rand]]
return newArr
}, [...arr]
)
}
[array[i], array[rand]]=[array[rand], array[i]]
? Tal vez puedas describir cómo funciona eso. ¿Por qué eliges iterar hacia abajo?
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
};
var b =
en un bucle en lugar de declarar b fuera del bucle y asignarlo b =
en un bucle?
Ver https://stackoverflow.com/a/18650169/28234 . Se deja aquí como referencia porque la idea no es rara.
//one line solution
shuffle = (array) => array.sort(() => Math.random() - 0.5);
//Demo
let arr = [1, 2, 3];
shuffle(arr);
alert(arr);
https://javascript.info/task/shuffle
Math.random() - 0.5
es un número aleatorio que puede ser positivo o negativo, por lo que la función de ordenación reordena elementos al azar.
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();
n >>> 0
lugar de ~~n
. Los índices de matriz pueden ser superiores a 2³¹-1.
Encontré esta variante colgando en las respuestas "eliminado por el autor" en un duplicado de esta pregunta. A diferencia de algunas de las otras respuestas que ya tienen muchos votos positivos, esto es:
shuffled
nombre en lugar de shuffle
)Aquí hay un jsfiddle que lo muestra en uso .
Array.prototype.shuffled = function() {
return this.map(function(n){ return [Math.random(), n] })
.sort().map(function(n){ return n[1] });
}
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); });
- no da un orden aleatorio, y si lo usa puede terminar avergonzado: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
.sort(function(a,b){ return a[0] - b[0]; })
si desea que el ordenamiento compare valores numéricamente. El .sort()
comparador predeterminado es lexicográfico, lo que significa que se considerará 10
menor que 2
ya que 1
es menor que 2
.
Math.random()
produce. (es decir, el orden lexicográfico es el mismo que el orden numérico cuando se trata de números del 0 (inclusive) al 1 (exclusivo))
var shuffle = function(array) {
temp = [];
originalLength = array.length;
for (var i = 0; i < originalLength; i++) {
temp.push(array.splice(Math.floor(Math.random()*array.length),1));
}
return temp;
};
arr1.sort(() => Math.random() - 0.5);
Puedes hacerlo fácilmente con:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
Consulte las matrices de ordenación de JavaScript
Fisher-Yates baraja en javascript. Estoy publicando esto aquí porque el uso de dos funciones de utilidad (swap y randInt) aclara el algoritmo en comparación con las otras respuestas aquí.
function swap(arr, i, j) {
// swaps two elements of an array in place
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function randInt(max) {
// returns random integer between 0 and max-1 inclusive.
return Math.floor(Math.random()*max);
}
function shuffle(arr) {
// For each slot in the array (starting at the end),
// pick an element randomly from the unplaced elements and
// place it in the slot, exchanging places with the
// element in the slot.
for(var slot = arr.length - 1; slot > 0; slot--){
var element = randInt(slot+1);
swap(arr, element, slot);
}
}
En primer lugar, eche un vistazo aquí para ver una excelente comparación visual de los diferentes métodos de clasificación en JavaScript.
En segundo lugar, si echas un vistazo rápido al enlace de arriba, verás que el random order
tipo parece funcionar relativamente bien en comparación con los otros métodos, a la vez que es extremadamente fácil y rápido de implementar como se muestra a continuación:
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
Editar : como señaló @gregers, la función de comparación se llama con valores en lugar de índices, por lo que debe usarla indexOf
. Tenga en cuenta que este cambio hace que el código sea menos adecuado para matrices más grandes, ya que se indexOf
ejecuta en tiempo O (n).
Array.prototype.sort
pasa en dos valores como a
y b
, no el índice. Entonces este código no funciona.
Actualización : Aquí estoy sugiriendo un algoritmo relativamente simple (no desde una perspectiva de complejidad ) y corto que funcionará bien con matrices de pequeño tamaño, pero definitivamente va a costar mucho más que el algoritmo clásico de Durstenfeld cuando se trata de matrices enormes. Puede encontrar el Durstenfeld en una de las principales respuestas a esta pregunta.
Respuesta original:
Si no desea que su función aleatoria mute la matriz fuente , puede copiarla en una variable local y luego hacer el resto con una simple lógica aleatoria .
function shuffle(array) {
var result = [], source = array.concat([]);
while (source.length) {
let index = Math.floor(Math.random() * source.length);
result.push(source[index]);
source.splice(index, 1);
}
return result;
}
Lógica aleatoria : seleccione un índice aleatorio, luego agregue el elemento correspondiente a la matriz de resultados y elimínelo de la copia de la matriz de origen . Repita esta acción hasta que la matriz fuente se vacíe .
Y si realmente lo quieres corto, aquí está lo lejos que podría llegar:
function shuffle(array) {
var result = [], source = array.concat([]);
while (source.length) {
let index = Math.floor(Math.random() * source.length);
result.push(source.splice(index, 1)[0]);
}
return result;
}
splice
siendo una forma terriblemente ineficiente de hacer lo que llamaron "ponchar". Si no desea mutar la matriz original, simplemente cópiela y luego baraje esa copia en su lugar utilizando la variante de Durstenfeld mucho más eficiente.
splice
método para crear una copia de este modo: source = array.slice();
.
Aquí está el más fácil ,
function shuffle(array) {
return array.sort(() => Math.random() - 0.5);
}
por ejemplo, puedes consultarlo aquí
Otra implementación más de Fisher-Yates, usando el modo estricto:
function shuffleArray(a) {
"use strict";
var i, t, j;
for (i = a.length - 1; i > 0; i -= 1) {
t = a[i];
j = Math.floor(Math.random() * (i + 1));
a[i] = a[j];
a[j] = t;
}
return a;
}
Todas las otras respuestas se basan en Math.random (), que es rápido pero no adecuado para la aleatorización de nivel criptográfico.
El código siguiente está utilizando el bien conocido Fisher-Yates
algoritmo mientras que la utilización Web Cryptography API
de nivel criptográfico de asignación al azar .
var d = [1,2,3,4,5,6,7,8,9,10];
function shuffle(a) {
var x, t, r = new Uint32Array(1);
for (var i = 0, c = a.length - 1, m = a.length; i < c; i++, m--) {
crypto.getRandomValues(r);
x = Math.floor(r / 65536 / 65536 * m) + i;
t = a [i], a [i] = a [x], a [x] = t;
}
return a;
}
console.log(shuffle(d));
Una modificación simple de la respuesta de CoolAJ86 que no modifica la matriz original:
/**
* Returns a new array whose contents are a shuffled copy of the original array.
* @param {Array} The items to shuffle.
* https://stackoverflow.com/a/2450976/1673761
* https://stackoverflow.com/a/44071316/1673761
*/
const shuffle = (array) => {
let currentIndex = array.length;
let temporaryValue;
let randomIndex;
const newArray = array.slice();
// While there remains elements to shuffle...
while (currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// Swap it with the current element.
temporaryValue = newArray[currentIndex];
newArray[currentIndex] = newArray[randomIndex];
newArray[randomIndex] = temporaryValue;
}
return newArray;
};
Aunque ya hay una serie de implementaciones recomendadas, creo que podemos hacerlo más corto y fácil usando forEach loop, por lo que no debemos preocuparnos por calcular la longitud de la matriz y también podemos evitar con seguridad el uso de una variable temporal.
var myArr = ["a", "b", "c", "d"];
myArr.forEach((val, key) => {
randomIndex = Math.ceil(Math.random()*(key + 1));
myArr[key] = myArr[randomIndex];
myArr[randomIndex] = val;
});
// see the values
console.log('Shuffled Array: ', myArr)
Solo para tener un dedo en el pastel. Aquí presento una implementación recursiva de Fisher Yates shuffle (creo). Da aleatoriedad uniforme.
Nota: El ~~
(operador de doble tilde) se comporta de hecho como Math.floor()
para números reales positivos. Solo un atajo es.
var shuffle = a => a.length ? a.splice(~~(Math.random()*a.length),1).concat(shuffle(a))
: a;
console.log(JSON.stringify(shuffle([0,1,2,3,4,5,6,7,8,9])));
Editar: El código anterior es O (n ^ 2) debido al empleo de .splice()
pero podemos eliminar el empalme y la combinación aleatoria en O (n) mediante el truco de intercambio.
var shuffle = (a, l = a.length, r = ~~(Math.random()*l)) => l ? ([a[r],a[l-1]] = [a[l-1],a[r]], shuffle(a, l-1))
: a;
var arr = Array.from({length:3000}, (_,i) => i);
console.time("shuffle");
shuffle(arr);
console.timeEnd("shuffle");
El problema es que JS no puede cooperar con grandes recursiones. En este caso particular, el tamaño de su matriz está limitado con 3000 ~ 7000, dependiendo del motor de su navegador y algunos hechos desconocidos.
Aleatorizar matriz
var arr = ['apple','cat','Adam','123','Zorro','petunia'];
var n = arr.length; var tempArr = [];
for ( var i = 0; i < n-1; i++ ) {
// The following line removes one random element from arr
// and pushes it onto tempArr
tempArr.push(arr.splice(Math.floor(Math.random()*arr.length),1)[0]);
}
// Push the remaining item onto tempArr
tempArr.push(arr[0]);
arr=tempArr;
-1
for n ya que <
no lo <=
la arrayShuffle
función más corta
function arrayShuffle(o) {
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
Desde un punto de vista teórico, la forma más elegante de hacerlo, en mi humilde opinión, es obtener un único número aleatorio entre 0 y n! -1 y calcular un mapeo uno a uno de {0, 1, …, n!-1}
todas las permutaciones de(0, 1, 2, …, n-1)
. Siempre que pueda usar un generador (pseudo-) aleatorio lo suficientemente confiable como para obtener dicho número sin ningún sesgo significativo, tendrá suficiente información para lograr lo que desea sin necesidad de varios otros números aleatorios.
Al calcular con números flotantes de precisión doble IEEE754, puede esperar que su generador aleatorio proporcione aproximadamente 15 decimales. Como tiene 15! = 1,307,674,368,000 (con 13 dígitos), puede usar las siguientes funciones con matrices que contienen hasta 15 elementos y asumir que no habrá sesgo significativo con matrices que contengan hasta 14 elementos. Si trabaja en un problema de tamaño fijo que requiere calcular muchas veces esta operación aleatoria, puede probar el siguiente código, que puede ser más rápido que otros códigos ya que Math.random
solo se usa una vez (sin embargo, implica varias operaciones de copia).
La siguiente función no se utilizará, pero la doy de todos modos; devuelve el índice de una permutación dada de (0, 1, 2, …, n-1)
acuerdo con el mapeo uno a uno utilizado en este mensaje (el más natural al enumerar permuciones); Está diseñado para trabajar con hasta 16 elementos:
function permIndex(p) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
var tail = [];
var i;
if (p.length == 0) return 0;
for(i=1;i<(p.length);i++) {
if (p[i] > p[0]) tail.push(p[i]-1);
else tail.push(p[i]);
}
return p[0] * fact[p.length-1] + permIndex(tail);
}
El recíproco de la función anterior (requerido para su propia pregunta) está abajo; está diseñado para trabajar con hasta 16 elementos; devuelve la permutación de orden n de (0, 1, 2, …, s-1)
:
function permNth(n, s) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
var i, j;
var p = [];
var q = [];
for(i=0;i<s;i++) p.push(i);
for(i=s-1; i>=0; i--) {
j = Math.floor(n / fact[i]);
n -= j*fact[i];
q.push(p[j]);
for(;j<i;j++) p[j]=p[j+1];
}
return q;
}
Ahora, lo que quieres simplemente es:
function shuffle(p) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000];
return permNth(Math.floor(Math.random()*fact[p.length]), p.length).map(
function(i) { return p[i]; });
}
Debería funcionar para hasta 16 elementos con un pequeño sesgo teórico (aunque imperceptible desde un punto de vista práctico); se puede ver como totalmente utilizable para 15 elementos; con matrices que contienen menos de 14 elementos, puede considerar con seguridad que no habrá absolutamente ningún sesgo.