[CLOSED] [[my]sql] получить рандомную строку в зависимости от ее веса
какое распределение для весов ты закладываешь в слова "получить рандомную строку в зависимости от веса?"
на 1000 селектов получить 200 item3, 300 item2 и 500 item1
тебе надо создать столбец, в котором будут абсолютные веса
т.е. в твоем случае это будет
item1 50 50
item2 30 80
item3 20 100
ну а дальше надо выбрать максимум из минимов по рандому от 100
типа select max(abs_weight) where abs_weight < my_rand_number
кстати, этот третий столбец проще заранее создавать, например триггером
onInsert: my_abs_weight = select max + my_abs_weight
onUpdate, onDelete: recalc all where abs_weight > my_abs_weight
но можно и каждый раз вычислять, селект вродь не сложно такой написать
select * from items order by rand*weight desc limit 1;
На практике перебор всей таблицы с Using temporary; Using filesort если таблица не совсем маленькая - будет печально.
вот так и хотел
Нет, табличка маленькая, то, что надо.
прикольна
когда там 10-50-100 записей, это не так важно, думаю
select * from items order by rand*weight desc limit 1;вощем
думал я думал
думал я думал
твоё решение не верно
ну в том смысле что вероятность что выпадет отрезок с весом 10 у тебя не 10
но в принципе от весов твоя формула тоже зависит
твоя задача фактически сводится к такой:
у нас есть два отрезка
(0,3) и (0,97)
какова вероятность что случайно выбранное число с первого отрезка будет больше случайно выбранного числа со второго отрезка?
Провёл несколько тестиков 10000 запросов:
веса 1,2,3:
[1]=>
int(517)
[2]=>
int(3146)
[3]=>
int(6337)
явная несправедливость.
дальше вместо rand*weight берётся rand*pow(weight,.5);
[1]=>
int(1527)
[2]=>
int(3161)
[3]=>
int(5312)
ближе к истине
добавим вес 6:
[1]=>
int(447)
[2]=>
int(1290)
[3]=>
int(2674)
[6]=>
int(5589)
Ну в общем можно сказать что корень сильно лучшее распределение даёт (всё равно перекос есть но тем не менее, вынужден признать, что казалось бы несложный, на первый взгляд, переход от одной задачи к другой, вызвал трудности.
Точную формулу для правильного распределения кто-нибудь осилит?
select max(abs_weight) where abs_weight < my_rand_numberТаки, наверное, все-таки select min(abs_weight) where abs_weight > my_rand_number.
Да, надо еще выяснить, как правильнее будет: '>' или '>='.
Таки, наверное, все-таки select min(abs_weight) where abs_weight > my_rand_number.пофиг
Да, надо еще выяснить, как правильнее будет: '>' или '>='.
идея ясна
Идея — согласен, охуенная.
в лоб пробовал?я делал немного по-другому, в таблице был не "абсолютный вес" (кстати, странное название ты этому выбрал а функция распределения, но идея в целом как у тебя:
тебе надо создать столбец, в котором будут абсолютные веса
т.е. в твоем случае это будет
item1 50 50
item2 30 80
item3 20 100
ну а дальше надо выбрать максимум из минимов по рандому от 100
типа select max(abs_weight) where abs_weight < my_rand_number
item weight pr_from pr_to
item1 50 0.0 0.5
item2 30 0.5 0.8
item3 20 0.8 1.0
для выбора использовал запрос типа
select item from ttt where rand between pr_from and pr_to
только надо еще предусмотреть что-то, чтобы раз в 100 лет не выбралось 2 записи, у меня окончательно было в форме:
select item from ttt where floor(rand / eps) * eps + eps / 2 between round(pr_from / eps) * eps and round(pr_to / eps) * eps
в общем твой вариант наверное лучше, если нет надобности доставать одним запросом
select first item from t where (+\[w])>=rand sum w
+\[w]=(x1;x1+x2;x1+x2+x3:...)
Оставить комментарий
uncle17
item1 50item2 30
item3 20
Думаю, пояснений не надо
Как проще всего?