1,問題:?
在游戲中,我們經常會遇到以下情況:打開寶箱,獲得x個卡牌碎片。
? 但通常策劃只會給一個既定的數值空間,和一個期望得到的值,然后讓我們去隨機。比如:
? 問題A:在1~3之間的整數中隨機,期望隨機結果的平均值是1.175,如何實現?
? 延伸:1~10之間,期望平均值是5.4呢?
? 以上問題可以歸納為:m~n之間,期望值是q,求出最終結果。
? 首先,我們可以問下AI,如何實現這個算法。以下是AI給出結果的截圖:
? 這種按照多元方程求解的方法,需要自己去隨機給定概率,當然可以實現。但有沒有更直接的方法呢?
2,求解過程:
? 經過思考,我們可以用補償算法的方式,來得到這個既定結果。這里需要引入一個補償數的概念,補償數由每次隨機結果的偏差值累加而成。
? 因為每次隨機事件是獨立的,我們引入問題A,得到如下隨機結果:
第1次隨機區間1~3,隨機結果1,補償數為?0.175 ;
第二次隨機區間2~3,隨機結果2,補償數為-0.65;
第3次隨機區間1~2,隨機結果1,補償數為-0.475,小于精度0.5,取隨機數組結束。
隨機數組為1,2,1;在其中隨機取1位便是結果。
? 我們可以驗算下:(1+2+1)/3~=1.333,與1.175偏差小于0.5。代碼編譯:
? 然后我們將以上思考過程改為函數如下:
//獲取所有的隨機范圍void GetAllRandom(float _expect, int _min, int _max, float _precision){var _getList = new List<int>();GetRandomOnce(_getList, 0, 0f, _expect, _min, _max, _precision);var _f = 0f;var _str = "";foreach (var idx in _getList){_f += idx;_str += idx + ",";}Debug.LogError("得到結果數組:" + _str + "\n平均值:" + (_f / _getList.Count));Debug.LogError("最終結果:" + _getList[Random.Range(0, _getList.Count)]);}/// <summary>/// 取到的隨機數 數組/// </summary>/// <param name="_list">取值數組</param>/// <param name="_ranNum">隨機次數</param>/// <param name="_compensate">補償值:累加</param>/// <param name="_expect">期望值</param>/// <param name="_min">最小值</param>/// <param name="_max">最大值</param>/// <param name="_precision">精確度</param>/// 約束條件:/// 1,隨機次數>n次,比如1~10之間隨機,隨機次數需要>10/// 2,最終補償數需要誤差小于1(精確度) private void GetRandomOnce(List<int> _list, int _ranNum, float _compensate, float _expect, int _min, int _max, float _precision){//根據補償數重新定義范圍int _ran = 0;if (_compensate > 0f){var _tMin = Mathf.Min(_max, Mathf.CeilToInt(_min + _compensate));_ran = Random.Range(_tMin, _max + 1);Debug.Log(_tMin + " " + _ran + " " + _max);}else{var _tMax = Mathf.Max(_min, Mathf.FloorToInt(_max + _compensate));_ran = Random.Range(_min, _tMax + 1);Debug.Log(_min + " " + _ran + " " + _tMax);}_compensate += _expect - _ran;_ranNum++;Debug.LogError(_ran + " " + _compensate+" "+_ranNum);_list.Add(_ran);if (_ranNum > (_max - _min + 1) && Mathf.Abs(_compensate) < _precision) return;//滿足約束條件if (_ranNum > 100) { _list.Clear();_list.Add((int)_expect);return; }//防止意外GetRandomOnce(_list, _ranNum, _compensate, _expect, _min, _max, _precision);}
3,測試
? 寫個簡單方法測試下:
public int min = 1;//區間最小值public int max = 3;//區間最大值public float expect = 1.175f;//期望值public float precision = 0.5;//精度void Update(){if (Input.GetKeyDown(KeyCode.A)){GetAllRandom(expect, min, max, precision);}}
? 得到結果如下:
? 當然我們有時候不想隨機那么多步驟(純隨機的不確定性),還可以添加更多的約束。
? 最后,我們代入擴展再測試下。電腦給出的隨機結果如下:
? 經過多次測試,實驗結果是逼近期望值的(不過取越靠近中間數的話,獨立事件的結果偏差可能越大,這里最好再加上一層約束)。