PDA

Pogčedajte punu verziju : random koji favorizuje


kodi
26. 04. 2007., 02:15
pozdrav ekipo

nije da sam bas toliko los sa matematikom koliko mi se spava :)

elem problem:

potreban mi je specijalan random koji ce raditi na sledecem principu:

spec_rand($from,$to,$factor);

elem to bi radilo otprilike ovako

spec_random(10,100,3);
to bi znacilo da je 3x veca mogucnost da funkcija vrati veci broj tj blizi 100
a ako bi faktor bio -3 znaci veca verovatnoca da ce biti manji broj, tj blizi 10



any ideas?


p.s. ne, nisam googlao :D

p.s.2

kratko objasnjenje, imacu neku tabelu sa nekim proizvodima, gde ce svaki imati neki faktor recimo od 1-100, a na glavnoj stranici ce se prikazivati recimo 20 proizvoda stim sto ce biti favorizovani ovi sa vecim faktorom..
a ako u spec_rand funkciju unesem 0 kao faktor onda da radi kao obican random, tj da ne favorizuje...

hmm sad razmisljam 1 i -1 faktor bi bili takodje kao obican random....

filmil
26. 04. 2007., 02:50
to bi znacilo da je 3x veca mogucnost da funkcija vrati veci broj tj blizi 100
a ako bi faktor bio -3 znaci veca verovatnoca da ce biti manji broj, tj blizi 10


Теби треба генерисање псеудо-случајног целог броја на задатом интервалу, са произвољном расподелом. Програмски језици обично нуде генерисање псеудо-случајног целог броја са униформном расподелом.

Срећом па се то лако ради ако имаш на лагеру генератор униформне расподеле, можеш да направиш ма који други.

Међутим овако како си ти дефинисао неће да може, или бар не може директно. Бољи начин је да одредиш функцију расподеле која ти врши посао, а онда да пробаш некако ту функцију да пресликаш у твој параметар.

Почиње се од функције расподеле за униформну случајну променљиву на интервалу 0 до 1. За друге интервале можеш да скалираш, али овај је најједноставнији. Означи са U ту случајну променљиву.

Њена функција расподеле је:

F_U(x) = P( U < x) = { 0, x <0 ; x, x E [0, 1]; 1, x > 1

Претпостави сад да знаш која ти функција расподеле G(x) треба. Она ти зависи од примене, тако да је дефинисање G(x) твој посао. Претпостављам из твог описа да се ради о линеарној функцији густине расподеле, према томе о квадратној функцији расподеле, на опсегу који те занима. То је зато што је функција расподеле интеграл функције густине расподеле, а како је густина вероватно линеарна, то је расподела квадратна. Али о овом касније.

Све што треба јесте да генеришеш случајну променљиву Y = G^-1(U), где је G^-1 инверзна функција од G, а U случајна променљива са униформном расподелом. Тако добијена променљива има управо расподелу G(x). Доказ следи:

F_Y(x) = P ( Y < x) = P( G^-1(U) < x) = P [U < G(x)]= G(x).

Дакле добили смо да је тачно: F_Y(x) = G(x) а то је оно што нам треба.

Због појављивања инверза функције расподеле G, она мора да буде монотона. Уколико није, у овом случају то може да се реши накнадним пермутовањем елемената, а у општем случају можда треба да се потражи други генератор.

ф

filmil
26. 04. 2007., 03:22
Само да допуним горњу причицу једним примером, да не буде да лупам у празну шерпу.

Пробаћу да набодем шта ти је потребно, мада то није тако добро дефинисано.

Претпостављам да је густина расподеле g(x) на интервалу (f,t) (from->to) таква да је
g(t) = n * g(f), што је отприлике оно што си рекао горе. Једино је фактор мало другачији јер је инверз мултипликативан а не адитиван као у твом случају. Тојест, обрнути случај од n = 3 добио би се са n' = 1/3, а не са n = -3. Али то ваљда није неки проблем.

Претпоставићу да је интерполација између крајњих тачака линеарна. Овај део ниси навео у поставци, тако да провери да ли ти одговара. Тада је:

g(x) = { 0, x < f; ax + b, x E [f, t]; 0, x > t

Треба нам да одредимо a и b у функцији од n да бисмо добили g(x). Онда ћемо да инвертујемо g(x) и то је оно што нам треба.

Линеарном интерполацијом се добија да је:

a = (n-1) / (t-f) * q
b = (t - nf) / (t-f) * q

што су вредности које добијете ако протурите праву ax+b кроз тачке: (f, q) и (t, nq). Овде нам је n параметар кога задаје корисник, а q мора да се одреди из интегралног услова за расподелу.

Пошто g(x) мора да задовољи интегрални услов за расподелу да је \int_{-\infty}^{+\infty} = 1, интеграљењем функције g(x) по целом домену добије се:

q = 2/[(n+1)(t-f)]

(паде ми напамет мало касније па да допуним: вредност за q добио сам формално интеграљењем, али може да се добије и на бржи начин тако што се уочи да је средња вредност функције тачно на средини интервала. Штос је сличан ономе ког је Гаус искористио да срачуна брзо збир свих бројева од 1 до 100)

Сада треба да пређемо на функцију расподеле G(x). По дефиницији,

G(x) = \int_{\infty}^x g(x) dx

а интеграљењем део по део у овом случају добија се:

G(x) = { 0, x< f; a/2(x^2-f^2) + b(x-f), x \in [f, t]; 1, x > t

Налажењем инверза G^{-1}(x) добија се, за интервал [0,1] ово:

G^{-1}(y) = H(y) = 1/a * ( -b + \sqrt{ b^2 + 2a(a/2 f^2 + bf)})

где су нам све величине или познате од пре, или зависе од параметра n.

H(y) је у ствари функција и од параметра n, па ћу да забележим: H(y; n).

И сад је лако.

Ако имаш функцију rand() која генерише реални број од 0 до 1 са униформном расподелом, радиш следеће:

u = rand();
y = H(u; n)

И број y је извучен тачно из расподеле коју си хтео.

ф

ivanhoe
26. 04. 2007., 04:34
u prevodu za one koji nisu sa ETF-a ili PMF-a, poenta je da podelis interval na konacan broj delova (tj. cele brojeve u ovom slucaju) i da onda napravis funkciju koja ce da oslika to "favorizovanje". Posto imas na raspolaganju funkciju koja daje (pseudo) uniformnu raspodelu, odnosno svi brojevi na intervalu imaju iste sanse, ono sto treba da se uradi je da se prosiri interval na kome je odredjeni rezultat. Ako 1 ima 5 puta vece sanse od 5 onda, da bi dobio tvoju verovatnocu na intervalu (1,5) treba da ti je interval za rezultat "1" od 0-4, za rezultat "2" od 5-8, "3" je od 9-11, "4" od 12-13 i "5" je 14. Onda izvuces rand (0,14) i vidis u koji interval ti upada dobijeni rezultat. "1" ti ima verovatnocu 5/15, "2" je 4/15, "3" je 3/15, "4" je 2/15 i "5" je 1/15, odnosno dobio si trazeno..

zira
26. 04. 2007., 09:57
Mozda mozes da iskoristis (PHP manual):


Here's an algorithm to make a weighted selection of an item from an array.
Say we have an array $items with keys as items and values as corresponding weights.
For example:

<?php
$items = array(
item1 => 3,
item2 => 4,
item3 => 5,
);
?>

i.e. we want to choose item1 25% of the time, item2 33.3% of the time and item3 41.6% of the time.
Here's a function that works when the weights are positive integers:
<?php
function array_rand_weighted($values) {
$r = mt_rand(1, array_sum($values));
foreach ($values as $item => $weight) {
if ($r <= $weight) return $item;
$r -= $weight;
}
}
?>

kodi
26. 04. 2007., 11:13
i ja sam razmisljao nesto kao ivanhoe, na kraju sam nacrtao grafik i vidm da je to manje-vise neka logaritamska funkcija...

sad ja znam da je ovo daleko od idealnog, al radi posao
(pod pretpostavkom da je range 1 <-> x i da je n >= 2 )


function spec_rand($from,$to,$n){

$new_to=($n * ($to/2 + 0.5));
$u=rand($from,$new_to);
$final=round($to*(log(($u+(triag(1)*($n/$to))),($new_to))));
if($final==0){ $final=1; }
return $final;

}


-spec_rand(1,30,5) vraca mnogo vise brojeva blizih 30 nego spec_rand(1,30,2) gde mnogo cesce izbacuje brojeve manje od 15;

-spec_rand(1,30,100) vraca skoro uvek 22 i vece.. bas se retko zadesi manji od 20

naravno glavni je nedostatak sto se neki brojevi zbog zaokruzivanja ne pojavljuju redje, vec nikad :(

kodi
26. 04. 2007., 12:59
dve stvari

1) gornji kod treba da bude::


function spec_rand($from,$to,$n){

$new_to=($n * ($to/2 + 0.5));
$u=rand($from,$new_to);
$final=round($to*(log(($u+($n/$to)),($new_to))));
if($final==0){ $final=1; }
return $final;

}


2) uh, zakomplikovah ga bez potrebe, ovo resava stvar
(tako mi se bar cini)





$n=3;
$SQL="select id, name, (FLOOR(1 + (RAND() * 99)) * (product_factor*$n)) as new_ord
from products
order by new_ord desc
limit 0,20";

jablan
26. 04. 2007., 13:10
Očigledno se nisi potrudio da pročitaš šta su ti ljudi napisali nego teraš svoje... ;)

Da probam da približim ovo što Ivanhoe kaže (filmil je i za mene previše stručan ;)), prenoseći ideju u svet baza.

Dakle, na raspolaganju ti je npr. najobičnija unfiormna random funkcija rand(n), koja vraća ceo broj između 0 i n.

Imaš tabelu T sa nekim ID-jem i nekim slogovima. Svaki slog ima polje težina, koja je neki integer. Hoćeš da polje sa 5 puta većom težinom od drugog bude 5 puta verovatnije izvučeno iz random bubnja. Uradiš sledeće:

n := select sum(težina) from T

x := rand(n)

Upit koji ti vraća traženi slog je:


select top 1 t1.id, sum(t2.težina) from tabela t1
inner join tabela t2 on t1.težina > t2.težina
group by t1.id
having sum(t2.težina) <= x
order by sum(t2.težina) desc

kodi
26. 04. 2007., 13:40
Hvala!

filmil
26. 04. 2007., 18:25
i ja sam razmisljao nesto kao ivanhoe, na kraju sam nacrtao grafik i vidm da je to manje-vise neka logaritamska funkcija...


Није мање-више логаритамска, него је тачно квадратни корен, тј. константа плус квадратни корен из нечега. Логаритамска функција ти даје експоненцијални инверз који брзо оде у засићење, отуд ти се неки бројеви не појављују.

Пробај са квадратним кореном, не мораш нужно да наместиш све параметре као што сам показао, него направи неколико проба са разним параметрима, па узми шта ти се највише свиђа.

ф

kodi
26. 04. 2007., 22:02
holly ****, izgleda da radi :)
(doduse za 1/n favorizuej vece a za n manje brojeve, nisam uradio onaj deo gde inverzujes)


$f=0;
$t=1;
$n=1/10;

$my_to=10;

$q = 2/(($n+1)*($t-$f));
$a = ($n-1) / ($t-$f) * $q;
$b = ($t - $n*$f) / ($t-$f) * $q;

$u=rand(0,1000) / 1000;
$my_rand=round($u*$my_to);

echo "<hr />";
echo "rand(u): $my_rand";
echo "<hr />";
echo "H(u)". round(($a/2*(pow($u,2)-pow($f,2)) + $b*($u-$f))*$my_to);



ocekivao sam malo vece promene za velike vrednosti n, nadam se da nisam pogresio negde.

0 0
1 2
2 4
3 5
4 6
5 7
6 8
7 9
8 10
9 10
10 10

ovo mi je za n=1000
za 1 se svaki mapira na sebe
Da probam jos jednom da objasnim zasto mi je ovo bitno (osim sto sam istripovao da je bitno :) )
zato sto hocu da jednom kad popunim tu tabelu proizvodima, i odredim tezine, posle samo menjam taj jedan parametar, tako da ako je 1 onda radi obican random (testirao sam sa ovom F-jom, radi) a ako hocu da favorizujem neke proizvode, samo malo podignem n, bez potrebe da cackam po bazi.

a takodje se moze iskoristiti kod za prokazivanje oglasa i baner-a (valjda)


Elem ovo sasvim radi posao, sad samo da vidim kako da ga mapiram na bazu. :D

zira
26. 04. 2007., 22:40
Zanimljiva ideja, samo pazi da ne smoris one koji ce azurirati takav sajt. U praksi je uglavnom dovoljno oznaciti neki proizvod kao "promovisani" i takve proizvode gurati u prvi plan. Znaci, bas mi je zanimljivo to kako si zamislio, samo ne znam koliko ce to biti lako odrzavati sa par hiljada proizvoda npr.

kodi
26. 04. 2007., 22:42
opusteno, moj projekat :D

edit: naravno, filmil ima pice+klopu kad dodje ovde.. :)

Pedja
26. 04. 2007., 22:54
Jednom davno sam radi onesto slicno i primenio sam nesto drugaciju logiku. Naime, moj radnom gneerator je morao da ispuni uslov da obezbedi da se svaki elemnet prikaze dovoljan broj puta. Zbog toga nisam ostavljao da sve prosto zavisi od matematike nego sam uradio sledece:

Uveo sam pojam kruga, definisan kao broj prikazivanja dok svi elementi nisu prikazani bar jednom. Standardno se element prikazuje jednom u jednom krugu.

Za svaki elemnt se definise koliko puta cesce se pojavljuje od standardnog.
Ako je 1 onda se element pojavljuje standradno ucestano, ako je 2, dva puta, 5 pet puta, a moze da se koriste i decimalni brojevi za profinjenije odnose.

Imam jedan brojac koji broji "krugove" Jedan krug se zavrsava kada su svi elementi prikazani bar jednom. Za svaki element se pamti i koliko je puta prikazan.

Imajuci ove podatke, relativno je jednostavno izvlaciti elemente koji su za prikazivanje.

MrSteel
29. 04. 2007., 21:39
heh, ja sam ladno sinoc sanjao random funkciju
cilj random funkcije je da bude takva da sa njom moze da se dobije recimo random n lica razlicite starosne dobi ali tako da odgovara trenutnom preseku populacije

i resenje je tu negde bilo al kao i svaki san najbolje se zaboravi ;)
verovatno je poruka - moze to i na javi )

zira
29. 04. 2007., 21:55
LOL, vrlo zanimljiv san :)

pcigre
29. 04. 2007., 22:09
heh, ja sam ladno sinoc sanjao random funkciju
cilj random funkcije je da bude takva da sa njom moze da se dobije recimo random n lica razlicite starosne dobi ali tako da odgovara trenutnom preseku populacije

i resenje je tu negde bilo al kao i svaki san najbolje se zaboravi ;)
verovatno je poruka - moze to i na javi )

pravo vreme da pocnes da razmislja o odmoru :) makar to bilo i par dana oko "praznika" da malo :1074: :1074: :1074:

A onda lepo nazad na random :1042: