论坛首页 Java版

[讨论] 概率问题的研究

浏览 248 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
时间:2008-05-17 关键字: 概率 几率 随机
众所周知计算机世界里没有绝对的随机函数 所谓的随机都是由特定方法计算的来的 也就是有规律的 那计算一组概率呢?
最近在做游戏的开发 遇到很多随机掉落的问题 一个npc身上掉落装备有很多件 每件的掉落都是有概率的 以便控制游戏的平衡
怎么样才能相对公平呢? 我想到了3种方案 和大家分享一下吧 也希望大家能给我一些更好的思路

getPersent1
这个是最基本的概率处理方法 百分之的几率 要么ture要么false 处理单一事件概率的都这么写

getPersent2 思路
处理一组事件的几率 根据不同事件设定的百分率返回
重要思路是递归 在所有给定的几率中伪随机得到一个 计算这个几率是否返回ture 如果true就认定这个概率得到一次 false则进行递归调用指导返回ture为止
优点: 几率相对平静的时候效率好 在调用频繁的时候效率好 执行伪随机调用测试少
缺点: 在极端情况下可能出现无法返回的情况例如1,1,1,1,1....这样的几率求解

getPersent3 思路
我引入了随机池的概念 按照给定的几率 随机设定100个数组成的随机数组 之后通过一个0-99的伪随机数得到返回
优点: 相对公平 非频繁调用效率好
缺点: 无法处理超过100个事件的几率问题 伪随机调用相对较多

import java.util.Arrays;

/**
 * 本类主要包括3个静态方法,通过调用可以得到一个设定百分率产生的随即结果。
 *
 * @author 赵麟
 * @version 1.0, 2008/04/26
 */
public class TestMain
{
	private static int[] array = new int[100];
	private static long count1 = 0;
	private static long count2 = 0;
	private static long count3 = 0;
	
	public static void main(String[] args)
	{
		int loop = 1;
		int[] persents = {20,10,30,40};
		int[] result2 = new int[persents.length];
		int[] result3 = new int[persents.length];
		long beginTime = System.nanoTime();
		
		// getPersent1 测试
		System.out.println("getPersent1 测试开始 : ");
		int trueNum=0,falseNum=0;
		for (int i=0;i<loop;i++)
		{
			if (getPersent1(persents[0]))
			{
				trueNum++;
			}
			else
			{
				falseNum++;
			}
		}
		System.out.println(persents[0]+"% -> "+trueNum+" , "+1.0d*trueNum/loop*100+"%\n"+(100-persents[0])+"%  -> "+falseNum+" , "+1.0d*falseNum/loop*100+"%");
		System.out.println("getPersent1执行"+loop+" 随机调用 : "+count1+" , 用时 : "+(System.nanoTime()-beginTime)/1000000.0d+"ms");
		
//		 最差效率极限测试
//		persents = new int[loop];
//		result2 = new int[persents.length];
//		result3 = new int[persents.length];
//		for (int i=0;i<persents.length;i++)
//		{
//			persents[i] = 1;
//		}
		// getPersent2 测试
		System.out.println("\ngetPersent2 测试开始 : ");
		for (int i=0;i<loop;i++)
		{
			result2[getPersent2(persents)]++;
		}
		for (int i=0;i<result2.length;i++)
		{
			System.out.println(persents[i]+"% -> "+result2[i]+" , "+1.0d*result2[i]/loop*100+"%");
		}
		System.out.println("getPersent2执行"+loop+" 随机调用 : "+count2+" , 用时 : "+(System.nanoTime()-beginTime)/1000000.0d+"ms");
		
		// getPersent3 测试
		System.out.println("\ngetPersent3 测试开始");
		for (int i=0;i<loop;i++)
		{
			result3[getPersent3(persents)]++;
		}
		for (int i=0;i<result3.length;i++)
		{
			System.out.println(persents[i]+"% -> "+result3[i]+" , "+1.0d*result3[i]/loop*100+"%");
		}
		System.out.println("getPersent3执行"+loop+" 随机调用 : "+count3+" , 用时 : "+(System.nanoTime()-beginTime)/10000000.0d+"ms");

		
	}

	/**
	 * 单百分率随机抽取计算处理
	 * @author CoolinZ
	 * 
	 * @param int per 要计算的百分比
	 * @return bolean 选中返回ture,否则返回false
	 */
	private static boolean getPersent1(int per)
	{
		// 特殊情况判断
		if (per==100)
		{
			 return true;
		}
		else
		{
			if (per!=0)
			{
				// 纪录随机函数调用次数 效率检验用
				count1++;
				count2++;
				count3++;
				// 产生1至100之间的一个伪随机数整数
				int random = (int)((Math.min(Math.random() + 0.01d,1.0d)) * 100);
				
				return random<=per;
			}
		}
		
		return false;
	}
	
	private static int getPersent2(int[] per) throws NumberFormatException
	{
		if (per==null)
		{
			throw new NullPointerException("参数为空。");
		}
		else
		{
			if (per.length==1)
			{
				throw new NumberFormatException("参数太少。");
				//return getPersent1(per[0]);
			}
			else
			{
				// 纪录随机函数调用次数
				count2 += 2;
				int random = (int)((Math.random() + 1.0d) * per.length) % per.length;
				return getPersent1(per[random])?random:getPersent2(per); 
			}
		}
	}
	
	/**
	 * 通过创建随机池的方式 按照设定的多组百分率,随机抽取计算
	 * @author CoolinZ
	 * 
	 * @param int[] per2 设定的百分比的数组
	 * @return 被选中的百分率在参数数组对应的下标数
	 * @throws NumberFormatException 参数蹿地个时不正确
	 */
	private static int getPersent3(int[] per2) throws NumberFormatException
	{
		// 参数null检测
		if (per2==null)
		{
			throw new NullPointerException("参数为空");
		}
		else
		{
			int[] per = initArray(per2);
			int random = 0;
			for (int i=0;i<array.length;i++)
			{
				count3++;
				random = (int)((Math.random() + 1.0d) * per.length) % per.length;
				if (per[random]>0)
				{
					array[i] = random;
					per[random]--;
				}
				else
				{
					for (int j=0;j<per.length;j++)
					{
						if (j!=random&&per[j]>0)
						{
							array[i] = j;
							per[j]--;
							break;
						}
					}
				}
			}
			count3++;
			return array[(int)((Math.random() + 1.0d) * array.length) % array.length]; 
		}
	}
	
	/**
	 * 通过几率参数得到随机池
	 * @author CoolinZ
	 * 
	 * @param int[] arr2 几率数组
	 * @return int[] 按照参数几率得到的含有100个数字的随机池数组
	 */
	private static int[] initArray(int[] arr2)
	{
		int[] arr = new int[arr2.length];
		for(int i=0;i<arr.length;i++)
		{
			arr[i] = arr2[i];
		}
		// 初始化数组
		Arrays.fill(array,-1);
		int sum = 0;
		for (int i : arr)
		{
			sum += i;
		}
		if (sum!=array.length&&sum!=0)
		{
			for (int i=0;i<arr.length;i++)
			{
				arr[i] = (int)(1.0d*arr[i]/sum*100);
			}
			sum = 0;
			for (int i : arr)
			{
				sum += i;
			}
			if (sum!=array.length)
			{
				count3++;
				int random = (int)((Math.random() + 1.0d) * arr.length) % arr.length;
				arr[random] += array.length - sum;
				if (arr[random]<0) arr[random]=0;
				if (arr[random]>100) arr[random]=100;
			}
		}
		
		return arr;
	}
}




我分别测试了10,100,1000,10000,100000几种情况下的效率 结果如下

getPersent1 测试开始 : 
20% -> 0 , 0.0%
80%  -> 1 , 100.0%
getPersent1执行1 随机调用 : 1 , 用时 : 1.370065ms

getPersent2 测试开始 : 
20% -> 0 , 0.0%
10% -> 0 , 0.0%
30% -> 0 , 0.0%
40% -> 1 , 100.0%
getPersent2执行1 随机调用 : 4 , 用时 : 1.664772ms

getPersent3 测试开始
20% -> 0 , 0.0%
10% -> 0 , 0.0%
30% -> 0 , 0.0%
40% -> 1 , 100.0%
getPersent3执行1 随机调用 : 103 , 用时 : 0.2041043ms

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

getPersent1 测试开始 : 
20% -> 2 , 20.0%
80%  -> 8 , 80.0%
getPersent1执行10 随机调用 : 10 , 用时 : 1.802515ms

getPersent2 测试开始 : 
20% -> 1 , 10.0%
10% -> 0 , 0.0%
30% -> 6 , 60.0%
40% -> 3 , 30.0%
getPersent2执行10 随机调用 : 139 , 用时 : 2.30035ms

getPersent3 测试开始
20% -> 1 , 10.0%
10% -> 0 , 0.0%
30% -> 4 , 40.0%
40% -> 5 , 50.0%
getPersent3执行10 随机调用 : 1063 , 用时 : 0.3901289ms

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

getPersent1 测试开始 : 
20% -> 28 , 28.000000000000004%
80%  -> 72 , 72.0%
getPersent1执行100 随机调用 : 100 , 用时 : 3.083059ms

getPersent2 测试开始 : 
20% -> 14 , 14.000000000000002%
10% -> 10 , 10.0%
30% -> 45 , 45.0%
40% -> 31 , 31.0%
getPersent2执行100 随机调用 : 1252 , 用时 : 4.408793ms

getPersent3 测试开始
20% -> 18 , 18.0%
10% -> 13 , 13.0%
30% -> 27 , 27.0%
40% -> 42 , 42.0%
getPersent3执行100 随机调用 : 10584 , 用时 : 0.7471563ms

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

getPersent1 测试开始 : 
20% -> 173 , 17.299999999999997%
80%  -> 827 , 82.69999999999999%
getPersent1执行1000 随机调用 : 1000 , 用时 : 2.504266ms

getPersent2 测试开始 : 
20% -> 204 , 20.4%
10% -> 86 , 8.6%
30% -> 287 , 28.7%
40% -> 423 , 42.3%
getPersent2执行1000 随机调用 : 12796 , 用时 : 4.343045ms

getPersent3 测试开始
20% -> 209 , 20.9%
10% -> 107 , 10.7%
30% -> 305 , 30.5%
40% -> 379 , 37.9%
getPersent3执行1000 随机调用 : 105932 , 用时 : 1.4981965ms

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

getPersent1 测试开始 : 
20% -> 2092 , 20.919999999999998%
80%  -> 7908 , 79.08%
getPersent1执行10000 随机调用 : 10000 , 用时 : 4.122348ms

getPersent2 测试开始 : 
20% -> 2006 , 20.06%
10% -> 1011 , 10.11%
30% -> 2896 , 28.96%
40% -> 4087 , 40.87%
getPersent2执行10000 随机调用 : 128380 , 用时 : 11.95996ms

getPersent3 测试开始
20% -> 2001 , 20.01%
10% -> 1036 , 10.36%
30% -> 2956 , 29.56%
40% -> 4007 , 40.07%
getPersent3执行10000 随机调用 : 1059460 , 用时 : 11.7831945ms

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

getPersent1 测试开始 : 
20% -> 20135 , 20.135%
80%  -> 79865 , 79.865%
getPersent1执行100000 随机调用 : 100000 , 用时 : 16.952833ms

getPersent2 测试开始 : 
20% -> 20138 , 20.138%
10% -> 9913 , 9.913%
30% -> 30030 , 30.03%
40% -> 39919 , 39.919%
getPersent2执行100000 随机调用 : 1294279 , 用时 : 100.3098ms

getPersent3 测试开始
20% -> 20094 , 20.094%
10% -> 10047 , 10.047%
30% -> 29960 , 29.959999999999997%
40% -> 39899 , 39.899%
getPersent3执行100000 随机调用 : 10598093 , 用时 : 104.0197551ms
   
时间:2008-05-20
呵呵,我最近也在做游戏,正确也写了个哈哈哈哈,握手握手
int n1 = 0;
int n2 = 0;
public Random(int n1,int n2)
{
this.n1 = n1;
this.n2 = n2;
}

public boolean getcaprandon()
{
if(getRandom(1,n1)<=n2)
return true;
else
return false;
}

public static int getRandom(int min,int max)
{
return (int)((max+1-min)*Math.random()+min);
}
   
0 请登录后投票
时间:2008-05-20
之前提交好像把getpersent1给弄丢了 加上它应该就完整了
   
0 请登录后投票
论坛首页 Java版

跳转论坛:
快速回复 引用上一条消息 (Alt+S)