Eloszlás (distribution) #matek 2018. július 14., szombat - 1:31


Elkezdtem csinálni egy "fél órás" projektet. Egy Cleverbothoz hasonló csetbotot. Alapesetben ezt közel sem fél óra lenne megcsinálni, főleg, ha csicsázzuk a dolgot mindenféle extra dologgal, elágazásokkal, vizsgálatokkal, fene tudja. Alapvetően az egyszerűsége annyiban van egy YAML struktúrát használó bothoz képest (vagy valami ilyen, már nem emlékszek, bátyám foglalkozott ezzel többet), hogy nem kell előre megírni ötmillió beszélgetést, hogy értelmesnek látszódjon, hanem magától tanul.

Ezt majd részletezem másik bejegyzésben, viszont szükségem volt egy normál eloszláshoz hasonló véletlenszerű generálásra. A chatbothoz. De nem találtam, vagy nem értelmeset, vagy magas gépigényűt, stb. Miért?

Mármint, minek ez nekem, meg mi ez? Egyszerű. Igazából egy súlyozott véletlenszerűen választó szkript kellett nekem. Példa: a "hogy vagy?" kérdésre tegyük fel, hogy 100-ból 70 ember azt írja, hogy "jól.", 20 azt, hogy "megvagyok.", és 10 azt, hogy "rosszul". Ez százalékban is pont így jön ki.

Az én tanuló chatbotom az ugyanolyan kérdésre ugyanolyan adott válaszokat nem jegyzi fel magának többször, helyette egy számlálót növel mindössze. Ez a számláló mutatja, hogy mire mennyiszer mondták azt, hogy ... blablabla. Pl. az előző példa - hogy a chatbottal való beszélgetés közben hányszor mondták a "hogy vagy?" kérdésre, hogy "jól.".

Ez a chatbot visszafelé is így működik. Tehát ha Te írod neki, hogy "hogy vagy?", erre ő válaszol az ezutáni emberi válaszok közül valamelyikkel, véletlenszerűen. De minél többen mondtak egy adott választ, annál valószínűbb, hogy ő is azt fogja rá mondani. Na, erre kellett nekem ez a normál eloszlás. Ha valamit 500-an mondtak, és valamit csak 1x, akkor szinte soha ne azt a "csak egyszer" választ adja, hanem azt, amit 500 ember.

De erre épkézláb megoldást nem találtam (emberi nyelven / JÓ programozói megoldással; nem matematikusi megfogalmazást), csak 2-3 feltett, de nagyjából megválaszolatlan Stack Overflow kérdést. De az egyik béna tömbös megválaszolása után eszembe jutott egy viszontlag memória- és CPU kímélő megoldás a problémára. Most teszteltem is, jónak tűnik.

Előbb leírom a konkrét PHP kódot (phptester.net-en nyomtam le, de bárhogy lehet tesztelgetni):

$results = array('Jól', 'Megvagyok', 'Rosszul'); $odds = array(70, 20, 10); $max = 0; for($i = 0; $i < count($results); $i++) $max += $odds[$i]; $rand = mt_rand(0, $max); $choosenIndex = 0; /* echo "Esély: $rand<br />"; */ for($i = 0; $i < count($results); $i++) { $rand = max(0, $rand - $odds[$i]); if($rand == 0) break; else $choosenIndex++; } echo ' - Hogy vagy? <br /> - ' . $results[$choosenIndex];

Van egy tömbnyi választandó elemem, meg egy tömbnyi ugyanolyan sorrendbe feltöltött esélyem. Persze, megoldható kétdimenziós tömbbel is, vagy ilyesmi, de én most az egyszerűség kedvéért így csináltam.

Nem is bonyolult így nézve, ugye? Az esélyek bármennyik lehetnek. Végül van egy maximum számom, a randomizálás tetőpontja. Ez az összes elem esélye összeadva. 70+20+10 (lehetne 5000+20+10 is, működik úgy is, ofc).

Majd egy változót tényleg randomizálunk 0 és max közt. Kell még egy index változó, ami a számok alapján kiválasztottnak ítélt tömbindexet mutatja majd.

Innentől logikus. Csinálunk egy akkora for ciklust, mint a tömb elemeinek száma. A randomunkból minden "körben" kivonjuk az épp ciklusváltozó szerinti sorrendben következő esélyek tömb számát. Nem muszáj 0-ra redukálni, de úgy szebb. Ezután ellenőrizzük, hogy a randomunk kevesebbegyenlő-e, mint 0. Ha igen, akkor elértünk oda, hogy a jelenlegi index esik abba a szakaszba, amivel párosul a választott tömbelem a másik tömbben. Ha nem, akkor túlléptünk egy elemnek a lehetőségén. Ennyi.

Ezt nem is igazán tudom elmagyarázni, ezért lerajzolom, így befejezésképp:


Nincsenek megjegyzések: