Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: algorytm do oceny fotek
Forum PHP.pl > Forum > Gotowe rozwiązania > Szukam
ersiv
jest sobie serwis x i głosuje się tam na fotki, każdy użytkownik ma ranking liczony na podstawie oddanych głosów, skala od 1-10 wartość średnia odpada smile.gif
potrzebuję uwzględnić coś takiego że np profil mający 100 głosów na '1' jest dużo gorszy od profilu mającego 10 głosów na '1', a np profil mający głosy 1x8, 1x9, 1x10 jest lepszy od tego, który ma tylko 1x10.... wydaje się proste ale nic mądrego nie przychodzi mi do głowy, szukam, szukam i ciężko... miliony jets tkaich serwisów, gdzie głosuje sie na cokolwiek ale nie mogę znaleź gotowych rozwiązań
Spirit86
proste, zrób sobie mnożenie przez jakiś współczynnik

ocena na 10:

ocena = ocena + 10*1,10

ocena 1:

ocena = ocena +1*1.01

czy jakoś tak

albo stwórz coś w tabeli kilka kolumn ocena_10 - ilość głosów, ocena_9 ilość głosów itd. a później zrobisz SORT by ocena_10 DESC, ocena_9 DESC etc.
Nattfarinn
Myślę, że najlepsze rozwiązanie to wyznaczenie średniej oceny a następnie branie pod uwagę ilości oddanych głosów. W zależności od średniej, duża ilość głosów świadczyłaby na korzyść lub niekorzyść wartości oceny o tej samej średniej.

Teraz chodzi o ustalenie wagi głosów. Bo akurat skrajne głosy są oczywiste, 3x10 jest mniej warte od 5x10, ale 5x1 jest mniej warte od 3x1. Ale co w przypadku 3x3 a 5x3? Co w przypadku 3x4 a 5x4? Trzeba przyjąć połowę, czyli średnią 5,5. Oceny o tej samej średniej, ale większej (lub równej) od połowy, będą sortowane wg. malejącej ilości głosów, te ze średnią mniejszą od połowy, sortowane wg. rosnącej ilości głosów.

Jak to będzie wyglądało w praktyce?

Fotka 1: 1 1 1 1 1 1 1 1 (średnia: 1, głosów: 8)
Fotka 2: 1 1 1 1 1 (średnia: 1, głosów: 5)
Fotka 3: 1 1 1 1 1 1 1 1 1 1 1 1 2 (średnia: 1.076, głosów: 13)
Fotka 4: 1 1 1 1 1 1 1 1 1 1 1 1 1 3 (średnia: 1.143, głosów: 14)
Fotka 5: 5 6 (średnia: 5.5, głosów: 2)
Fotka 6: 5 6 5 6 5 6 5 6 (średnia: 5.5, głosów: 8)
Fotka 7: 5 5 6 5 6 5 6 5 (średnia: 5.375, głosów: 8)
Fotka 8: 5 (średnia: 5, głosów: 1)
Fotka 9: 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 (średnia: 5, głosów: 25)

Posortowane da w wyniku fotki (kolejno od najlepszej do najgorszej):
6, 5, 7, 8, 9, 4, 3, 2, 1

Trzeba więc przyjąć, że Fotki o średniej < 5.5 są negatywne, a fotki >= 5.5 pozytywne. Im mniej głosów w fotce o ocenie negatywnej tym lepiej, odwrotnie dla fotek pozytywnych: ogromna ilość głosów przy zachowaniu oceny pozytywnej jest więcej warta od tej samej średniej ale uzyskanej np. 3 głosami.

Ciężko będzie znaleźć inne rozwiązanie. Te podane przez Spirit86 raczej odpadają. Pierwszy sposób nie zmienia właściwie nic, drugi zaś o ilę się nie mylę wykaże, że 2x10 + 1000x1 jest lepsze od 1x10 + 1x1.

Namieszałem, co?

W bazie dla każdej fotki przechowywana jest suma głosów i ilość głosów. Teraz wyciągasz za pomocą dwóch zapytań fotki pozytywne i negatywne (przyznaję się bez bicia, że nie wiem jak rozwiązać to za pomocą jednego zapytania) sortując je głównie wg średniej oceny (suma/ilość) a następnie wg. ilości głosów (inaczej dla obu zapytań, w zalezności od tego czy fotki są pozytywne czy negatywne).

Coś jakby:
Kod
[Pozytywne]: SELECT photo_id FROM photo_rating WHERE  photo_rate_sum/photo_rate_count >= 5.5 ORDER BY photo_rate_sum DESC,  photo_rate_count DESC

Kod
[Negatywne]: SELECT photo_id FROM photo_rating WHERE photo_rate_sum/photo_rate_count < 5.5 ORDER BY photo_rate_sum DESC, photo_rate_count ASC


I to właściwie tyle...

Pozdrawiam!
ersiv
Rzeczywiście trochę namieszane smile.gif za chwileczkę dokładniej przeczytam i popróbuje może dodam że w bazie zapisuje ilość głosów oddanych na każdą z ocen dla konkretnego zdjęcia... co jakiś czas jest uruchamiany skrypt który zlicza ocenę końcową i gdzieś ją tam uaktualnia tylko obecny system oceniania w tym serwisie aż wstyd mówić jest to porostu średnia i jest to beznadziejne rozwiązanie... serwis jest spory i samo przejechanie po tabeli i wyliczanie średniej muli serwer na kilka minut, więc musi mieć to ręce i nogi...
Przyznam że główkowałem i ciężko mi wymyślić jak to liczyć żeby było to adekwatne do ilości i jakości oddanych ocen...
Nattfarinn
Podejrzewam (bo niestety nie miałem możliwości przetestowania z kodem - nadrobię to zaraz), że mój sposób (zamieszany jak radziecki magnetowid) będzie w miare sensownie działał. Dodatkowo nie będzie musiał przechowywać wszystkich głosów a tylko samą ich sumę, więc zmaleje obciążenie.

Cytat
co jakiś czas jest uruchamiany skrypt który zlicza ocenę końcową i gdzieś ją tam uaktualnia

Nie widzę powodu uruchamiania cyklicznie co jakiś czas zdalnie skryptu (a tak rozumiem to zdanie). Wydaje mi się, że aktualizować powinien tylko i wyłącznie przy dodaniu głosu (bo w końcu jak nikt nic nie dodaje, to nie ma co aktualizować).

Cytat
serwis jest spory i samo przejechanie po tabeli i wyliczanie średniej muli serwer na kilka minut, więc musi mieć to ręce i nogi...

Tym bardziej, taka aktualizacja powinna mieć miejsce tylko przy oddaniu głosu i raczej zrezygnowałbym z zapisywaniem wszystkich głosów (chyba, że tylko i wyłącznie w celach statystycznych).
złowieszczy_pan
Witam serdecznie.

Sorry za odkopanie tematu, ale nie mogłem znaleźć nic na ten temat.

Mam podobny problem jak założyciel tematu.
Ma ktoś pomysł jak można stworzyć jedno zapytanie, zamiast tych dwóch przedstawionych przez pana Nattfarinna?

Jak sądzicie, przedstawione przez pana Nattfarinna rozwiązanie jest dobre?
Może ma ktoś inny pomysł jak rozwiązać wyżej opisany problem?


Z góry dziękuję za wszelką pomoc.
elemek
Cóż ja bym to rozwiązał w sposób następujący:

1. W danych profilu kolumny: 'ilość głosów', 'średnia ocen'. aktualizacja oceny sprowadza się do pomnożenia ilości głosów przez średnią, dodania do tak powstałego iloczynu nowej oceny, a następnie podzielenia całości przez ilość głosów + 1 co matematycznie da taki sam efekt jakbyś przechowywał wszystkie oceny i za każdym głosem je od nowa sumował lol. Następnie w bazie uaktualniana jest średnia i zwiększana o 1 liczba ocen. Czas operacji błyskawiczny.

  1. <?php
  2. $nowa_srednia = ($stara_srednia * $ilosc_glosow + $nowa_ocena) / ($ilosc_glosow + 1);
  3. $ilosc_glosow++;
  4. ?>


2. Tabela oddanych głosów: kto na kogo już głosował zeby nie było, że ktoś da komuś 5 czy 500 dziesiątek. Wykorzystanie: sprawdzić czy user nie oddał jeszcze głosu, a jeśli nie oddał to pozwolić na oddanie głosu i zapisać, że już oddał. Czas operacji: błyskawiczny.

EDIT: aha i trzecia rzecz czyli uwzględnienie ilości oddanych głosów przy ocenie: tutaj proponuję posłużyć się logarytmem naturalnym co da oczekiwany efekt przy małej ilości głosów, natomiast dla profili o dużej ilości głosów przestanie mieć znaczenie.

  1. <?php
  2. $ocena = $srednia * $log($ilosc_ocen);
  3. ?>


EDIT2: lepiej przechowywać sumę ocen, a nie średnia w 1 punkcie.

I tyle.
SirZooro
Ja proponuję inny nieco system oceniania - pierwiastek z sumy kwadratów poszczególnych ocen, z kilkoma modyfikacjami. Cały wzór by wyglądał tak:
Kod
           _________________________________________
           | (O1+1)^2 + (O2+1)^2 + ... + (On+1)^2  '
Ocena =    | --------------------------------------    - 1
          \/                    N


Ponieważ najniższa ocena to 1, a 1 do kwadratu to dalej 1, więc dodaję do każdej oceny 1. Na końcu aby ocenę znów sprowadzić do przedziału 1..10 odejmuję 1.

W SQL będzie to tak:
  1. SELECT SQRT(SUM(POW(ocena+1, 2))/COUNT(ocena))-1 FROM oceny WHERE id_fotki=123
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.