Size: a a a

Clojure — русскоговорящее сообщество

2020 February 07

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Andrey Ivanov
Да. И чтобы не реализовывать это неоптимальный по времени и памяти алгоритм, был придуман описанный постом выше )
Вроде в императивном виде то, что я описал, очень даже эффективный алгоритм.
источник

VM

Vyacheslav Mikushev in Clojure — русскоговорящее сообщество
Mikhail Borisov
Предположим, что мы имеем дело не с трансдьюсерами, а с вектором. Тогда нужно сначала случайно выбрать один из n элементов. Затем убрать его и выбрать случайно из (n-1) элементов и тд. Адекватно это лучше всего сделать через transient, свапать выбранный на k-ом шаге с k-м с конца (и соответственно генерировать случайный индекс от 1 до (n-k))
Можно не убирать, а складывать выбранные индексы в сет и проверять, если такой индекс уже выбран, то сгенерировать новый. Вероятно, это будет быстрее, чем манипуляции с коллекцией.
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Vyacheslav Mikushev
Можно не убирать, а складывать выбранные индексы в сет и проверять, если такой индекс уже выбран, то сгенерировать новый. Вероятно, это будет быстрее, чем манипуляции с коллекцией.
Да, я знаю, что так можно. Разве сгенерировать новый индекс быстрее, чем свапнуть два элемента в массиве?
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Особенно плохо будет, когда надо будет 9 элементов из 10 выбрать, когда будем брать последний, придется кучу раз "перебрасывать кубик"
источник

VM

Vyacheslav Mikushev in Clojure — русскоговорящее сообщество
Свапнуть? Имеется ввиду, что выбранный элемент мы перемещаем в позицию n-m, а random делаем для оставшейся части вектора?
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Vyacheslav Mikushev
Свапнуть? Имеется ввиду, что выбранный элемент мы перемещаем в позицию n-m, а random делаем для оставшейся части вектора?
Да
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Andrey Ivanov
ЗЫ для ценителей расскажу словами еще один алгоритм. Отрезок [1;n] разбиваем пополам, и решаем задачу рекурсивно, разделяя K на две НЕРАВНЫЕ (очевидно, т.к. при равных будет эмуляция равномерной сетки, заполняющей интервал [1;n] с равномерным шагом) части по БИНОМИАЛЬНОМУ РАСПРЕДЕЛЕНИЮ (отлично аппроксимируется обычным Гауссом), далее каждую половину интервала так же разбиваем пополам с разделением части K этой половины интервала аналогично и т.д. Когда дошли до K=N берем все точки интервала, когда K=0 прекращаем безобразие и ничего не берем из этого интервала. Нафига такие сложности - алгоритм выше имеет временнУю сложность O(n), а описанный O(k). Если k сравнимо с n, то проще не морочиться и взять однострочник выше. Если n велико, а k относительно мало - то описанный алгоритм.
А есть ссылочка?
источник

VM

Vyacheslav Mikushev in Clojure — русскоговорящее сообщество
Похоже, что быстрее. Своп по скорости такой же как и rand. Значит, один Своп быстрее пары rand'ов. 😁 Но лучше написать алгоритмы и проверить.
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Vyacheslav Mikushev
Похоже, что быстрее. Своп по скорости такой же как и rand. Значит, один Своп быстрее пары rand'ов. 😁 Но лучше написать алгоритмы и проверить.
Я же не persistent vector предлагаю своповать
источник

ST

Sergey Trofimov in Clojure — русскоговорящее сообщество
Mikhail Borisov
Предположим, что мы имеем дело не с трансдьюсерами, а с вектором. Тогда нужно сначала случайно выбрать один из n элементов. Затем убрать его и выбрать случайно из (n-1) элементов и тд. Адекватно это лучше всего сделать через transient, свапать выбранный на k-ом шаге с k-м с конца (и соответственно генерировать случайный индекс от 1 до (n-k))
Или просто сделать shuffle?
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Sergey Trofimov
Или просто сделать shuffle?
Ага
источник

AI

Andrey Ivanov in Clojure — русскоговорящее сообщество
Mikhail Borisov
А что такое n и k здесь?
размер коллекции и количество требуемых случайных элементов. в первом и втором алгоритмах они перепутаны, так как описание второго копипастил как писал на другой форум и лень было менять, но там и так все понятно
источник

AI

Andrey Ivanov in Clojure — русскоговорящее сообщество
Mikhail Borisov
А есть ссылочка?
Ссылочка на что?
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Я не совсем понял описание алгоритма со слов. Например "Когда дошли до K=N...", не очень понятно, как мы доходим :)
источник

AI

Andrey Ivanov in Clojure — русскоговорящее сообщество
Mikhail Borisov
Вроде в императивном виде то, что я описал, очень даже эффективный алгоритм.
В случае императивного вектора и малого количества требуемых случайных элементов по сравнению с его размером - да, по времени будет более-менее. Но по памяти все равно придется разворачивать весь массив. Мой алгоритм по памяти О(1), и может выдавать ленивую последовательность по запросу (что он и делает)
источник

AI

Andrey Ivanov in Clojure — русскоговорящее сообщество
Mikhail Borisov
Я не совсем понял описание алгоритма со слов. Например "Когда дошли до K=N...", не очень понятно, как мы доходим :)
Давай на примере. Есть 10 элементов  -  [1,2,3,4,5,6,7,8,9,10], надо выбрать 8 случайных. Делим массив пополам, по биномиальному рандому получаем что из первой половины надо выбрать 5 элементов, а из второй 3. Значит из первой берем все: [1,2,3,4,5], а со второй продолжаем рекурсивный процесс - делим пополам и т.д.
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Andrey Ivanov
Давай на примере. Есть 10 элементов  -  [1,2,3,4,5,6,7,8,9,10], надо выбрать 8 случайных. Делим массив пополам, по биномиальному рандому получаем что из первой половины надо выбрать 5 элементов, а из второй 3. Значит из первой берем все: [1,2,3,4,5], а со второй продолжаем рекурсивный процесс - делим пополам и т.д.
Кайф, прикольный алгоритм
источник

MB

Mikhail Borisov in Clojure — русскоговорящее сообщество
Andrey Ivanov
Не понял при чем здесь трансдьюсеры, но вот как - с вероятностью n/k берем первый элемент, ииии..... все ))) пришли к той же задаче на остатке элементов, рекурсия )
А вот это? Мне показалось, что тут не факт, что k элементов наберется
источник

AI

Andrey Ivanov in Clojure — русскоговорящее сообщество
Да, когда надо выбрать малое число элементов из большой последовательности он особенно эффективен. Но на практике я реализовал свой первый как более простой и универсальный
источник

AI

Andrey Ivanov in Clojure — русскоговорящее сообщество
Давай опять на примере ))) 10 элементов, надо выбрать 8. говори первый рандом от 0 до 1
источник