다음과 같은 배열이 있습니다.
var arr1 = ["a", "b", "c", "d"];
랜덤 화 / 셔플하는 방법은 무엇입니까?
다음과 같은 배열이 있습니다.
var arr1 = ["a", "b", "c", "d"];
랜덤 화 / 셔플하는 방법은 무엇입니까?
답변:
사실상의 비 편향 셔플 알고리즘은 Fisher-Yates (일명 Knuth) 셔플입니다.
참조 https://github.com/coolaj86/knuth-shuffle를
당신은 볼 수 있습니다 여기에 큰 시각화 (그리고 원래의 게시물 이 링크를 )
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);
사용 된 알고리즘에 대한 추가 정보
i--
아니 어야합니다 --i
. 또한, 테스트는 if (i==0)...
경우 때문에 불필요 동안의 루프가 입력되지 않습니다. 를 사용하여 더 빠르게 전화를 걸 수 있습니다 . 어느 템포 또는 tempj는 제거 될 수 있고, 값은 직접 할당 될 myArray의 [I] 또는 J 적절. i == 0
Math.floor
...| 0
0 !== currentIndex
).
Fisher-Yates의 최적화 된 버전 인 Durstenfeld shuffle 의 JavaScript 구현은 다음과 같습니다 .
/* 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;
}
}
각 원본 배열 요소에 대해 임의의 요소를 선택하고 카드 덱에서 무작위로 선택하는 것처럼 다음 추첨에서 제외합니다.
이 영리한 제외는 선택한 요소를 현재 요소와 교체 한 다음 나머지 요소에서 다음 임의 요소를 선택하여 최적의 효율성을 위해 뒤로 반복하여 임의 선택이 단순화되고 (항상 0에서 시작할 수 있음) 최종 요소를 건너 뜁니다.
알고리즘 런타임은 O(n)
입니다. 참고 먼저, 원래의 배열은 수정과의 복사본을 만들고 싶어하지 않는, 그래서 만약 셔플이 자리에서 이루어집니다 .slice(0)
.
새로운 ES6을 사용하면 한 번에 두 개의 변수를 할당 할 수 있습니다. 이것은 한 줄의 코드로 할 수 있기 때문에 두 변수의 값을 바꾸고 싶을 때 특히 유용합니다. 이 기능을 사용하는 동일한 기능의 짧은 형식은 다음과 같습니다.
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
JavaScript는 함수 인수로 사용될 때 참조로 배열을 전달하므로 필요하지 않습니다 . 이것이 스택 공간을 절약하는 것이라고 가정하지만 흥미로운 작은 기능입니다. 어레이에서 셔플을 수행하면 원래 어레이가 셔플됩니다.
Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt ()`. 특정 범위에서 JavaScript에서 임의의 정수 생성을 참조하십시오 . 매우 포괄적 인 설명이 필요합니다.
경고!
이 알고리즘 은 비효율적 이고 강력하게 편향되어 있으므로 사용 하지 않는 것이 좋습니다 . 의견을 참조하십시오. 아이디어가 그렇게 드물지 않기 때문에 나중에 참조하기 위해 여기에 남겨두고 있습니다.
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});
Array의 protoype로 사용할 수 있거나 사용해야합니다.
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
루프를 사용하여 배열을 반복 하지 마십시오 .
맵과 정렬로 쉽게 할 수 있습니다.
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)
다형성 배열을 섞을 수 있으며 정렬은 Math.random만큼 임의적이며 대부분의 목적에 충분합니다.
요소는 각 반복을 재생성하지 않는 일관된 키에 대해 정렬되고 각 비교는 동일한 분포에서 가져 오기 때문에 Math.random 분포의 비 랜덤 성은 취소됩니다.
속도
시간 복잡도는 빠른 정렬과 마찬가지로 O (N log N)입니다. 공간 복잡도는 O (N)입니다. 이것은 Fischer Yates shuffle만큼 효율적이지 않지만 제 의견으로는 코드가 상당히 짧고 기능적입니다. 배열이 큰 경우 Fischer Yates를 사용해야합니다. 수백 개의 항목이있는 작은 배열이있는 경우이 작업을 수행 할 수 있습니다.
underscore.js 라이브러리를 사용하십시오. 이 방법 _.shuffle()
은 좋습니다. 다음은 그 방법에 대한 예입니다.
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
함수 를 얻기 위해 전체 라이브러리를 포함시킬 필요는 없습니다 .
새로운!
더 짧고 아마도 더 빠른 Fisher-Yates shuffle 알고리즘
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
}
스크립트 크기 (함수 이름으로 fy) : 90 바이트
데모 http://jsfiddle.net/vvpoma8w/
* 크롬을 제외한 모든 브라우저에서 더 빠를 것입니다.
궁금한 점이 있으면 물어보십시오.
편집하다
네 빠릅니다
성능 : http://jsperf.com/fyshuffle
최고 투표 기능 사용.
편집 초과 계산이 있었으며 (-c + 1 필요 없음) 아무도 눈치 채지 못했습니다.
더 짧고 (4 바이트) 더 빠릅니다 (테스트 해보세요!).
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
}
다른 곳에서 캐싱 var rnd=Math.random
한 다음 사용 rnd()
하면 큰 어레이의 성능이 약간 향상됩니다.
http://jsfiddle.net/vvpoma8w/2/
읽기 가능 버전 (원래 버전을 사용이 느린, 바르가 폐쇄 및처럼 쓸모. ";", 코드 자체도 짧은 ... 어쩌면이 글을 읽을 방법 '작게하다'자바 스크립트 코드 BTW, 당신은 할 수 없습니다 위의 코드와 같은 자바 스크립트 축소 기에서 다음 코드를 압축하십시오.)
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
과 shuffle prototype
내가 얻을 fy
는 OS X 10.9.5에 크롬 (37)의 바닥 (81 % 느리게 ~ 20K 작전이 ~ 100,000에 비해) 및 Safari 7.1에서 일관되게 8 % 느린 ~까지입니다. YMMV이지만 항상 빠르지는 않습니다. jsperf.com/fyshuffle/3
편집 :이 답변이 잘못되었습니다
의견 및 https://stackoverflow.com/a/18650169/28234를 참조하십시오 . 아이디어가 드물지 않기 때문에 참조를 위해 여기에 남아 있습니다.
작은 배열을위한 매우 간단한 방법은 다음과 같습니다.
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
아마 효율적이지 않을 수도 있지만 작은 배열의 경우에는 잘 작동합니다. 다음은 그 예가 임의적이든 아니든, 사용 사례에 맞는지 여부를 확인할 수 있습니다.
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>
이 페이지의 일부 솔루션은 신뢰할 수 없습니다 (어레이의 일부만 무작위 화). 다른 솔루션은 효율성이 크게 떨어집니다. 와testShuffleArrayFun
우리 안정성 및 성능 어레이 셔플 기능을 테스트 할 수있다 (아래 참조). 신뢰할 수 있고 효율적이며 짧은 솔루션은 다음과 같습니다 (ES6 구문 사용).
[ testShuffleArrayFun
Google 크롬에서 다른 솔루션을 사용하여 비교 테스트를 수행함]
배열 셔플
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 순수 반복
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
};
이 페이지에서 볼 수 있듯이 과거에 잘못된 솔루션이 제공되었습니다. 나는 순수한 (부작용이없는) 배열 무작위 화 함수를 테스트하기 위해 다음 함수를 작성하고 사용했습니다.
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}`)
}
재미를위한 다른 솔루션.
ES6 순수 재귀
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))];
};
array.map을 사용하는 ES6 Pure
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]
})
}
array.reduce를 사용하는 ES6 Pure
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]]
? 어쩌면 당신은 그것이 작동하는 방법을 설명 할 수 있습니다. 왜 아래쪽으로 반복하도록 선택합니까?
@Laurens Holsts에 추가하십시오. 이 압축률은 50 %입니다.
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 =
b 외부 루프를 선언 b =
하고 루프에서 할당하는 대신 루프에서 수행하는 것이 효율적 입니까?
참조 https://stackoverflow.com/a/18650169/28234를 . 아이디어가 드물지 않기 때문에 참조를 위해 여기에 남아 있습니다.
//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
는 양수이거나 음수 일 수있는 난수이므로 정렬 함수는 요소를 무작위로 재정렬합니다.
ES2015를 사용하면 다음 중 하나를 사용할 수 있습니다.
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;
}
용법:
[1, 2, 3, 4, 5, 6, 7].shuffle();
n >>> 0
대신 대신 사용해야 합니다 ~~n
. 배열 지수는 2³¹-1보다 높을 수 있습니다.
이 질문의 복제본에 대한 "저자에 의해 삭제됨"답변에서이 변형이 발견되었습니다. 많은 공표가 이미있는 다른 답변들과 달리 다음과 같습니다.
shuffled
이름이 아닌 shuffle
)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(); });
-임의의 정렬을 제공하지 않으며,이를 사용하면 당황 할 수 있습니다. robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
.sort(function(a,b){ return a[0] - b[0]; })
정렬을 통해 값을 숫자로 비교하려면 사용해야 합니다. 기본 .sort()
비교기는 고려할 것입니다 의미 사전 식이다 10
미만으로 2
이후 1
미만이다 2
.
Math.random()
생산 범위의 숫자에 중요하지 않습니다 . (즉, 사전 식 순서는 0 (포함)부터 1 (제외)까지의 숫자를 처리 할 때 숫자 순서와 동일)
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);
당신은 쉽게 할 수 있습니다 :
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
JavaScript 정렬 배열을 참조하십시오
Fisher-Yates 는 자바 스크립트로 섞습니다. 두 개의 유틸리티 함수 (swap 및 randInt)를 사용하면 다른 답변과 비교하여 알고리즘이 명확 해지기 때문에 여기에 게시하고 있습니다.
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);
}
}
우선 여기를보세요 자바 스크립트에서 서로 다른 분류 방법의 훌륭한 시각적으로 비교합니다.
둘째, 위의 링크를 살펴보면 random order
정렬이 다른 방법에 비해 상대적으로 잘 수행되는 것처럼 보이지만 아래 표시된 것처럼 구현하기가 매우 쉽고 빠릅니다.
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
편집 : @gregers가 지적한 것처럼 비교 함수는 인덱스가 아닌 값으로 호출되므로을 사용해야 indexOf
합니다. 이 변경으로 indexOf
O (n) 시간에 실행되는 큰 배열에는 코드가 적합하지 않습니다 .
Array.prototype.sort
인덱스가 아닌 및 로 두 값 을 전달 합니다. 따라서이 코드는 작동하지 않습니다. a
b
업데이트 : 여기 에서는 작은 크기의 배열에서 잘 작동 하는 비교적 간단한 ( 복잡성 관점이 아닌 ) 짧은 알고리즘을 제안 하지만 거대한 배열을 처리 할 때 고전적인 Durstenfeld 알고리즘 보다 훨씬 많은 비용이 듭니다 . Durstenfeld 는이 질문에 대한 최고의 답변 중 하나에서 찾을 수 있습니다 .
원래 답변 :
당신이 경우 원하지 않는 돌연변이 당신의 셔플 기능을 소스 배열을 , 당신은 간단한으로 나머지를 로컬 변수에 복사 할 수 있습니다 셔플 논리 .
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;
}
셔플 링 로직 : 랜덤 인덱스를 선택한 다음 결과 배열에 해당 요소를 추가 하고 소스 배열 복사본 에서 삭제합니다 . 소스 배열이 비워 질 때까지이 동작을 반복하십시오. .
그리고 당신이 정말로 그것을 짧게 원한다면, 내가 얻을 수있는 거리는 다음과 같습니다.
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
"스트라이크 아웃 (striking out)"이라고하는 것을 수행하는 끔찍한 비효율적 인 방법입니다. 원래 배열을 변경하지 않으려면 복사 한 다음 훨씬 더 효율적인 Durstenfeld 변형을 사용하여 해당 복사본을 제자리에 섞으십시오.
splice
방법을 사용하여 다음과 같은 사본을 만들 수도 있습니다 source = array.slice();
.
엄격 모드를 사용하는 Fisher-Yates의 또 다른 구현 :
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;
}
다른 모든 답변은 Math.random () 기반으로 빠르지 만 암호화 수준 무작위 화에는 적합하지 않습니다.
아래 코드는 암호화 수준의 무작위 화Fisher-Yates
를 Web Cryptography API
위해 잘 알려진 알고리즘을 사용하고 있습니다 .
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));
원래 배열을 수정하지 않는 CoolAJ86의 간단한 대답 수정 :
/**
* 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;
};
이미 많은 구현이 권장되었지만 forEach 루프를 사용하면 더 짧고 쉽게 만들 수 있다고 생각하므로 배열 길이 계산에 대해 걱정할 필요가 없으며 임시 변수 사용을 안전하게 피할 수 있습니다.
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)
파이에 손가락을 넣는 것만으로 여기에서는 Fisher Yates shuffle의 재귀 구현을 제시합니다 (생각합니다). 균일 한 무작위성을 제공합니다.
참고 : ~~
(이중 물결표 연산자)는 실제로 Math.floor()
양의 실수 처럼 동작 합니다. 바로 가기입니다.
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])));
편집 : 위 코드는 고용으로 인해 O (n ^ 2) .splice()
이지만 스왑 트릭으로 O (n)에서 스플 라이스 및 셔플을 제거 할 수 있습니다.
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");
문제는 JS가 큰 재귀를 처리 할 수 없다는 것입니다. 이 특정 경우 배열 크기는 브라우저 엔진 및 일부 알 수없는 사실에 따라 3000 ~ 7000과 같이 제한됩니다.
배열 랜덤 화
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
당신이 사용되는 N에 대한 <
없습니다<=
가장 짧은 arrayShuffle
기능
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;
}
이론적 인 관점에서, 가장 겸손한 견해로, 그것을 수행하는 가장 우아한 방법은 0 과 n 사이 의 단일 난수 를 구하고 모든 순열 에서 일대일 매핑을 계산하는 것 입니다.{0, 1, …, n!-1}
(0, 1, 2, …, n-1)
. 중대한 편견없이 그러한 숫자를 얻을 수있을만큼 신뢰할 수있는 (의사) 난수 생성기를 사용할 수 있다면, 다른 난수없이 몇 가지 원하는 정보를 얻을 수있는 충분한 정보가 있습니다.
IEEE754 배정 밀도 부동 숫자로 계산할 때 임의 생성기가 약 15 진수를 제공 할 것으로 예상 할 수 있습니다. 당신이 있기 때문에 15! = 1,307,674,368,000 (13 자리), 당신은 배열 (15 개) 요소까지 포함하고 배열 (14 개) 요소까지 포함와 유의 한 바이어스가 없을 것입니다 가정에 다음과 같은 기능을 사용할 수 있습니다. 이 셔플 작업을 여러 번 계산해야하는 고정 크기 문제에 대해 작업 하는 경우 한 번만 사용하기 때문에 다른 코드보다 더 빠를 수 있는 다음 코드를 시도 할 수 있습니다 Math.random
(다수의 복사 작업이 포함됨).
다음 함수는 사용되지 않지만 어쨌든 제공합니다. (0, 1, 2, …, n-1)
이 메시지에 사용 된 일대일 매핑에 따라 주어진 순열의 인덱스를 반환합니다 (퍼뮤 테이션을 열거 할 때 가장 자연스러운 것). 최대 16 개의 요소로 작업하도록되어 있습니다.
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);
}
이전 함수의 역수 (자신의 질문에 필요)는 다음과 같습니다. 최대 16 개의 요소로 작업하도록되어 있습니다. 순서 n 의 순열을 반환합니다 (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;
}
이제 원하는 것은 다음과 같습니다.
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]; });
}
이론적 인 편견이 거의없는 최대 16 개의 요소에 대해 작동해야합니다 (실제적인 관점에서는 눈에 띄지 않지만). 15 개의 요소에 대해 완전히 사용 가능한 것으로 볼 수 있습니다. 14 개 미만의 요소를 포함하는 배열을 사용하면 치우침이 없을 것입니다.