Как перетасовать символы в строке в JavaScript?
В частности, я хочу избежать ошибки, допущенной в коде перемешивания браузера Browser Choice от Microsoft. То есть я хочу убедиться, что каждая буква имеет равную вероятность оказаться в каждой возможной позиции.
Например, учитывая "ABCDEFG", вернуть что-то вроде "GEFBDCA".
16 ответов
Я изменил пример из записи Fisher-Yates Shuffle в Википедии, чтобы перемешать строки:
String.prototype.shuffle = function () {
var a = this.split(""),
n = a.length;
for(var i = n - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
return a.join("");
}
console.log("the quick brown fox jumps over the lazy dog".shuffle());
//-> "veolrm hth ke opynug tusbxq ocrad ofeizwj"
console.log("the quick brown fox jumps over the lazy dog".shuffle());
//-> "o dt hutpe u iqrxj yaenbwoolhsvmkcger ozf "
Дополнительную информацию можно найти в ответе Джона Скита на " Правильно ли использовать метод JavaScript Array.sort() для перетасовки?,
Если "действительно" случайность важна, я рекомендую против этого. Смотрите мой ниже редактировать.
Я просто хотел добавить свой любимый метод для небольшого разнообразия;)
Учитывая строку:
var str = "My bologna has a first name, it's O S C A R.";
Перемешать в одну строку:
var shuffled = str.split('').sort(function(){return 0.5-Math.random()}).join('');
Выходы:
oa, a si'rSRn f gbomi. aylt AtCnhO ass eM
as'oh ngS li Ays.rC nRamsb Oo ait a ,eMtf
y alCOSf e gAointsorasmn bR Ms .' ta ih,a
РЕДАКТИРОВАТЬ: Как @PleaseStand указал, это не отвечает на вопрос OP, так как он страдает от кода "Microsoft Browser Choice shuffle". Это не очень хороший рандомизатор, если ваша строка должна быть близка к случайной. Тем не менее, это удивительно - быстро "перемешивать" ваши строки, где "истинная" случайность не имеет значения.
Статья, на которую он ссылается ниже, отлично читается, но объясняет совершенно другой вариант использования, который влияет на статистические данные. Лично я не могу представить практическую проблему с использованием этой "случайной" функции в строке, но как кодировщик, вы должны знать, когда не следует использовать это.
Я оставил это здесь для всех случайных рандомизаторов.
Кратчайший один лайнер: не гарантирует статистически равного распределения, но может использоваться в большинстве случаев.
const shuffle = str => str.split('').sort(()=>(Math.random()-0.5)).join('');
const shuffle = str => str.split('').sort(()=>(Math.random()-0.5)).join('');
document.write(shuffle("The quick brown fox jumps over the lazy dog"));
Несмотря на то, что на этот вопрос уже дан ответ, я хотел бы поделиться решением, которое придумал:
function shuffelWord (word){
var shuffledWord = '';
word = word.split('');
while (word.length > 0) {
shuffledWord += word.splice(word.length * Math.random() << 0, 1);
}
return shuffledWord;
}
// 'Batman' => 'aBmnta'
Вы также можете попробовать это (jsfiddle).
Следующие однострочники беспристрастны:
Фишер-Йейтс - Возьми из одного списка и положи в другой или на себя (Дурстенфельд/Кнут):
"ABCDEFG".split("").map((v, i, a) => (r => ([v, a[r]] = [a[r], v], v))(Math.floor(Math.random() * (a.length - i)) + i)).join("")
Преобразование Шварца . Прикрепите случайные значения к каждому элементу и отсортируйте по ним:
"ABCDEFG".split("").map(v => [v, Math.random()]).sort((a, b) => a[1] - b[1]).map(v => v[0]).join("")
Однолинейное решение и ограниченная длина вывода ...
var rnd = "ABCDEF23456789".split('').sort(function(){return 0.5-Math.random()}).join('').substring(0,6);
Используя алгоритм перемешивания Фишера-Йейтса и ES6:
// Original string
let string = 'ABCDEFG';
// Create a copy of the original string to be randomized ['A', 'B', ... , 'G']
let shuffle = [...string];
// Defining function returning random value from i to N
const getRandomValue = (i, N) => Math.floor(Math.random() * (N - i) + i);
// Shuffle a pair of two elements at random position j (Fisher-Yates)
shuffle.forEach( (elem, i, arr, j = getRandomValue(i, arr.length)) => [arr[i], arr[j]] = [arr[j], arr[i]] );
// Transforming array to string
shuffle = shuffle.join('');
console.log(shuffle);
// 'GBEADFC'
Вы не можете перетасовать
string
на месте, но вы можете использовать это:
Просто для полноты, даже если это может быть не совсем то, что было после OP, поскольку на этот конкретный вопрос уже был дан ответ.
Вот тот, который перемешивает слова.
Вот объяснение этого регулярного выражения: https://regex101.com/r/aFcEtk/1
И у этого также есть забавные результаты.
// Shuffles words
// var str = "1 2 3 4 5 6 7 8 9 10";
var str = "the quick brown fox jumps over the lazy dog A.S.A.P. That's right, this happened.";
var every_word_im_shuffling = str.split(/\s\b(?!\s)/).sort(function(){return 0.5-Math.random()}).join(' ');
console.log(every_word_im_shuffling);
shuffleString = function(strInput){
var inpArr = strInput.split("");//this will give array of input string
var arrRand = []; //this will give shuffled array
var arrTempInd = []; // to store shuffled indexes
var max = inpArr.length;
var min = 0;
var tempInd;
var i =0 ;
do{
tempInd = Math.floor(Math.random() * (max - min));//to generate random index between range
if(arrTempInd.indexOf(tempInd)<0){ //to check if index is already available in array to avoid repeatation
arrRand[i] = inpArr[tempInd]; // to push character at random index
arrTempInd.push(tempInd); //to push random indexes
i++;
}
}
while(arrTempInd.length < max){ // to check if random array lenght is equal to input string lenght
return arrRand.join("").toString(); // this will return shuffled string
}
};
Просто передайте строку в функцию и взамен получите перемешанную строку
String.prototype.shuffle=function(){
var that=this.split("");
var len = that.length,t,i
while(len){
i=Math.random()*len-- |0;
t=that[len],that[len]=that[i],that[i]=t;
}
return that.join("");
}
Другой подход к набору слов. Все остальные ответы с достаточным количеством итераций вернут слово без шифрования, а у меня - нет.
var scramble = word => {
var unique = {};
var newWord = "";
var wordLength = word.length;
word = word.toLowerCase(); //Because why would we want to make it easy for them?
while(wordLength != newWord.length) {
var random = ~~(Math.random() * wordLength);
if(
unique[random]
||
random == newWord.length && random != (wordLength - 1) //Don't put the character at the same index it was, nore get stuck in a infinite loop.
) continue; //This is like return but for while loops to start over.
unique[random] = true;
newWord += word[random];
};
return newWord;
};
scramble("God"); //dgo, gdo, ogd
Rando.js использует криптографически безопасную версию перетасовки Фишера-Йейтса, она довольно короткая и удобочитаемая.
console.log(randoSequence("This string will be shuffled.").join(""));
<script src="https://randojs.com/2.0.0.js"></script>
Если бы у меня была куча букв на столе (как в скрэббле, например), я бы их перетасовывал. Эта функция имитирует этот процесс. Я просто выбираю буквы случайным образом, по одной букве за раз, удаляю их из текущего местоположения и помещаю в новое место.
Я не видел необходимости использовать что-либо, кроме цикла while и основных строковых операций.
function stringScrambler(theString) {
let scrambledString = "";
while (theString.length > 0) {
//pick a random character from the string
let characterIndex = Math.floor(Math.random() * (theString.length));
let theCharacter = theString[characterIndex];
//add that character to the new string
scrambledString += theCharacter;
//remove that character from the original string
theString = theString.slice(0, characterIndex) + theString.slice(characterIndex + 1, theString.length);
}
return scrambledString;
}
Еще одна реализация Фишера-Йейтса:
const str = 'ABCDEFG',
shuffle = str =>
[...str]
.reduceRight((res,_,__,arr) => (
res.push(...arr.splice(0|Math.random()*arr.length,1)),
res) ,[])
.join('')
console.log(shuffle(str))
.as-console-wrapper{min-height:100%;}
String.prototype.shuffle = function(){
return this.split('').sort(function(a,b){
return (7 - (Math.random()+'')[5]);
}).join('');
};