Piotr Stanek

Piotr Stanek Programista PHP /
JS developer

Temat: losowanie według priorytetu

Witam,

Mam pewien problem.

Mam np w bazie 100 rekordów każdy z tych rekordów ma id i np kolumnę count - w której jest liczba
Chcę wylosować 7 rekordów z tej puli 100 z tym ze te o najwyższym count mają wyświetlać się najczęściej.

wiec ORDER by RAND() odpada.

może jakieś pomysły.

Myślałem żeby zapisywać gdzieś ile dany element był losowany i np. jak osiągnie 7 to potem już go nie biorę pod uwagę ale to troszkę bez sensu... może inne rozwiązanie.

EDIT tego typu rzeczy

[SQL] pobierz, plaintext

SELECT * FROM `tag` ORDER BY count DESC,RAND() LIMIT 7

niedziałają. Wiec trzeba to zrobić po stronie PHP
Łukasz C.

Łukasz C. Senior Technical
Architect

Temat: losowanie według priorytetu

hmm teoretycznie widze to tak:

- mam pule rekordow w srod ktorych losuje
- x to ilosc rekordow podanych do losowania
- tworze nowy wektor w ktorym duplikuje rekordy (lub ich klucze), ilosc duplikatow rekordu o id A wynosci wartosc count + 1
- ilosc rekordow w nowej puli wynosi y = x + sum(R1.count + ... + Rn.count)
- przeprowadzam zwykle losowanie (rand() % y)

w praktyce:
- mozna sprobowac ze skopiowaniem rekordow to tablicy pomocniczej (procedura skladowana)
- mozna zrobic powyzsze w php :pŁukasz Cepowski edytował(a) ten post dnia 30.05.11 o godzinie 21:08

konto usunięte

Temat: losowanie według priorytetu

Co prawda cudów się nie spodziewaj :-)
Ale ten kod SQL (pisany dla MySQL) daje mniej więcej to co chciałeś:
SELECT * FROM (SELECT * FROM `tag` ORDER BY `count` DESC LIMIT 30) AS `tmp` ORDER BY RAND() LIMIT 7
Jakub L.

Jakub L. Programista

Temat: losowanie według priorytetu

Łukasz C.:
hmm teoretycznie widze to tak:

- mam pule rekordow w srod ktorych losuje
- x to ilosc rekordow podanych do losowania
- tworze nowy wektor w ktorym duplikuje rekordy (lub ich klucze), ilosc duplikatow rekordu o id A wynosci wartosc count + 1
- ilosc rekordow w nowej puli wynosi y = x + sum(R1.count + ... + Rn.count)

Samo sum, ale to szczegół.
- przeprowadzam zwykle losowanie (rand() % y)

Dałem plusa ale zaraz po tym zauważyłem babola - jeżeli losowanie ma być jednokrotne, to po każdym losowaniu trzeba albo usuwać wszystkie duplikaty wylosowanego, albo sprawdzać i losować jeszcze raz jeżeli zdarzył się duplikat.
Łukasz C.

Łukasz C. Senior Technical
Architect

Temat: losowanie według priorytetu

chyba o niejednokrotne Ci chodzi ;)? imho wystarczy usunanc duplikat wylosowanego rekordu z tabeli (wektora) tymczasowej, da rade to zrobic chyba w czasie O(n) wiec pelna zlozonosc losowania to bylo by cos w okolicach: O(n + sum(r1.count + ... + rn.count) + k*n) => O((k+c)*n)

gdzie:
k - ilosc losowan
c - pewna stala (1...1 + avg(count))
n - ilosc rekordow

// zakladam ze count moze byc rowne zero

konto usunięte

Temat: losowanie według priorytetu

Proponuje losowanie z ruletką - czyli właśnie losowanie z wagą / priorytetem.

1) a - Suma wszystkich liczników:
a = sum(R1.count + R2.count...+Rn.count)

2) b - Suma liczników dla rekordu nr k

b(k) = sum(R1.count + R2.count...+Rk.count)

p(0) = 0

Górny limit segmentu prawdopodobieństwa wylosowania pierwszego rekordu:
p(1) = b(1) / a = R1.count / a

Górny limit segmentu prawdopodobieństwa wylosowania k-tego elementu:
p(k) = b(k) / a

Górny limit segmentu prawdopodobieństwa wylosowania ostatniego elementu:
p(n) = b(n) / a = a / a = 1

a i b(k) trzeba było policzyć wcześniej, a potem coś w rodzaju:

select ... from nazwa_tabeli a where rand() between a.p_prior and a.p

p_prior = p(k-1)
p = p(k)

Wynik będzie odrobinę niedokładny, ponieważ between włącza wartości testowane do zakresu, a jedna strona zakresu powinna być nieostra. Ale w takim zastosowaniu to raczej nie powinno mieć znaczenia.
Łukasz C.

Łukasz C. Senior Technical
Architect

Temat: losowanie według priorytetu

@up: podoba sie, ladne between i pozbyles sie mojego usuwania rekordow z tabeli pomocniczej :)

konto usunięte

Temat: losowanie według priorytetu

Piotr Stanek:
Witam,

Mam pewien problem.

Mam np w bazie 100 rekordów każdy z tych rekordów ma id i np kolumnę count - w której jest liczba
Chcę wylosować 7 rekordów z tej puli 100 z tym ze te o najwyższym count mają wyświetlać się najczęściej.

wiec ORDER by RAND() odpada.

może jakieś pomysły.

Myślałem żeby zapisywać gdzieś ile dany element był losowany i np. jak osiągnie 7 to potem już go nie biorę pod uwagę ale to troszkę bez sensu... może inne rozwiązanie.

EDIT tego typu rzeczy

[SQL] pobierz, plaintext

SELECT * FROM `tag` ORDER BY count DESC,RAND() LIMIT 7

niedziałają. Wiec trzeba to zrobić po stronie PHP


a może w ten sposób?


$q = mysql_query('SELECT * FROM `tag` ORDER BY `count` DESC LIMIT 25;');
$results = array();
while($r = mysql_fetch_array($q)) $results[] = $r;

$seven = array_rand($results, 7);


Trochę prymitywne, aczkolwiek spełnia swoje zadanie.. wyciągasz 1/4 tych o największym count i z nich losujesz 7...

Nie sprecyzowałeś jak DOKŁADNE to ma być, więc nie wiem czy takie rozwiązanie jest wystarczające.
Jakub L.

Jakub L. Programista

Temat: losowanie według priorytetu

Co was tak bierze z tym LIMIT?
Uwalając część potencjalnych wyników całe losowanie można sobie wsadzić.
Piotr Stanek

Piotr Stanek Programista PHP /
JS developer

Temat: losowanie według priorytetu

Jakub dokładnie.

W waszym przypadku losuję z części rekordów a ja chce np zeby byl częsciej 1 rekord ale zeby bral tez pod uwage przy losowaniu jakis rekord malo wazny ...

napisze po swojemu w php

potem pokaze moze sie komus przyda.

Temat: losowanie według priorytetu

Piotr Likus:
Wynik będzie odrobinę niedokładny, ponieważ between włącza wartości testowane do zakresu, a jedna strona zakresu powinna być nieostra. Ale w takim zastosowaniu to raczej nie powinno mieć znaczenia.

Można zmienić between na >= and <

konto usunięte

Temat: losowanie według priorytetu

Wojciech Małota:
Piotr Likus:
Wynik będzie odrobinę niedokładny, ponieważ between włącza wartości testowane do zakresu, a jedna strona zakresu powinna być nieostra. Ale w takim zastosowaniu to raczej nie powinno mieć znaczenia.

Można zmienić between na >= and <

Można, ale wtedy nie użyjesz rand() w SQL-u (trzeba będzie go zrobić w PHP-ie lub SP).
Marcin Molga

Marcin Molga Senior Solution
Architect, IBM.

Temat: losowanie według priorytetu

Piotr Stanek:
Witam,

Mam pewien problem.

Mam np w bazie 100 rekordów każdy z tych rekordów ma id i np kolumnę count - w której jest liczba
Chcę wylosować 7 rekordów z tej puli 100 z tym ze te o najwyższym count mają wyświetlać się najczęściej.
<cut>

niedziałają. Wiec trzeba to zrobić po stronie PHP

Nie wiem do czego to ma służyć, co to znaczy 'najczęściej', itp. ale możesz wygenerować sobie liczby z parametryzowanego rozkładu np. Poissona metodą odwracania dystrybuanty.

http://pl.wikipedia.org/wiki/Rozk%C5%82ad_Poissona#Gen...

Pozdrawiam.
Tomasz Zadora

Tomasz Zadora programuję

Temat: losowanie według priorytetu

@Marcin: autorowi wątki chodzi o "wagi" elementów wyrażone przez kolumnę count.

Czyli jeżeli element A ma wagę 10 a element B wagę 2, to możemy to wyrazić jako zbiór 12-stu elementów gdzie 10 elementów to A a 2 elementy to B i z tego zbioru jest losowanie.
Jakub L.

Jakub L. Programista

Temat: losowanie według priorytetu

Piotr Likus:
Wojciech Małota:
Piotr Likus:
Wynik będzie odrobinę niedokładny, ponieważ between włącza wartości testowane do zakresu, a jedna strona zakresu powinna być nieostra. Ale w takim zastosowaniu to raczej nie powinno mieć znaczenia.

Można zmienić between na >= and <
>
Można, ale wtedy nie użyjesz rand() w SQL-u (trzeba będzie go zrobić w PHP-ie lub SP).

Podwójnie zagnieżdżony select nie da rady?
Wojciech K.

Wojciech K. realizator pomysłów
własnych

Temat: losowanie według priorytetu

Piotr Stanek:
Chcę wylosować 7 rekordów z tej puli 100 z tym ze te o najwyższym count mają wyświetlać się najczęściej.
wiec ORDER by RAND() odpada.

select *,((rand()*wspolczynnik)+licznik) as blabla from .... order by blabla desc;

gdzie wspolczynnik to stała liczbowa, którą dobierasz eksperymentalnie (wynosić może np. max(licznik), albo max(licznik)/2))
Jakub L.

Jakub L. Programista

Temat: losowanie według priorytetu

Wojciech K.:
Piotr Stanek:
Chcę wylosować 7 rekordów z tej puli 100 z tym ze te o najwyższym count mają wyświetlać się najczęściej.
wiec ORDER by RAND() odpada.

select *,((rand()*wspolczynnik)+licznik) as blabla from .... order by blabla desc;

gdzie wspolczynnik to stała liczbowa, którą dobierasz eksperymentalnie (wynosić może np. max(licznik), albo max(licznik)/2))

Licznik i współczynnik jako stałe są niepotrzebne, współczynnik rozpycha wyniki rand() na osi, licznik je przesuwa, niestety nie ma tam wagi elementu zwanej count.

Podobne tematy


Następna dyskusja:

Grupowanie według miesiąca




Wyślij zaproszenie do