[CLOSED] [[my]sql] получить рандомную строку в зависимости от ее веса

uncle17

item1 50
item2 30
item3 20
Думаю, пояснений не надо :)
Как проще всего?

AlexV769

Сначала долго хотел написать "нет, нужно пояснить", после 10 раза вчитался и теперь задам другой вопрос:
какое распределение для весов ты закладываешь в слова "получить рандомную строку в зависимости от веса?"

uncle17

на 1000 селектов получить 200 item3, 300 item2 и 500 item1

pitrik2

в лоб пробовал?
тебе надо создать столбец, в котором будут абсолютные веса
т.е. в твоем случае это будет
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
но можно и каждый раз вычислять, селект вродь не сложно такой написать

NAIL

Если скорость не важна и задача чисто теоретическая (или полей очень мало то:
select * from items order by rand*weight desc limit 1;

На практике перебор всей таблицы с Using temporary; Using filesort если таблица не совсем маленькая - будет печально.

uncle17

точно
вот так и хотел
Нет, табличка маленькая, то, что надо.

pitrik2

я прально понимаю чт оты для кадой записи вызываешь рендом?
прикольна

uncle17

когда там 10-50-100 записей, это не так важно, думаю

pitrik2

select * from items order by rand*weight desc limit 1;
вощем
думал я думал
думал я думал
твоё решение не верно
ну в том смысле что вероятность что выпадет отрезок с весом 10 у тебя не 10
но в принципе от весов твоя формула тоже зависит :)
твоя задача фактически сводится к такой:
у нас есть два отрезка
(0,3) и (0,97)
какова вероятность что случайно выбранное число с первого отрезка будет больше случайно выбранного числа со второго отрезка?

NAIL

Прав.
Провёл несколько тестиков 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)

Ну в общем можно сказать что корень сильно лучшее распределение даёт (всё равно перекос есть но тем не менее, вынужден признать, что казалось бы несложный, на первый взгляд, переход от одной задачи к другой, вызвал трудности.
Точную формулу для правильного распределения кто-нибудь осилит? :)

al70

select max(abs_weight) where abs_weight < my_rand_number
Таки, наверное, все-таки select min(abs_weight) where abs_weight > my_rand_number.
Да, надо еще выяснить, как правильнее будет: '>' или '>='.

pitrik2

Таки, наверное, все-таки select min(abs_weight) where abs_weight > my_rand_number.
Да, надо еще выяснить, как правильнее будет: '>' или '>='.
пофиг
идея ясна

al70

Идея — согласен, охуенная. :)

state7401281

в лоб пробовал?
тебе надо создать столбец, в котором будут абсолютные веса
т.е. в твоем случае это будет
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
в общем твой вариант наверное лучше, если нет надобности доставать одним запросом

Papazyan

Исчо один пример корявости чистого sql. Если допустить элементарнейшее предположение о фиксированном порядке строк, то задача решается элементарно:
select first item from t where (+\[w])>=rand sum w
+\[w]=(x1;x1+x2;x1+x2+x3:...)
Оставить комментарий
Имя или ник:
Комментарий: