日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Alias Method解决随机类型概率问题(别名算法)

發(fā)布時(shí)間:2023/12/2 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Alias Method解决随机类型概率问题(别名算法) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

舉個(gè)例子,游戲中玩家推倒了一個(gè)boss,會(huì)按如下概率掉落物品:10%掉武器 20%掉飾品 30%掉戒指 40%掉披風(fēng)。現(xiàn)在要給出下一個(gè)掉落的物品類型,或者說一個(gè)掉落的隨機(jī)序列,要求符合上述概率。

一般人會(huì)想到的兩種解法

第一種算法,構(gòu)造一個(gè)容量為100(或其他)的數(shù)組,將其中10個(gè)元素填充為類型1(武器),20個(gè)元素填充為類型2(飾品)...構(gòu)造完畢之后,在1到100之間取隨機(jī)數(shù)rand,取到的array[rand]對(duì)應(yīng)的值,即為隨機(jī)到的類型。這種方法優(yōu)點(diǎn)是實(shí)現(xiàn)簡單,構(gòu)造完成之后生成隨機(jī)類型的時(shí)間復(fù)雜度就是O(1),缺點(diǎn)是精度不夠高,占用空間大,尤其是在類型很多的時(shí)候。

第二種就是一般的離散算法,通過概率分布構(gòu)造幾個(gè)點(diǎn),[10, 30, 60, 100],沒錯(cuò),后面的值就是前面依次累加的概率之和(是不是像斐波那契數(shù)列)。在生成1~100的隨機(jī)數(shù),看它落在哪個(gè)區(qū)間,比如50在[30,60]之間,就是類型3。在查找時(shí),可以采用線性查找,或效率更高的二分查找,時(shí)間復(fù)雜度O(logN)。

下面是第二種算法使用二分查找的實(shí)現(xiàn):

<?php class DiscreteSample {private $cdf;private $cnt;public function init($pdf){$this->cnt = count($pdf);if($this->cnt == 0)die("pdf size is empty");if(abs(array_sum($pdf) - 1) > 0.00001)die("pdf sum not equal 1, sum:".array_sum($pdf));$this->_pdf2cdf($pdf);}private function _pdf2cdf($pdf){$this->cdf = $pdf;for ($i=1; $i < $this->cnt; $i++){ $this->cdf[$i] += $this->cdf[$i - 1];}//因?yàn)楦↑c(diǎn)型精度問題,最后一個(gè)值強(qiáng)制為1$this->cdf[$this->cnt - 1] = 1;}public function next_rand(){$left = 0;$right = $this->cnt;$random = mt_rand() / mt_getrandmax();while ($left < $right - 1) {$mid = intval(($left + $right)/2);if($mid - 1 >= $this->cnt) break;if($random > $this->cdf[$mid - 1])$left = $mid;else$right = $mid;}return $left;} } ?>

Alias Method(別名方法)

別名算法最終的結(jié)果是要構(gòu)造拼裝出一個(gè)每一列合都為1的矩形,若每一列最后都要為1,那么要將所有元素都乘以4(概率類型的數(shù)量)

此時(shí)會(huì)有概率大于1的和小于1的,接下來就是構(gòu)造出某種算法用大于1的補(bǔ)足小于1的,使每種概率最后都為1,注意,這里要遵循一個(gè)限制:每列至多是兩種概率的組合。

最終,我們得到了兩個(gè)數(shù)組,一個(gè)是在下面原始的prob數(shù)組[0.4,0.8,0.6,1],另外就是在上面補(bǔ)充的Alias數(shù)組,其值代表填充的那一列的序號(hào)索引,(如果這一列上不需填充,那么就是NULL),[3,4,4,NULL]。當(dāng)然,最終的結(jié)果可能不止一種,你也可能得到其他結(jié)果。

等等,這個(gè)問題還沒有解決,得到這兩個(gè)數(shù)組之后,隨機(jī)取其中的一列,比如是第三列,讓prob[3]的值與一個(gè)隨機(jī)小數(shù)f比較,如果f小于prob[3],那么結(jié)果就是3,否則就是Alias[3],即4。

我們可以來簡單驗(yàn)證一下,比如隨機(jī)到第三列的概率是1/4,得到第三列下半部分的概率為1/4*3/5,記得在第一列還有它的一部分,那里的概率為1/4*(1-2/5),兩者相加最終的結(jié)果還是3/10,符合原來的pdf概率。這種算法初始化較復(fù)雜,但生成隨機(jī)結(jié)果的時(shí)間復(fù)雜度為O(1),是一種性能非常好的算法。

代碼示例

<?php class AliasMethod {private $length;private $prob_arr;private $alias;public function __construct ($pdf){$this->length = 0;$this->prob_arr = $this->alias = array();$this->_init($pdf);}private function _init($pdf){$this->length = count($pdf);if($this->length == 0)die("pdf is empty");if(array_sum($pdf) != 1.0)die("pdf sum not equal 1, sum:".array_sum($pdf));$small = $large = array();for ($i=0; $i < $this->length; $i++) { $pdf[$i] *= $this->length;if($pdf[$i] < 1.0)$small[] = $i;else$large[] = $i;}while (count($small) != 0 && count($large) != 0) {$s_index = array_shift($small);$l_index = array_shift($large);$this->prob_arr[$s_index] = $pdf[$s_index];$this->alias[$s_index] = $l_index;$pdf[$l_index] -= 1.0 - $pdf[$s_index];if($pdf[$l_index] < 1.0)$small[] = $l_index;else$large[] = $l_index;}while(!empty($small))$this->prob_arr[array_shift($small)] = 1.0;while (!empty($large))$this->prob_arr[array_shift($large)] = 1.0;}public function next_rand(){$column = mt_rand(0, $this->length - 1);return mt_rand() / mt_getrandmax() < $this->prob_arr[$column] ? $column : $this->alias[$column];} } ?>

?

轉(zhuǎn)載于:https://www.cnblogs.com/mmmzh/p/10140992.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的Alias Method解决随机类型概率问题(别名算法)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。