作者AI3767 (AI3767)
看板java
標題Re: [問題] 亂數產生一個矩陣
時間Wed Mar 15 01:52:20 2006
※ 引述《zanyking (遙遠的旅人)》之銘言:
: : private RangeRandom(int[] rangeArr, int off, float[] probArr) {
: : offset = off;
: : probabilityArray = probArr;
: : setAccumulateArray(rangeArr);
: : setProbArray(probArr);
: 這裡的寫法很可怕,難懂,又容易錯。
: 兩個set方法互相要求對方的成員跟自己的長度要一樣,而且你是先讓:
: probabilityArray = probArr;來方便你取得正確的長度
: 可是在setProbArray(probArr);卻又一定會把probArr指向另外一個值。
: 這樣寫很危險。
嗯...這裡其實, 在setProbArray的方法裡, 並不會將probArr指向另一個值
因為實作上, 是將probArr做了累積計算放到probabilityArray裡
為的是在取亂數時較為簡易快速
雖然在建構時做了assign的動作, 但在setProbArray裡,
會重新assign新的陣列, 這樣不會改動到原 probArr
而且在物件建出後, 還可以重新設定新的機率, 但是得和原範圍數量相等
例如 probArr為{0.2f,0.7f,0.1f}則在setProbArray裡
會將probabilityArray={0.2f, 0.9f, 1.0f}
本來是想將 set 的方法都private掉, 避免誤用
但想說至少 protected 的話, 還有機會被override掉, 如果不滿意的話 ^^
: : genRandom = new Random();
: : } //{END constructor}
: : /** 利用 <code>range</code> 陣列所設定的各個範圍大小, 相對同個索引位置的
: : * <code>probs</code> 陣列是該位置範圍的機率. <p>
: : * 舉例當呼叫 <code>createRangeRandom(new int[]{1,3,2}, 2,
: : * new float[]{0.2f,0.7f,0.1f})</code>,<br>
: : * 則亂數產生 2 的機率有 20%; 3, 4, 或5 的機率有 70%; 6 或 7 的機率有 10%.
: 這裡非常的難懂、也非常的不好用。那個new int[]{1,3,2}
: 指的是從2開始1個數、3個數、2個數吧?
: 也就是說如果我想要2,3,4,5,6,7分別有不同的機率我就要宣告:
: rangs = new int[]{1,1,1,1,1,1};
: probs = new float[]{0.1f,0.1f,0.1f,0.1f,0.2f,0.3f};
若是直接給定的話, 的確是難用的, 我的設想是在
使用者應該會去維護一個範圍array,如rArr,以及對映的機率array,如pArr
然後在要用時, 才去createRangeRandom(rArr,0,pArr)
而上面您給的例子, 可以是
int[] rangs = {4, 1, 1};
float probs = {0.4f, 0.2f, 0.3}; //其實會exception, 因為總和不為1.0f
createRangeRandom(rangs, 2, probs);
真正的缺點是在於如果要用的範圍中間是有大量小範圍gap時,
例如: 要 1~20, 22~25, 27~30, 31~35, 37~38 40~42
: 這樣的使用者介面超難用。
: 因為事實上,我並不需要知道個別值佔總體的比例,我只需要知道一個純量的權重就好。
: 也就是說像這樣:
: new int[]{12,123,34,1,6,77}
: 至於個別比例是多少,是寫程式的人要負責算的。
: : * <p>
: : * <code>probs<code> 機率陣列的機率和, 必須為 1.0f, 否則會丟出一個
: : * <code>RuntimeException</code>
...
: : private int getCase() {
: : int c = Arrays.binarySearch(probabilityArray, genRandom.nextFloat());
: : if( c<0 ) return -(++c);
: : else return c;
: : } //{END} private int getCase()
: 如果是要練習Arrays.binarySearch...那不錯。
: 不過事實上:return genRandom.nextInt(probabilityArray.length); 就好了。
: 寫程式,好懂的一行文才是王道。^^
這邊是不能用這樣的作法, 原因就在於我用的probabilityArray
實際是原機率array的累計值, 而每個範圍的機率不同,
所以取亂數時, 會依照random出來的float值,
來找出所對映的所在範圍
以上面的例子, 機率為 0.2f 0.7f 0.1f
所以probabilityArray為 0.2f 0.9f 1.0f
當取float時, 若是 0.0~0.2 , 則會再從第1個範圍去取亂數,
這才是真正會回傳的亂數
若 float值>0.2 且<=0.9f, 則在第2個範圍取亂數
同樣當值>0.9f且 <=1.0f, 則在第3個範圍取亂數
而使用Arrays.binaraySearch在於當找不到時,
回傳的值是 -(insertion point)-1, 這個在Documentation內有解釋
: : public int getRand() {
: : int caseRange = getCase();
: : if( caseRange==0 )
: : return offset+genRandom.nextInt(accumulateArray[0]-offset);
: : else
: : return accumulateArray[caseRange-1]+genRandom.nextInt(
: : accumulateArray[caseRange]-accumulateArray[caseRange-1]);
: : } //{END} public int getRand()
: : protected void setProbArray(float[] probArr) {
: : if( probArr.length!= accumulateArray.length )
: : throw new RuntimeException("Array size doesn't match.");
: : float test = 0f;
: : probabilityArray = new float[probArr.length];
: : for(int i=0; i<probArr.length; i++) {
: : test += probArr[i];
: : probabilityArray[i] = test;
: : }
: : if( test!= 1.0f )
: : throw new RuntimeException("Probability doesn't equal to 1.0.");
: : } //{END} protected void setProbArray()
...
: public static void main(String[] args)
: {
: RangeRandom rand = new RangeRandom(new int[]{1,3,2},2,new
: float[]{0.2f,0.7f,0.1f});
: TreeMap<Integer,Integer> ansMap = new TreeMap<Integer,Integer>() ;
: for(int i=0;i<100000;i++)
: {
: int test = rand.getRand();
: if(ansMap.containsKey(test)) ansMap.put(test,ansMap.get(test)+1);
: else ansMap.put(test,1);
: }
: for(Map.Entry<Integer,Integer> ent : ansMap.entrySet())
: {
: System.out.println("ans["+ent.getKey()+"]="+ent.getValue());
: }
: }
: //給你一個main()不然不能測。
: : } //{END class} class RangeRandom
: 其實這個問題,大家原理應該都是大同小異的。
: 我是給分佈方程式class,產生機率亂數。
: 也可以給離散數列,設成權陣Array輸入,產生機率亂數。
: 道理都相同的。
感謝 <(_ _)>
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.123.219.115
※ 編輯: AI3767 來自: 140.123.219.115 (03/15 01:54)
※ 編輯: AI3767 來自: 140.123.219.115 (03/15 01:59)
※ 編輯: AI3767 來自: 140.123.219.115 (03/15 02:01)