У меня есть такой массив:
var arr1 = ["a", "b", "c", "d"];
Как я могу рандомизировать / перемешать это?
javascript
arrays
random
shuffle
Нажмите Upvote
источник
источник
Ответы:
Де-факто беспристрастным алгоритмом тасования является тасовка Фишера-Йейтса (он же Кнут).
Смотрите https://github.com/coolaj86/knuth-shuffle
Вы можете увидеть отличную визуализацию здесь (и оригинальный пост, связанный с этим )
Еще немного информации об используемом алгоритме .
источник
i--
не--i
. Кроме того , тестif (i==0)...
является излишним , так как если в то время как петля никогда не будет введен. Вызов может быть сделан быстрее, используя . Либо Tempi или tempj могут быть удалены , а значение непосредственно отнесены к туАггау [I] или J соответствующим образом .i == 0
Math.floor
...| 0
0 !== currentIndex
).Вот JavaScript-реализация Durstenfeld shuffle , оптимизированной версии Fisher-Yates:
Он выбирает случайный элемент для каждого исходного элемента массива и исключает его из следующего тиража, как случайный выбор из колоды карт.
Это умное исключение заменяет выбранный элемент текущим, а затем выбирает следующий случайный элемент из остатка, возвращаясь назад для достижения оптимальной эффективности, обеспечивая упрощение случайного выбора (оно всегда может начинаться с 0) и, таким образом, пропуская последний элемент.
Алгоритм времени выполнения есть
O(n)
. Обратите внимание, что перемешивание выполняется на месте, поэтому, если вы не хотите изменять исходный массив, сначала сделайте его копию с помощью.slice(0)
.РЕДАКТИРОВАТЬ: Обновление до ES6 / ECMAScript 2015
Новый ES6 позволяет нам назначать две переменные одновременно. Это особенно удобно, когда мы хотим поменять значения двух переменных, как мы можем сделать это в одной строке кода. Вот более короткая форма той же функции, использующей эту функцию.
источник
return array
поскольку JavaScript передает массивы по ссылке при использовании в качестве аргументов функции. Я предполагаю, что это экономит место в стеке, но это небольшая интересная особенность. Выполнение перемешивания на массиве будет перемешивать исходный массив.Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () `. См. Генерация случайных целых чисел в JavaScript в определенном диапазоне? для очень полного объяснения.источник
Можно (или нужно) использовать его как прототип из Array:
От Кристофа:
источник
for...in
циклы для перебора массивов.Вы можете сделать это легко с картой и сортировать:
Вы можете перетасовать полиморфные массивы, и сортировка так же случайна, как и Math.random, что достаточно для большинства целей.
Поскольку элементы сортируются по согласованным ключам, которые не восстанавливаются на каждой итерации, и каждое сравнение извлекается из одного и того же распределения, любая неслучайность в распределении Math.random исключается.
скорость
Временная сложность составляет O (N log N), так же, как быстрая сортировка. Пространственная сложность O (N). Это не так эффективно, как случай Фишера-Йейтса, но, на мой взгляд, код значительно короче и более функциональный. Если у вас большой массив, вы обязательно должны использовать Фишера Йетса. Если у вас есть небольшой массив с несколькими сотнями элементов, вы можете сделать это.
источник
Используйте библиотеку underscore.js. Метод
_.shuffle()
хорош для этого случая. Вот пример с методом:источник
shuffle
функцию.NEW!
Более короткий и, вероятно, * более быстрый алгоритм тасования Фишера-Йейтса
размер скрипта (с именем fy в качестве функции): 90 байт
ДЕМО http://jsfiddle.net/vvpoma8w/
* быстрее, вероятно, во всех браузерах, кроме Chrome.
Если у вас есть какие-либо вопросы просто спросить.
РЕДАКТИРОВАТЬ
да это быстрее
ВЫПОЛНЕНИЕ: http://jsperf.com/fyshuffle
используя лучшие голосующие функции.
РЕДАКТИРОВАТЬ Был расчет в избытке (не нужно --c + 1), и никто не заметил
короче (4 байта) и быстрее (проверьте это!).
Кэширование в другом месте
var rnd=Math.random
и последующее использованиеrnd()
также немного увеличит производительность больших массивов.http://jsfiddle.net/vvpoma8w/2/
Читаемая версия (используйте оригинальную версию. Это медленнее, переменные бесполезны, как замыкания & ";", сам код также короче ... возможно, прочитайте это Как "минимизировать" код Javascript , кстати, вы не можете сжать следующий код в миниатюрах javascript, как показано выше.)
источник
fy
и»shuffle prototype
, яfy
вхожу в нижней части Chrome 37 на OS X 10.9.5 (на 81% медленнее ~ 20 тыс. Операций по сравнению с ~ 100 тыс.) И Safari 7.1 - на ~ 8% медленнее. YMMV, но это не всегда быстрее. jsperf.com/fyshuffle/3Редактировать: этот ответ неверный
Смотрите комментарии и https://stackoverflow.com/a/18650169/28234 . Это оставлено здесь для справки, потому что идея не редка.
Очень простой способ для небольших массивов заключается в следующем:
Это, вероятно, не очень эффективно, но для небольших массивов это работает просто отлично. Вот пример, чтобы вы могли увидеть, насколько он случайный (или нет), и соответствует ли он вашему сценарию использования или нет.
источник
Надежный, Эффективный, Короткий
Некоторые решения на этой странице не являются надежными (они только частично рандомизируют массив). Другие решения значительно менее эффективны. С помощью
testShuffleArrayFun
(см. Ниже) мы можем проверить функции перемешивания массива на надежность и производительность. Следующие решения: надежные, эффективные и короткие (с использованием синтаксиса ES6)[Сравнительные тесты проводились с использованием
testShuffleArrayFun
других решений в Google Chrome]Перестановка массива на месте
ES6 Чистый, итеративный
Тест надежности и производительности
Как вы можете видеть на этой странице, в прошлом здесь предлагались неверные решения. Я написал и использовал следующую функцию для тестирования любых чистых (без побочных эффектов) функций рандомизации массива.
Другие решения
Другие решения просто для удовольствия.
ES6 Pure, рекурсивный
ES6 Pure с использованием array.map
ES6 Pure с использованием array.reduce
источник
[array[i], array[rand]]=[array[rand], array[i]]
? Может быть, вы можете описать, как это работает. Почему вы выбираете итерацию вниз?Добавление к @Laurens Holsts ответа. Это на 50% сжато.
источник
var b =
в цикле вместо объявления b вне цикла и присвоения егоb =
в цикле?Редактировать: этот ответ неверный
См. Https://stackoverflow.com/a/18650169/28234 . Это оставлено здесь для справки, потому что идея не редка.
https://javascript.info/task/shuffle
источник
С ES2015 вы можете использовать это:
Применение:
источник
n >>> 0
вместо~~n
. Индексы массива могут быть выше, чем 2³¹-1.Я нашел этот вариант в ответах «удалено автором» на дубликат этого вопроса. В отличие от некоторых других ответов, которые уже имеют много голосов, это:
shuffled
названиеshuffle
)Вот jsfiddle, показывающий это в использовании .
источник
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); });
- это не дает случайную сортировку, и если вы используете это, вы можете в конечном итоге смущаться.sort(function(a,b){ return a[0] - b[0]; })
если вы хотите, чтобы сортировка сравнивала значения численно. По умолчанию.sort()
компаратор лексикографический, означает , что он будет рассматривать10
быть меньше ,2
так как1
меньше2
.Math.random()
производит. (то есть лексикографический порядок такой же, как и числовой порядок при работе с числами от 0 (включительно) до 1 (исключительно))источник
источник
Вы можете сделать это легко с:
Пожалуйста, ссылки на JavaScript Сортировка массивов
источник
Рекурсивное решение:
источник
Фишер-Йейтс перемешивает в javascript. Я публикую это здесь, потому что использование двух служебных функций (swap и randInt) проясняет алгоритм по сравнению с другими ответами здесь.
источник
Прежде всего, посмотрите здесь для большого визуального сравнения различных методов сортировки в JavaScript.
Во-вторых, если вы быстро посмотрите на приведенную выше ссылку, то обнаружите, что
random order
сортировка, по-видимому, работает относительно хорошо по сравнению с другими методами, и в то же время ее чрезвычайно легко и быстро реализовать, как показано ниже:Редактировать : как указано @gregers, функция сравнения вызывается со значениями, а не с индексами, поэтому вам нужно использовать
indexOf
. Обратите внимание, что это изменение делает код менее подходящим для больших массивов, так какindexOf
выполняется за время O (n).источник
Array.prototype.sort
передает в два значения какa
иb
, а не индекс. Так что этот код не работает.функция тасования, которая не меняет исходный массив
Обновление : здесь я предлагаю относительно простой (не с точки зрения сложности ) и короткий алгоритм, который отлично подойдет для небольших массивов, но он определенно будет стоить намного дороже, чем классический алгоритм Дюрстенфельда , когда вы имеете дело с огромными массивами. Вы можете найти Дурстенфельд в одном из лучших ответов на этот вопрос.
Оригинальный ответ:
Если вы не хотите, чтобы функция shuffle изменяла исходный массив , вы можете скопировать его в локальную переменную, а затем сделать все остальное с помощью простой логики перемешивания .
Логика перемешивания : выберите случайный индекс, затем добавьте соответствующий элемент в массив результатов и удалите его из копии исходного массива . Повторяйте это действие, пока исходный массив не станет пустым .
И если вы действительно хотите это коротко, вот, как далеко я мог бы получить:
источник
splice
ужасно неэффективным способом сделать то, что они называли «вычеркиванием». Если вы не хотите изменять исходный массив, просто скопируйте его, а затем перетасуйте эту копию на место, используя гораздо более эффективный вариант Durstenfeld.splice
метод , чтобы создать копию следующим образом:source = array.slice();
.Вот самый легкий ,
для дальнейшего примера, вы можете проверить это здесь
источник
Еще одна реализация Фишера-Йейтса, использующая строгий режим:
источник
Все остальные ответы основаны на Math.random (), который быстр, но не подходит для рандомизации на криптографическом уровне.
Ниже код с использованием хорошо известного
Fisher-Yates
алгоритма в то время , используяWeb Cryptography API
для криптографической уровня рандомизации .источник
Современное короткое встроенное решение с использованием функций ES6:
(для образовательных целей)
источник
Простая модификация ответа CoolAJ86, которая не изменяет исходный массив:
источник
Несмотря на то, что есть ряд реализаций, которые уже были рекомендованы, но я чувствую, что мы можем сделать это короче и проще, используя цикл forEach, поэтому нам не нужно беспокоиться о расчете длины массива, а также мы можем безопасно избежать использования временной переменной.
источник
Просто чтобы иметь палец в пироге. Здесь я представляю рекурсивную реализацию Fisher Yates shuffle (я думаю). Это дает равномерную случайность.
Примечание.
~~
(Оператор двойной тильды) на самом деле ведет себя какMath.floor()
для положительных действительных чисел. Это просто короткий путь.Изменить: приведенный выше код O (n ^ 2) из-за использования,
.splice()
но мы можем устранить сращивания и перетасовки в O (n) трюк подкачки.Проблема в том, что JS не может работать с большими рекурсиями. В этом конкретном случае размер вашего массива ограничен примерно 3000 ~ 7000 в зависимости от вашего браузера и некоторых неизвестных фактов.
источник
Рандомизировать массив
источник
-1
для п, как вы<
не использовали<=
самая короткая
arrayShuffle
функцияисточник
С теоретической точки зрения самый элегантный способ сделать это, по моему скромному мнению, это получить одно случайное число в диапазоне от 0 до n! -1 и вычислить отображение один к одному
{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 элементов:Обратная функция предыдущей функции (требуется для вашего собственного вопроса) приведена ниже; он предназначен для работы до 16 элементов; возвращает перестановку порядка n из
(0, 1, 2, …, s-1)
:Теперь, что вы просто хотите:
Он должен работать до 16 элементов с небольшим теоретическим уклоном (хотя и незаметным с практической точки зрения); его можно рассматривать как полностью пригодный для 15 элементов; с массивами, содержащими менее 14 элементов, вы можете смело считать, что смещения не будет абсолютно.
источник