Вот идея: Вы можете сделать деление на интервалы.
size s
- это постоянное время.Используйте randomR
, чтобы узнать, насколько далеко в выбранный вами набор. - Делайте
split
с различными значениями между исходными findMin
и findMax
, пока вы не получите элемент в нужной позиции.Если вы действительно боитесь, что набор состоит, скажем, из реалов и чрезвычайно тесно сгруппирован, вы можете пересчитывать findMin
и findMax
каждый раз, чтобы гарантировать выбивание некоторых элементов каждый раз.
производительность будет O (n log n), в основном не хуже, чем ваше текущее решение, но с довольно слабыми условиями, чтобы набор не был полностью кластеризован вокруг некоторой точки накопления, средняя производительность должна быть ~ ((logn) ^2), что является довольно постоянным.Если это набор целых чисел, вы получите O (log n * log m), где m - начальный диапазон набора;это только реальные данные, которые могут привести к очень плохой производительности в интервале деления (или других типах данных, чей тип заказа имеет точки накопления).
PS.Это обеспечивает идеально равномерное распределение, если только следить за тем, чтобы отдельные элементы находились сверху и снизу.
Редактирование: добавлено 'code'
Какой-то не элегантный, непроверенный (псевдо?) Код.На моем компьютере нет компилятора для тестирования дыма, возможен случайный отказ, и он, вероятно, может быть выполнен с меньшим количеством if
с.Одно: посмотрите, как генерируется mid
;потребуется некоторая настройка в зависимости от того, ищете ли вы что-то, что работает с наборами целых или действительных чисел (интервальное деление по своей сути топологически, и не должно работать совершенно одинаково для наборов с разными топологиями).
import Data.Set as Set
import System.Random (getStdGen, randomR, RandomGen)
getNth (s, n) = if n = 0 then (Set.findMin s) else if n + 1 = Set.size s then Set.findMax s
else if n < Set.size bott then getNth (bott, n) else if pres and Set.size bott = n then n
else if pres then getNth (top, n - Set.size bott - 1) else getNth (top, n - Set.size)
where mid = ((Set.findMax s) - (Set.findMin s)) /2 + (Set.findMin s)
(bott, pres, top) = (splitMember mid s)
randElem s g = (getNth(s, n), g')
where (n, g') = randomR (0, Set.size s - 1) g