Javascript sort array random

How to shuffle an array in JavaScript

As the function we pass to .sort() is looking for either a positive or negative number to either move the item ‘up’ or ‘down’ in the array, each item has a chance of being moved in either direction giving us a shuffled array of items. This works for a rough-and-ready approach but might not give you a truly random shuffle. If you do a bit of research in to the above technique (check out this article), you’ll see that using the custom sort function is flawed (although I can’t give you a definitive answer as to why that is!). If you need to shuffle an array and have a truly random distribution of items, you need to implement the Fisher-Yates algorithm.

The Fisher-Yates algorith

const shuffleArray = array =>  for (let i = array.length - 1; i > 0; i--)  const j = Math.floor(Math.random() * (i + 1)); const temp = array[i]; array[i] = array[j]; array[j] = temp; > > 

As you can see it’s just a case of looping through the array (from the end to the start) and picking a random item from the array and swapping it with the item in the current iteration. You can use the above function to shuffle an array in JavaScript and get a random result each time.

Источник

Javascript sort array random

Напишите функцию shuffle(array) , которая перемешивает (переупорядочивает случайным образом) элементы массива.

Многократные прогоны через shuffle могут привести к разным последовательностям элементов. Например:

let arr = [1, 2, 3]; shuffle(arr); // arr = [3, 2, 1] shuffle(arr); // arr = [2, 1, 3] shuffle(arr); // arr = [3, 1, 2] // . 

Все последовательности элементов должны иметь одинаковую вероятность. Например, [1,2,3] может быть переупорядочено как [1,2,3] или [1,3,2] , или [3,1,2] и т.д., с равной вероятностью каждого случая.

Простым решением может быть:

function shuffle(array) < array.sort(() =>Math.random() - 0.5); > let arr = [1, 2, 3]; shuffle(arr); alert(arr);

Это, конечно, будет работать, потому что Math.random() — 0.5 отдаёт случайное число, которое может быть положительным или отрицательным, следовательно, функция сортировки меняет порядок элементов случайным образом.

Но поскольку метод sort не предназначен для использования в таких случаях, не все возможные варианты имеют одинаковую вероятность.

Например, рассмотрим код ниже. Он запускает shuffle 1000000 раз и считает вероятность появления для всех возможных вариантов arr :

function shuffle(array) < array.sort(() =>Math.random() - 0.5); > // подсчёт вероятности для всех возможных вариантов let count = < '123': 0, '132': 0, '213': 0, '231': 0, '321': 0, '312': 0 >; for (let i = 0; i < 1000000; i++) < let array = [1, 2, 3]; shuffle(array); count[array.join('')]++; >// показать количество всех возможных вариантов for (let key in count) < alert(`$: $`); >

Результат примера (зависят от движка JS):

123: 250706 132: 124425 213: 249618 231: 124880 312: 125148 321: 125223

Теперь мы отчётливо видим допущенное отклонение: 123 и 213 появляются намного чаще, чем остальные варианты.

Результаты этого кода могут варьироваться при запуске на разных движках JavaScript, но очевидно, что такой подход не надёжен.

Так почему это не работает? Если говорить простыми словами, то sort это «чёрный ящик»: мы бросаем в него массив и функцию сравнения, ожидая получить отсортированный массив. Но из-за абсолютной хаотичности сравнений чёрный ящик сходит с ума, и как именно он сходит с ума, зависит от конкретной его реализации, которая различна в разных движках JavaScript.

Есть и другие хорошие способы решить эту задачу. Например, есть отличный алгоритм под названием Тасование Фишера — Йетса. Суть заключается в том, чтобы проходить по массиву в обратном порядке и менять местами каждый элемент со случайным элементом, который находится перед ним.

function shuffle(array) < for (let i = array.length - 1; i >0; i--) < let j = Math.floor(Math.random() * (i + 1)); // случайный индекс от 0 до i // поменять элементы местами // мы используем для этого синтаксис "деструктурирующее присваивание" // подробнее о нём - в следующих главах // то же самое можно записать как: // let t = array[i]; array[i] = array[j]; array[j] = t [array[i], array[j]] = [array[j], array[i]]; >>

Давайте проверим эту реализацию на том же примере:

Источник

Пятничный JS: случайное перемешивание

Всем привет. С ортодоксальной точки зрения сегодня не настоящая пятница — просто день, когда завтра выходной . Поэтому статья в моей традиционной рубрике тоже будет не совсем настоящая, у неё пониженный градус безумия и повышенная полезность. Однако довольно предисловий, перейдём к сути.

Перед моими студентами регулярно встаёт задача случайного перемешивания массива. За её решением они, как правило, лезут в гугл. И гугл им подсказывает следующее:

var shuffledArr = arr.sort(function()< return Math.random() - 0.5; >); 

Здесь и далее будем называть этот метод случайной сортировкой. Сегодня я решил написать о том, какие преимущества и недостатки есть у такого подхода.

Как это вообще работает?

Метод sort у джаваскриптовых массивов в качестве аргумента принимает функцию-компаратор. Эта функция должна принимать два элемента массива и возвращать число. Если число положительное, алгоритм сортировки считает, что первый элемент больше; если отрицательное — значит, первый элемент меньше; если же компаратор возвращает нуль, то в рамках данной сортировки элементы как бы равны. Если под видом компаратора передать функцию, которая будет возвращать положительное или отрицательное число случайным образом, то массив окажется отсортирован в «случайном» порядке.

Преимущества

Такое перемешивание очень быстро пишется. Я честно пытался придумать какое-нибудь ещё преимущество, но мне не удалось.

Недостатки

Этот параграф будет несколько длиннее, потому я разобью его на подпараграфы.

Несоответствие спецификации

Спецификация EcmaScript (я намеренно не называю версию, поскольку во всех версиях этот пункт остаётся примерно одинаковым) заявляет нам следующее:

If comparefn is not undefined and is not a consistent comparison function for the elements of this array (see below), the sort order is implementation-defined.

В переводе с технического английского на русский разговорный это означает, что если компаратор не отвечает некоторым очевидным требованиям (ниже в спецификации разъясняется, что, в частности, он должен для одних и тех же аргументов всегда возвращать одно и то же значение), то порядок сортировки зависит от реализации конкретного джаваскриптового движка. То есть, фактически, не определён. Разработчик браузера имеет, например, полное право сделать так, что при обнаружении того, что компаратор «ненастоящий», возвращается исходный массив без каких-либо перестановок. И это будет полностью соответствовать спецификации. То, что во всех существующих браузерах приведённый метод даёт нечто похожее на случайное перемешивание — не более чем счастливое совпадение.

Временна́я сложность

Корректный алгоритм перемешивания (см. ниже) имеет временную сложность O(n). Попросту говоря, это означает примерно следующее: если длина массива увеличится в 10 раз, то продолжительность его перемешивания также увеличится в 10 раз.

Самые быстрые алгоритмы сортировки имеют временную сложность O(n*log(n)). Это означает, что при увеличении длины массива в 10 раз продолжительность его перемешивания увеличится более чем в 10 раз.

В сумме эти два факта значат вот что: для достаточно большого массива перемешивание случайной сортировкой будет работать медленнее, чем «правильное» перемешивание (даже если для маленьких массивов это было не так). И чем больше массив, тем больше разница во времени выполнения.

Почему я сделал оговорку в скобках? Потому что Array#sort выполняется нативным кодом, и за счёт этого на небольших массивах потенциально может оказаться быстрее. У него может оказаться меньше константный множитель, сказал бы человек, знакомый с О-нотацией.

Ненастоящая случайность

Те, кто хотя бы поверхностно знаком с теорией вероятностей, знают, что случайность случайности рознь. Монетка может выпасть орлом или решкой, кубик может выпасть шестёркой или не шестёркой. И там, и там имеют место случайные события, однако в первом случае события равновероятны, а во втором нет.

Перемешивание массива называется истинно случайным, если все возможные перестановки его элементов имеют одинаковую вероятность. Именно этим свойством случайная сортировка не обладает, и я покажу это на практике.

Я набросал следующую страничку. На ней находятся две диаграммы, одна соответствует случайному перемешиванию, вторая — случайной сортировке. Впрочем, вы сейчас видите не диаграммы, а квадратики, разбитые на клеточки различных оттенков серого. Чтобы они стали диаграммами, нужна легенда — объяснение, что эти клеточки и их цвета означают. Всё очень просто. Мы несколько раз (в данном случае несколько = 10000) берём массив чисел от 0 до n (в данном случае n = 32) и случайно перемешиваем тем или иным алгоритмом. Затем мы вычисляем частоты, с которыми то или иное число оказывается на том или ином месте. Соответственно, цвет клетки в строке номер i и столбце номер j показывает, насколько часто число i оказывается на месте j. Чёрный цвет означает, что оно там не оказывается никогда, белый — что оно там оказывается вдвое или более чаще, чем положено. Если число попадает на указанное место с теоретически предсказанной частотой 1/n, клетка будет иметь цвет hsl(0, 0%, 50%) — оттенок серого, расположенный в точности посередине между чёрным и белым.

Если вы используете свежую версию браузера Chrome, вы видите, что в квадрате справа много белых или почти белых клеток, расположенных по определённой закономерности. Это означает, что при случайной сортировке определённые элементы имеют тенденцию оказываться в определённых местах. Плохо ли это? Зависит от того, для чего вы используете перемешивание. Если для каких-то косметических эффектов, то, возможно, ничего страшного. Если вам важно, чтобы пользователь не мог предсказать, какой элемент окажется на каком месте, или если закономерности в перемешивании каким-то образом заметны визуально — то плохо. И упаси вас Гермес использовать такое перемешивание для чего-то, связанного с криптографией.

Что удивительно, если вы используете Firefox, то для вас оба квадрата примерно одинаково серые. Это происходит оттого, что в разных браузерах используются различные алгоритмы сортировки (если интересно, вот моя статья на эту тему). В таком случае, если хотите удивиться ещё раз, допишите в адресной строке ?size=8 (вот готовая ссылка для ленивых). Firefox по-разному сортирует большие и маленькие массивы, сюрприз!

Upd: товарищ mk2 заметил, что равномерно-серым квадрат в Firefox будет только при размере, равном степени двойки. Нужно больше таких внимательных товарищей, товарищи!

Upd2: товарищи Stalker_RED и yaZva не поленились (в отличие от меня) сделать скрины в различных браузерах.

Напоследок добавлю, что вот эти мои диаграммы с пятьюдесятью оттенками серого — не критерий, а признак. Из того, что квадрат получился не серый, следует, что сортировка не равномерна, однако неверно обратное. Контрпример — циклический сдвиг на случайную величину. Частоты попадания элементов на места будут совершенно одинаковы, однако ни о какой истинной случайности перемешивания, разумеется, не может быть и речи.

Upd3: в этой ветке обсуждается, почему случайная сортировка не даёт равномерную случайность в Firefox (причём даже в том случае, если по диаграмме кажется, что даёт).

Как правильно?

Истинные джедаи используют одну из вариаций алгоритма Фишера-Йетса. Для своей демки я реализовал его так:

function shuffle(arr) < var j, temp; for(var i = arr.length - 1; i >0; i--) < j = Math.floor(Math.random()*(i + 1)); temp = arr[j]; arr[j] = arr[i]; arr[i] = temp; >return arr; >

Суть алгоритма, если перевести с JS на русский, следующая: берём последний элемент и меняем его местами со случайно выбранным элементом не правее его (в том числе, возможно, и с ним самим). Затем повторяем ту же операцию для предпоследнего элемента, потом для предпредпоследнего и так далее. Вуаля! (этим словом с JS на русский переводится «return arr;»).

Настало время безумия

Кто-то из читателей ждал этого целую статью, остальные, в принципе, могут этот параграф не дочитывать. Я задался вопросом: можно ли написать такую функцию compare, что arr.sort(compare) даст истинно случайную перестановку? Ответ: можно, но с определёнными оговорками. Первая — функцию надо перед каждой сортировкой создавать заново. Вторая — в массиве не должно быть одинаковых элементов. Итак, узрите:

//вспомогательная функция function putToCache(elem, cache) < if(cache.indexOf(elem) != -1)< return; >var i = Math.floor(Math.random()*(cache.length + 1)); cache.splice(i, 0, elem); > //функция, возвращающая свеженький, девственный компаратор function madness() < var cache = []; return function(a, b)< putToCache(a, cache); putToCache(b, cache); return cache.indexOf(b) - cache.indexOf(a); >> //собственно функция перемешивания function shuffle(arr)

Это работает следующим образом: при создании компаратор через замыкание получает доступ к массиву cache. Каждый раз, когда ему передаются аргументы, он кладёт их в cache на случайные места (если их там ещё нет), а затем считает, что тот элемент, который в cache стоит правее, будет больше. То есть по сути в массиве cache постепенно строится тот случайный порядок, в котором элементы должны стоять, а метод sort постепенно приводит исходный массив в соответствии с этим порядком. Если же в нём окажутся равные элементы (равные с точки зрения оператора ===, если мы сортируем объекты — то всё хорошо, даже если у них одинаковое содержание. !== ), они, к сожалению, будут идти подряд.

На этом всё. Спасибо за чтение, надеюсь, вам было познавательно, и особенно надеюсь, что я убедил вас не использовать случайную сортировку почти никогда. Приятного грядущего вечера.

Источник

Читайте также:  Днем рождения поздравляю html
Оцените статью