Tôi có một mảng như thế này:
var arr1 = ["a", "b", "c", "d"];
Làm thế nào tôi có thể ngẫu nhiên / xáo trộn nó?
Tôi có một mảng như thế này:
var arr1 = ["a", "b", "c", "d"];
Làm thế nào tôi có thể ngẫu nhiên / xáo trộn nó?
Câu trả lời:
Thuật toán xáo trộn không thiên vị trên thực tế là Shuffle Fisher-Yates (còn gọi là Knuth).
Xem https://github.com/coolaj86/knuth-shuffle
Bạn có thể thấy một hình ảnh tuyệt vời ở đây (và bài viết gốc được liên kết với điều này )
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ột số thông tin thêm về thuật toán được sử dụng.
i--
không --i
. Ngoài ra, kiểm tra if (i==0)...
là không cần thiết vì nếu i == 0
các trong khi vòng lặp sẽ không bao giờ được nhập vào. Cuộc gọi đến Math.floor
có thể được thực hiện nhanh hơn bằng cách sử dụng ...| 0
. Có thể loại bỏ tempi hoặc tempj và giá trị được gán trực tiếp cho myArray [i] hoặc j khi thích hợp.
0 !== currentIndex
).
Đây là một triển khai JavaScript của shuffle Durstenfeld , một phiên bản tối ưu của 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;
}
}
Nó chọn một phần tử ngẫu nhiên cho mỗi phần tử mảng ban đầu và loại trừ nó khỏi lần rút tiếp theo, như chọn ngẫu nhiên từ một cỗ bài.
Loại trừ thông minh này hoán đổi phần tử được chọn với phần tử hiện tại, sau đó chọn phần tử ngẫu nhiên tiếp theo từ phần còn lại, lặp lại để đạt hiệu quả tối ưu, đảm bảo chọn ngẫu nhiên được đơn giản hóa (luôn có thể bắt đầu từ 0) và do đó bỏ qua phần tử cuối cùng.
Thời gian chạy thuật toán là O(n)
. Lưu ý rằng việc xáo trộn được thực hiện tại chỗ, vì vậy nếu bạn không muốn sửa đổi mảng ban đầu, trước tiên hãy tạo một bản sao của nó với .slice(0)
.
ES6 mới cho phép chúng ta gán hai biến cùng một lúc. Điều này đặc biệt hữu ích khi chúng ta muốn trao đổi các giá trị của hai biến, vì chúng ta có thể thực hiện nó trong một dòng mã. Đây là một hình thức ngắn hơn của cùng một chức năng, sử dụng tính năng này.
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
vì JavaScript chuyển các mảng bằng tham chiếu khi được sử dụng làm đối số hàm. Tôi cho rằng điều này là để tiết kiệm không gian ngăn xếp, nhưng đó là một tính năng nhỏ thú vị. Thực hiện xáo trộn trên mảng sẽ xáo trộn mảng ban đầu.
Math.random() should not be multiplied with the loop counter + 1, but with
mảng.lengt () `. Xem Tạo số nguyên ngẫu nhiên trong JavaScript trong một phạm vi cụ thể? cho một lời giải thích rất toàn diện.
Cảnh báo!
Việc sử dụng thuật toán này không được khuyến khích , vì nó không hiệu quả và sai lệch mạnh ; Xem ý kiến. Nó đang được để lại ở đây để tham khảo trong tương lai, vì ý tưởng không phải là hiếm.
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});
Người ta có thể (hoặc nên) sử dụng nó như một protoype từ Array:
Từ BarsheD:
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
các vòng lặp để lặp qua các mảng.
Bạn có thể làm điều đó một cách dễ dàng với bản đồ và sắp xếp:
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)
Bạn có thể xáo trộn các mảng đa hình, và sắp xếp là ngẫu nhiên như Math.random, đủ tốt cho hầu hết các mục đích.
Vì các phần tử được sắp xếp theo các khóa nhất quán không được tạo lại mỗi lần lặp và mỗi phép so sánh lấy từ cùng một phân phối, nên mọi sự không ngẫu nhiên trong phân phối Math.random đều bị hủy bỏ.
Tốc độ
Độ phức tạp thời gian là O (N log N), giống như sắp xếp nhanh. Độ phức tạp không gian là O (N). Điều này không hiệu quả như xáo trộn Fischer Yates nhưng, theo tôi, mã này ngắn hơn đáng kể và nhiều chức năng hơn. Nếu bạn có một mảng lớn, bạn chắc chắn nên sử dụng Fischer Yates. Nếu bạn có một mảng nhỏ với vài trăm mục, bạn có thể làm điều này.
Sử dụng thư viện underscore.js. Phương pháp _.shuffle()
này là tốt đẹp cho trường hợp này. Dưới đây là một ví dụ với phương thức:
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
chức năng.
MỚI!
Thuật toán xáo trộn Fisher-Yates ngắn hơn và có thể nhanh hơn
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
}
kích thước tập lệnh (với fy là tên hàm): 90byte
DEMO http://jsfiddle.net/vvpoma8w/
* nhanh hơn có lẽ trên tất cả các trình duyệt ngoại trừ chrome.
Nếu bạn có bất kỳ câu hỏi chỉ cần hỏi.
BIÊN TẬP
vâng nó nhanh hơn
HIỆU SUẤT: http://jsperf.com/fyshuffle
sử dụng các chức năng được bình chọn hàng đầu.
EDIT Có một phép tính vượt quá (không cần --c + 1) và không ai chú ý
ngắn hơn (4byte) & nhanh hơn (kiểm tra nó!).
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
}
Bộ nhớ đệm ở một nơi khác var rnd=Math.random
và sau đó sử dụng rnd()
cũng sẽ tăng hiệu suất một chút trên các mảng lớn.
http://jsfiddle.net/vvpoma8w/2/
Phiên bản có thể đọc được (sử dụng phiên bản gốc. Phiên bản này chậm hơn, vars là vô dụng, như các bao đóng & ";", bản thân mã cũng ngắn hơn ... có thể đọc phần này Cách 'rút gọn' mã Javascript , btw bạn không thể nén mã sau đây trong một công cụ khai thác javascript như ở trên.)
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
và shuffle prototype
, tôi nhận được fy
một cách nhất quán ở dưới cùng trong Chrome 37 trên OS X 10.9.5 (chậm hơn 81% so với ~ 100k so với ~ 100k) và Safari 7.1 chậm hơn tới 8%. YMMV, nhưng không phải lúc nào cũng nhanh hơn. jsperf.com/fyshuffle/3
Chỉnh sửa: Câu trả lời này không chính xác
Xem bình luận và https://stackoverflow.com/a/18650169/28234 . Nó đang được để lại ở đây để tham khảo vì ý tưởng không phải là hiếm.
Một cách rất đơn giản cho các mảng nhỏ chỉ đơn giản là thế này:
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
Nó có thể không hiệu quả lắm, nhưng đối với các mảng nhỏ thì nó hoạt động tốt. Đây là một ví dụ để bạn có thể thấy nó ngẫu nhiên (hoặc không) và liệu nó có phù hợp với usecase của bạn hay không.
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>
Một số giải pháp trên trang này không đáng tin cậy (chúng chỉ ngẫu nhiên một phần mảng). Các giải pháp khác là ít hiệu quả hơn đáng kể. Với testShuffleArrayFun
(xem bên dưới), chúng tôi có thể kiểm tra các chức năng xáo trộn mảng về độ tin cậy và hiệu suất. Các giải pháp sau đây là: đáng tin cậy, hiệu quả và ngắn gọn (sử dụng cú pháp ES6)
[Các thử nghiệm so sánh đã được thực hiện bằng cách sử dụng testShuffleArrayFun
so với các giải pháp khác, trong Google Chrome]
Shuffle Array tại chỗ
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 Tinh khiết, lặp đi lặp lại
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
};
Như bạn có thể thấy trong trang này, đã có những giải pháp không chính xác được cung cấp ở đây trong quá khứ. Tôi đã viết và đã sử dụng hàm sau để kiểm tra bất kỳ hàm ngẫu nhiên mảng thuần túy (không có tác dụng phụ) nào.
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}`)
}
Các giải pháp khác chỉ để cho vui.
ES6 thuần túy, đệ quy
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 sử dụng mảng.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 sử dụng mảng.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]]
? Có lẽ bạn có thể phác thảo cách làm việc đó. Tại sao bạn chọn lặp đi lặp lại?
Thêm vào câu trả lời @Laurens Holsts. Đây là 50% nén.
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 =
trong một vòng lặp thay vì khai báo b bên ngoài và gán nó b =
trong một vòng lặp không?
Xem https://stackoverflow.com/a/18650169/28234 . Nó đang được để lại ở đây để tham khảo vì ý tưởng không phải là hiếm.
//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
là một số ngẫu nhiên có thể dương hoặc âm, vì vậy hàm sắp xếp sắp xếp lại các phần tử một cách ngẫu nhiên.
Với ES2015 bạn có thể sử dụng cái này:
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;
}
Sử dụng:
[1, 2, 3, 4, 5, 6, 7].shuffle();
n >>> 0
thay vì ~~n
. Chỉ số mảng có thể cao hơn 2³¹-1.
Tôi tìm thấy biến thể này treo trong câu trả lời "bị xóa bởi tác giả" trên một bản sao của câu hỏi này. Không giống như một số câu trả lời khác đã có nhiều upvote, đây là:
shuffled
tên chứ không phải shuffle
)Đây là một jsfiddle cho thấy nó được sử dụng .
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(); });
- nó không đưa ra một loại ngẫu nhiên, và nếu bạn sử dụng nó, bạn có thể sẽ bối rối: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
.sort(function(a,b){ return a[0] - b[0]; })
nếu bạn muốn sắp xếp để so sánh các giá trị bằng số. Bộ .sort()
so sánh mặc định là từ vựng, có nghĩa là nó sẽ được coi 10
là ít hơn 2
vì 1
ít hơn 2
.
Math.random()
sản xuất. (nghĩa là, thứ tự từ điển giống như thứ tự số khi xử lý các số từ 0 (đã bao gồm) đến 1 (độc quyền))
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);
Bạn có thể làm điều đó dễ dàng với:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
Vui lòng tham khảo tại Mảng sắp xếp JavaScript
Fisher-Yates xáo trộn trong javascript. Tôi đang đăng bài này ở đây vì việc sử dụng hai hàm tiện ích (hoán đổi và randInt) làm rõ thuật toán so với các câu trả lời khác ở đây.
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);
}
}
Trước hết, hãy xem ở đây để so sánh trực quan tuyệt vời về các phương pháp sắp xếp khác nhau trong javascript.
Thứ hai, nếu bạn xem nhanh liên kết ở trên, bạn sẽ thấy rằng random order
loại dường như hoạt động tương đối tốt so với các phương pháp khác, trong khi cực kỳ dễ dàng và nhanh chóng để thực hiện như dưới đây:
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
Chỉnh sửa : như được chỉ ra bởi @gregers, hàm so sánh được gọi với các giá trị thay vì chỉ mục, đó là lý do tại sao bạn cần sử dụng indexOf
. Lưu ý rằng thay đổi này làm cho mã ít phù hợp hơn với các mảng lớn hơn khi indexOf
chạy trong thời gian O (n).
Array.prototype.sort
vượt qua trong hai giá trị là a
và b
không phải là chỉ số. Vì vậy, mã này không hoạt động.
Cập nhật : Ở đây tôi đề xuất một cách tương đối đơn giản (không phải từ sự phức tạp thuật toán góc ) và thuật toán ngắn sẽ hoạt động tốt với các mảng có kích thước nhỏ, nhưng chắc chắn sẽ tốn kém hơn nhiều so với thuật toán Durstenfeld cổ điển khi bạn xử lý các mảng lớn. Bạn có thể tìm thấy Durstenfeld trong một trong những câu trả lời hàng đầu cho câu hỏi này.
Câu trả lời gốc:
Nếu bạn không muốn hàm xáo trộn của mình biến đổi mảng nguồn , bạn có thể sao chép nó sang một biến cục bộ, sau đó thực hiện phần còn lại với logic xáo trộn đơn giản .
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;
}
Xáo trộn logic : chọn một chỉ mục ngẫu nhiên, sau đó thêm phần tử tương ứng vào mảng kết quả và xóa nó khỏi bản sao mảng nguồn . Lặp lại hành động này cho đến khi mảng nguồn trống .
Và nếu bạn thực sự muốn nó ngắn, đây là khoảng cách tôi có thể nhận được:
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
là một cách không hiệu quả khủng khiếp để làm điều mà họ gọi là "nổi bật". Nếu bạn không muốn thay đổi mảng ban đầu, thì chỉ cần sao chép nó, sau đó xáo trộn bản sao đó tại chỗ bằng cách sử dụng biến thể Durstenfeld hiệu quả hơn nhiều.
splice
phương pháp để tạo một bản sao như vậy : source = array.slice();
.
Đây là cái DỄ DÀNG NHẤT ,
function shuffle(array) {
return array.sort(() => Math.random() - 0.5);
}
ví dụ thêm, bạn có thể kiểm tra nó ở đây
một triển khai khác của Fisher-Yates, sử dụng chế độ nghiêm ngặt:
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;
}
Tất cả các câu trả lời khác đều dựa trên Math.random () nhanh nhưng không phù hợp với ngẫu nhiên mức mã hóa.
Đoạn mã dưới đây đang sử dụng Fisher-Yates
thuật toán nổi tiếng trong khi sử dụng Web Cryptography API
cho mức độ ngẫu nhiên mã hóa .
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));
Một sửa đổi đơn giản cho câu trả lời của CoolAJ86 không sửa đổi mảng ban đầu:
/**
* 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;
};
Mặc dù có một số cách triển khai đã được khuyên dùng nhưng tôi cảm thấy chúng ta có thể làm cho nó ngắn hơn và dễ dàng hơn bằng cách sử dụng vòng lặp forEach, vì vậy chúng ta không cần lo lắng về việc tính toán độ dài mảng và chúng ta cũng có thể tránh sử dụng biến tạm thời một cách an toàn.
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)
Chỉ cần có một ngón tay trong chiếc bánh. Ở đây tôi trình bày một triển khai đệ quy của Fisher Yates shuffle (tôi nghĩ). Nó cho sự ngẫu nhiên thống nhất.
Lưu ý: ~~
(toán tử dấu ngã kép) trên thực tế hoạt động giống như Math.floor()
đối với các số thực dương. Chỉ cần một đoạn ngắn là nó.
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])));
Chỉnh sửa: Mã ở trên là O (n ^ 2) do việc làm của .splice()
nhưng chúng ta có thể loại bỏ mối nối và xáo trộn trong O (n) bằng thủ thuật hoán đổi.
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");
Vấn đề là, JS không thể hợp tác với các cuộc thu hồi lớn. Trong trường hợp cụ thể này, kích thước mảng của bạn bị giới hạn với khoảng 3000 ~ 7000 tùy thuộc vào công cụ trình duyệt của bạn và một số sự kiện chưa biết.
Ngẫu nhiên mảng
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 như bạn đã sử dụng <
không<=
arrayShuffle
chức năng ngắn nhất
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;
}
Theo quan điểm lý thuyết, cách thức thanh lịch nhất, theo ý kiến khiêm tốn của tôi, là lấy một số ngẫu nhiên duy nhất trong khoảng từ 0 đến n! -1 và tính toán một ánh xạ từ một {0, 1, …, n!-1}
đến tất cả các hoán vị của(0, 1, 2, …, n-1)
. Miễn là bạn có thể sử dụng một trình tạo ngẫu nhiên (giả) đủ tin cậy để có được một số như vậy mà không có bất kỳ sai lệch đáng kể nào, bạn có đủ thông tin trong đó để đạt được những gì bạn muốn mà không cần một số số ngẫu nhiên khác.
Khi tính toán với các số trôi nổi chính xác kép của IEEE754, bạn có thể mong đợi trình tạo ngẫu nhiên của mình cung cấp khoảng 15 số thập phân. Vì bạn có 15! = 1.307,674,368,000 (có 13 chữ số), bạn có thể sử dụng các hàm sau với mảng chứa tối đa 15 phần tử và giả sử sẽ không có sai lệch đáng kể với mảng chứa tối đa 14 phần tử. Nếu bạn làm việc với một vấn đề kích thước cố định cần phải tính toán nhiều lần thao tác xáo trộn này, bạn có thể muốn thử đoạn mã sau có thể nhanh hơn các mã khác vì nó Math.random
chỉ sử dụng một lần (tuy nhiên nó liên quan đến một số thao tác sao chép).
Hàm sau sẽ không được sử dụng, nhưng dù sao thì tôi cũng cung cấp cho nó; nó trả về chỉ số của một hoán vị nhất định (0, 1, 2, …, n-1)
theo ánh xạ từ một đến một được sử dụng trong thông điệp này (một cách tự nhiên nhất khi liệt kê các hoán vị); nó được dự định để làm việc với tối đa 16 yếu tố:
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);
}
Đối ứng của hàm trước (cần thiết cho câu hỏi của riêng bạn) bên dưới; nó được dự định để làm việc với tối đa 16 yếu tố; nó trả về hoán vị của thứ tự n của (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;
}
Bây giờ, những gì bạn muốn chỉ là:
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]; });
}
Nó nên hoạt động cho tối đa 16 yếu tố với một chút sai lệch về lý thuyết (mặc dù không được chú ý từ quan điểm thực tế); nó có thể được xem là hoàn toàn có thể sử dụng cho 15 yếu tố; với các mảng chứa ít hơn 14 phần tử, bạn có thể cân nhắc một cách an toàn sẽ không có sai lệch.