1. 程式人生 > >深度分析遊戲中的隨機概率

深度分析遊戲中的隨機概率

這段時間公司開發的遊戲上線測試,許多玩家在抽卡時抱怨臉黑,很難抽到所需要的卡牌,而又有一部分玩家反應運氣好能連著抽到紫卡,檢查了下隨機相關邏輯程式碼,並沒有找出問題所在,玩家運氣好與壞只是覺得真有可能是概率原因。

測試開服了幾天之後,需要開放某個限時抽卡活動,在內部測試時,我們發現玩家反應的問題在限時抽卡中格外明顯,尤其是其中最主要的一張稀有卡牌,猜測因為限時抽卡庫配置的種類較少,然後就拿該活動來檢查了下我們遊戲隨機機制問題。

5%概率?20次出現一次?

大部分遊戲策劃使用權值來配置隨機概率,因為權值有個好處就是可以在增加隨機物品時,可以不對之前的配置進行更改,比如:白卡 30,藍卡 10,紫卡 10,轉為概率即是:白卡 60%,藍卡 20%,紫卡 20%。

而上述限時抽卡的例子中,我們的權值配置是5和95,模擬50000次隨機(使用系統隨機函式,如C的rand函式,Python的random庫)得到如下結果:

rnd1.jpg

上圖繪製的是權值為5的卡牌的隨機狀態,紅色的圖是分佈圖,X軸是出現的次數,Y軸是相同卡牌再次出現的間隔。綠色的圖是分佈概率圖,X軸是間隔數,Y軸是概率。按策劃的想法,5%概率應該等同於20次出現一次,那上圖很明顯並不滿足20次出現一次出現規則,實際間隔從近到遠呈下坡形狀分佈,就是說相鄰的概率最大,間隔最大超過160,這與玩家所吐槽的抽卡體驗是一致的。但50000次隨機總共出現了2508次,從統計的意義上來說又是符合5%概率的。所以這個問題,究其原因就是所謂的概率是統計意義上的還是分佈意義上的問題。

最原始的實現

我用列表裡取元素的方式來模擬20次出現一次,為了方便比較異同,直接隨機的方式我也貼上相關程式碼。

1 2 pool = [0]*5 + [1]*95 result = [random.choice(a) for in xrange(N)]

上面是直接隨機的方式,只保證5%概率。

1 2 3 4 5 6 7 8 pool = [] result = [] for in xrange(N): if not pool: pool = [0]*1 + [1]*19 random.shuffle(pool) result.append(pool[-1])
del pool[-1]

上面是打亂列表,然後依次取元素的方式,保證20次出現一次,而5%概率則是隱含在內的,生成效果如下圖。

rnd2.jpg

該圖明顯跟第一個實現的圖不一樣,上圖表明瞭間隔基本上是落在[0, 40]的區間內,並且均勻分佈在20那條藍色對稱線附近。這個才是最終想要的隨機的效果。紅色的線是正態分佈曲線,是不是很相似?後面我會講到。

眼尖的會發現在第一個實現中我用的pool是[0]*5 + [1]*95,而第二個實現中我用的是[0]*1 + [1]*19。

這裡20次出現一次並不等同於100次出現五次,也是從分佈的意義上來說的,100次出現五次是存在5次連續出現的可能。

針對策劃的配置,我們需要進行預處理,怎麼處理?GCD啊~,5和95的最大公約數是5,所以在第二個實現的程式碼中我直接使用了1和19。

但這裡有個問題,一般策劃配置的隨機庫中肯定有多個物品。權值如果配置的比較隨意的話,很可能就導致GCD為1,這樣想要實現XX次出現一次就不可行了。比如剛才的權值配置5和95,再加一個權值為11的話,就只能實現111次出現5次。

所以這兩種依賴列表的隨機方式並不適用,一是需要維護的列表記憶體會比較大,二是對策劃配置方式有過多約束。

更通用更優美的實現

20次出現一次是以20為標準週期,當然不能每次都是間隔20出現,這樣就太假了,根本沒有隨機感受可言,為了模擬隨機並可以控制一定的出現頻率,我選擇正態分佈來進行偽隨機分佈生成,原因是分佈會更自然一些。

rnd3_Normal_distribution.jpg

關於正態分佈這裡就不詳細描述了,只需關心分佈的兩個引數即可,位置引數為μ、尺度引數為σ。根據正態分佈,兩個標準差之內的比率合起來為95%;三個標準差之內的比率合起來為99%。

rnd4_normal_sigma.jpg

用上面的例子來定下引數,μ=20,σ=20/3,這樣每次按正態分佈隨機,就能得到一個理想的隨機分佈和概率區間。

C語言標準函式庫中只有rand,如何生成符合正態分佈的隨機數可以參見WiKi上的介紹。這裡我直接使用Python中random庫中的normalvariate函式,當然gauss函式也是一樣的,官方文件上說gauss函式會快些,StackOverFlow上說gauss是非執行緒安全函式,所以會快。我自己簡單測試了下,在單執行緒情況下,gauss是會快些,但只是快了一點點而已。

首先,我直接生成權值為5的卡牌的間隔,檢驗下正態分佈的隨機效果。

1 2 3 NN = int(N*0.05) mu, sigma = 20, 20/3. delta = [int(random.normalvariate(mu, sigma)) for in xrange(NN)]

rnd3.jpg

這圖是不是比第二個實現的圖更好看一些,分佈也更平滑一些呢。OK,接下來就是替換舊的隨機演算法了。

細節和優化

剛才說了隨機庫中會有很多物品,都需要按照各自的權值隨機,並各自出現頻率符合正態分佈。下面我們來說說細節。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 wtp = [1.*x/sum(wt) for in wt] result = [] p = [random.normalvariate(1./x, 1./x/3.) for in wtp] for in xrange(N): minp = 1.e9 minj = -1 for j, pp in enumerate(p): if pp < minp: minp = pp minj = j result.append(minj) for j, pp in enumerate(p): p[j] -= minp p[minj] = random.normalvariate(1./wtp[minj], 1./wtp[minj]/3.)

這裡我使用了統一的隨機種子,隨機測試了500萬次後,所得的結果與多個隨機種子差別不大。

簡單解釋下程式碼:初始化對所有物品按權值進行正態分佈隨機,每次取位置最小值的物品(也就是最先出現的),然後其它物品均減去該值,被取出的物品再單獨進行一次正態分佈隨機,再次迴圈判斷位置最小值。

這裡,每次都需要對所有物品進行求最小值和減法,都是需要遍歷的運算,我們可以有如下優化。

例如:(1,3,4) -> 取1減1, (0,2,3) -> 隨機1, (1,2,3),其實我們只是為了保持各物品之間位置的相對順序即可,將對其它物品的減法變成對自己的加法,操作量級立馬從O(N)縮為O(1) 。

如上面的例子:(1,3,4) -> 取1, (0,3,4) -> 隨機1加1, (2,3,4),這樣的操作不會改變物品序列的正確性。

熟悉最小堆的朋友,將查詢最小值優化到O(1)應該也沒啥問題吧。

1 2 3 4 5 6 7 8 wtp = [1.*x/sum(wt) for 

相關推薦

深度分析遊戲隨機概率

這段時間公司開發的遊戲上線測試,許多玩家在抽卡時抱怨臉黑,很難抽到所需要的卡牌,而又有一部分玩家反應運氣好能連著抽到紫卡,檢查了下隨機相關邏輯程式碼,並沒有找出問題所在,玩家運氣好與壞只是覺得真有可能是概率原因。 測試開服了幾天之後,需要開放某個限時抽卡活動,在內部測試

遊戲概率抽獎的php程式碼及使用方法

按概率抽取道具是遊戲活動中常用的功能,基本思路是在class中定義一個抽取概率的私有方法,在其他方法中呼叫此方法,並和其他活動道具配置相配合即可完成按概率抽獎的活動。 首先是一個按概率獲得相應概率id的私有方法: private function get_rand($Ar

演算法導論 第五章:概率分析隨機演算法 筆記(僱傭問題、指示器隨機變數、隨機演算法、概率分析和指示器隨機變數的進一步使用)

僱傭問題: 假設你需要僱用一名新的辦公室助理。你先前的僱傭嘗試都以失敗告終,所以你決定找一個僱用代理。僱用代理每天給你推薦一個應聘者。你會面試這個人,然後決定要不要僱用他。你必須付給僱用代理一小筆費用來面試應聘者。要真正地僱用一個應聘者則要花更多的錢,因為你必須辭掉目前的辦公室助理,還要付一

在區間(0,1)隨機的取出兩個數,則兩數之和大於等於1.1的概率

概率題 答案為0.405 過程:(0.9 × 0.9 ÷ 2) ÷ 1 #隨機從0-1抽兩數之和大於等於1.1的概率-約等於0.405 import random i=0 #i為符合條件的數目 n=num=eval(input('num=')) #n為迴圈數,num為總數 if num

Java設計模式之從[遊戲的兵種狀態轉換]分析狀態(State)模式

  假設我們正在做一個即時戰略遊戲,我們設計一個兵種,他在剛剛生產出來的時候是步兵,但是他可以切換武器,第一次切換會變成弓箭手,第二次切換會變成舉著盾牌的裝甲兵,第三次切換則又變成了步兵……如何實現這個切換的機制?我們一開始會想到,在步兵這個類中加入switch語句,然而這

Java設計模式之從[打飛機遊戲的控制器]分析命令(Command)模式

  首先請允許我囉嗦幾句。為什麼我們在軟體設計過程中強調設計模式?為軟體增加設計模式確實會增加一定的程式碼複雜程度,但是它的好處是無窮的。它可以使得軟體更加易於擴充套件而無需改變原始碼。“解耦”是設計模式中的一個關鍵詞。例如,對於某個物件obj,在呼叫obj.method(

Java設計模式之從[遊戲開啟寶箱]分析中介者(Mediator)模式

  假設我們正在設計一個遊戲房間,房間裡有兩個按鈕和一個寶箱,如果我們按下了按鈕1,則按鈕2和寶箱均不可使用;如果我們按下了按鈕2,則按鈕1和寶箱均不可使用;如果我們開啟寶箱,則按鈕1不可按下。   在上面的例子中,我們可以看到兩個按鈕和一個寶箱之間存在著依賴關係,它們之間

深度學習概率知識詳解

1. 基礎概念 隨機變數(連續,離散): 對可能狀態的描述, 在機器學習演算法中,每個樣本的特徵取值,標籤值都可以看作是一個隨機變數,包括離散型隨機變數和連續型隨機變數 概率分佈: 用來指定每個狀態的可能性, 對於離散型的概率分佈,稱為概率質量函式(Prob

深度分析如何在Hadoop控制Map的數量

很多文件中描述,Mapper的數量在預設情況下不可直接控制干預,因為Mapper的數量由輸入的大小和個數決定。在預設情況下,最終input佔據了多少block,就應該啟動多少個Mapper。如果輸入的檔案數量巨大,但是每個檔案的size都小於HDFS的blockSize

知乎問題"房間裡100個人,每人1000元,他們玩一個遊戲,每輪遊戲,每個人拿出1元,隨機給另一個人,最後他們的財富分佈是怎樣的"實踐解答

知乎上有個有趣的問題,房間裡100個人,每人1000元,他們玩一個遊戲,每輪遊戲中,每個人拿出1元,隨機給另一個人,最後他們的財富分佈是怎樣的? 朋友圈有轉文章《用資料分析告訴你這個世界》分析,可以負債的情況下,17000次後,接近冪律分佈。 驗證如下,結論就是該文章在資

遊戲隨機地形生成演算法(三)

在上篇教程中,我們已經把Map中的每個點都實實在在的畫了出來,四個點形成一個正方形。其中,正方形的每條邊都有一箇中點。 那麼,這節課我們來把該連在一起的點連起來,繪製我們的mesh。 public class Square { publ

從0到n-1隨機概率輸出m個不重複的數

題目描述 假設n遠大於m,程式設計實現從0到n-1中隨機等概率的輸出m個不重複的數。 void knuth(int n,int m) { srand((unsigned int)time(0

ThreadPoolExecutor的應用和實現分析)—— 任務處理相關源碼分析

stateless 自身 tran als row exce 繼承 break attribute 轉自:http://www.tuicool.com/articles/rmqYjq 前面一篇文章從Executors中的工廠方法入手,已經對ThreadPoolExecuto

Unity遊戲使用貝塞爾曲線

str net 順序 復雜 讓我 創建 函數 高程 gin 孫廣東 2015.8.15比方在3D rpg遊戲中。我們想設置彈道,不同的軌跡類型!目的:這篇文章的主要目的是要給你關於在遊戲怎樣使用貝塞爾曲線的基本想法。 貝塞爾曲線是最主要的曲線,一般用

什麽是遊戲的幀同步

提高 服務 顯示 多個 網絡數 操作 導致 其他 方式 遊戲中的幀同步是一種客戶端與服務器的同步方式,是為了實現高實時性的需求而設計的。在實時pvp遊戲中,要求每個客戶端高度同步,怎麽做到精確的同步呢,那就是向同步的所有客戶端廣播同步消息。由於網絡存在延遲,因此一個客戶端發

在java隨機生成一個無序且長度不大於10的字符串

image ack con 技術 exti pen collect tint span package xiangmu; import java.util.ArrayList; import java.util.Collections; import java.ut

如何高效地分析Android_log的問題?——查看Android源碼

work bug 發生 file roi 選擇 就會 技術分享 framework   在日常解bugs時,需要通過log日誌來分析問題,例如查看crash發生時的堆棧信息時,就會有Android的源碼的調用,這是就要去查看Android源碼。   1.進入Android源

【BZOJ1444】[Jsoi2009]有趣的遊戲 AC自動機+概率DP+矩陣乘法

pri 註意 script aaaaa mil size borde tput char 【BZOJ1444】[Jsoi2009]有趣的遊戲 Description Input 註意 是0<=P Output Sample

js從數組隨機獲取n個不重復的數據

課堂 || 不重復 function return func () 思路 != 做雲課堂的作業時遇到一要求,實現刷新頁面時顯示不同數據,(數組中20個據,頁面加載10個)。思路就是從0-19中隨機生成10個不同的數,讓數組取下標輸出數據。 下面是在num的範圍內生成n個不重

python隨機函數

import logs style pan 數字 字母 clas col cnblogs 1 #隨機函數 2 import random #導入random 3 #隨機生字符和數字的驗證碼 4 #(65-90)對應的ACIll碼對應的字符為a-z 5 temp