java实现以某个概率测试某事是否发生

java实现以某个概率测试某事是否发生

这是一个JAVA语言中的概率问题。也许在游戏编程中常常会遇到这样一种场景:某样道具有2%概率的暴击效果,在一次攻击中如何判定这种暴击效果会发生呢?这涉及到一个很简单也很有代表性的判断某概率下某次具体的实验中某事件是否发生的问题。我们当然不能人为的规定第几次一定发生或者满足某种条件就一定发生,既然是概率,就说明是事件是随机的,并非人为可控的。如何实现呢?我们知道,说到随机,就难免提及java为我们提供的随机数工具Random,Random的各种方法可以为我们产生伪随机数,虽然这些随机数也是通过某些复杂的算法产生的,与真正自然状态下的随机数有所区别,但在绝大多数情况下把它们视作真正的随机数已经足够了。
以random.nextInt(n)为例,它可以产生在区间[0,n)之间的随机数,这些随机数分布是均匀的,也就是说在[0,n)之间的任一一个数为随机数是等概率的。比如,在r=random.nextInt(100);中r为0,1,2,3,...,99的概率是相等的,均为1/100(1%)。如何来验证这一点呢?这里需要一点点的数学技巧了。一般情况下,概率无法直接计算得到,我们可以用频率来近似的表示概率。而且随着实验次数的不断增加,频率一、会越来越接近概率,并且围绕着某个数值波动,那个数值就是我们要的概率。明白这一点,我们就可以测试random.nextInt(n)产生的结果是否有足够的等可能性性。所以我们将在[0,99)这100个数内以100次,500次,1000次,5000次,10000次,以及1000000的取随机数字测试,并统计各数字出现的频率,如果每个数字出现的频率会逐步稳定在0.1左右,则说明各数字出现的概率是0.1,也就说明了random工具的等概率性。*(注:当然要说明的一点是我们采用的是不完全测试,并且将用不完全测试的结果来进行结论,这是一种不完全归纳。 但因为进行归纳的样本已经足够大,所以我有理由取信归纳的结果。)
用于进行测试的代码如下:
<pre name="code" class="java">package com.zyzz.test;

import java.util.Random;

public class PossibilityTest {

	public static void main(String[] args) {

		// 进行测试
		baseTest(100000);

	}

	/**
	 * 频率测试
	 * 
	 * @param time
	 */
	private static void baseTest(int time) {
		// 各数字的出现次数的计数器
		final int[] NumberCounter = new int[100];
		// 初始化一下计数器
		for (int j = 0; j < NumberCounter.length; j++) {
			NumberCounter[j] = 0;
		}
		// 创建一个随机数发生器
		final Random random = new Random();
		// 用来暂存随机数结果
		int rads = 0;
		for (int i = 0; i < time; i++) {
			rads = random.nextInt(100);
			// 取得的随机数的对应计数器加一
			NumberCounter[rads]++;
		}
		// 下面就是打印结果了
		// 打印表头
		System.out.println("\t\t\t" + time + "次测试\n");
		System.out.println("\t数字\t|\t出现的次数\t\t频率\t");
		// 频率
		float f = 0.0f;
		// 总频率
		for (int k = 0; k < 100; k++) {
			f = (float) NumberCounter[k] / time;
			System.out.println("\t" + k + "\t|" + "\t" + NumberCounter[k]
					+ "\t" + "\t" + f + " (" + f * 100 + "%)" + "\t");
		}

	}

	private static void possiTest(int time) {
		int counter = 0;
		for (int i = 0; i < time; i++) {
			if (possibility(40))
				counter++;

		}
		System.out.println("进行" + time + "次测试");
		System.out.println("true:" + counter + "\nf="
				+ ((float) counter / time));
	}

}

 
 部分输出结果: 
 
	数字	|	出现的次数		频率	
	0	|	978		0.00978 (0.978%)	
	1	|	1066		0.01066 (1.066%)	
	2	|	977		0.00977 (0.97700006%)	
	3	|	978		0.00978 (0.978%)	
	4	|	979		0.00979 (0.979%)	
	5	|	1037		0.01037 (1.0370001%)	
	6	|	1030		0.0103 (1.03%)	
	7	|	1027		0.01027 (1.0270001%)	
	8	|	953		0.00953 (0.95300007%)	
	9	|	942		0.00942 (0.94200003%)	
	10	|	987		0.00987 (0.987%)	
	11	|	1055		0.01055 (1.055%)	
	12	|	996		0.00996 (0.99600005%)	
	13	|	1036		0.01036 (1.036%)	
	14	|	976		0.00976 (0.97599995%)	
	15	|	1011		0.01011 (1.011%)	
	16	|	981		0.00981 (0.98099995%)	
	17	|	967		0.00967 (0.96699995%)	
	18	|	997		0.00997 (0.997%)	
	19	|	990		0.0099 (0.99%)	
	20	|	1013		0.01013 (1.013%)	
	21	|	1017		0.01017 (1.017%)	
	22	|	999		0.00999 (0.999%)	
	23	|	962		0.00962 (0.96199995%)	
	24	|	1003		0.01003 (1.0029999%)	
	25	|	952		0.00952 (0.95199996%)	
	26	|	1065		0.01065 (1.0649999%)	
	27	|	963		0.00963 (0.963%)	
	28	|	978		0.00978 (0.978%)	
将这几个输出结果用图形表示出来:

从图型可以看出,实验进行的次数越多,就越趋近于一条x=1.0(%)的直线,根据概率与频率的关系,可以得出r区间[0,100)其中任一个数被取出的概率为1%,这样也说明了Random工具取随机数是等概率的。
说明了这一点,利用Random工具来模拟概率问题(比如上文所说的“2%概率暴击是否发生”)就比较简单了。
首先是如何产生某个特定的概率,比如40%。因为random.nextInt(n)可能产生的总的结果数是有限的(可能的结果为[0,n)之间的n个整数),上面也通过论证了每个可能的结果是等概率的。所以适用于古典概型公式:p=符合条件的事件数/总的事件数=m/n。n=100时,需要m=40,才能达到p=m/n=40%的目标,转换成代码:
public final static boolean possibility(int possibility){
		final Random random =new Random();
		if(random.nextInt(100)<possibility)
			return true;
		return false;
	
	}
代码就这么简单,下面来测试一下它是不是有效。就以40%为例,还是概率与频率的关系。如果上面的代码是有效的话,那么进行大量的实验,那么什么函数返回真的频率应稳定在0.4(40%)左右。验证一下
package com.zyzz.test;

import java.util.Random;

public class PossibilityTest {

	public static void main(String[] args) {

		// 进行测试
		// baseTest(100000);
		 possiTest(10000);

	}

	/**
	 * 频率测试
	 * 
	 * @param time
	 */
	private static void baseTest(int time) {
		// 各数字的出现次数的计数器
		final int[] NumberCounter = new int[100];
		// 初始化一下计数器
		for (int j = 0; j < NumberCounter.length; j++) {
			NumberCounter[j] = 0;
		}
		// 创建一个随机数发生器
		final Random random = new Random();
		// 用来暂存随机数结果
		int rads = 0;
		for (int i = 0; i < time; i++) {
			rads = random.nextInt(100);
			// 取得的随机数的对应计数器加一
			NumberCounter[rads]++;
		}
		// 下面就是打印结果了
		// 打印表头
		System.out.println("\t\t\t" + time + "次测试\n");
		System.out.println("\t数字\t|\t出现的次数\t\t频率\t");
		// 频率
		float f = 0.0f;
		// 总频率
		for (int k = 0; k < 100; k++) {
			f = (float) NumberCounter[k] / time;
			System.out.println("\t" + k + "\t|" + "\t" + NumberCounter[k]
					+ "\t" + "\t" + f + " (" + f * 100 + "%)" + "\t");
		}

	}

	private static void possiTest(int time) {
		int counter = 0;
		for (int i = 0; i < time; i++) {
			if (possibility(40))
				counter++;

		}
		System.out.println("进行" + time + "次测试");
		System.out.println("true:" + counter + "\nf="
				+ ((float) counter / time));
	}

	private final static boolean possibility(int possibility) {
		final Random random = new Random();
		if (random.nextInt(100) < possibility)
			return true;
		return false;

	}
}

多次测试的部分输出:
进行100次测试
true:37
f=0.37

进行1000次测试
true:385
f=0.385

进行10000次测试
true:4043
f=0.4043

进行1000000次测试
true:400543
f=0.400543

进行10000000次测试
true:4001353
f=0.4001353

...
从上面的输出可以看到,返回true的频率稳定在0.4左右,说明返回true的概率为40%,与我们传入的参数一致,说明这个简单到只有几行的函数还是有用的。
回到最初,2%概率暴击的问题,应该可以这样写了
	static boolean attack() {
		if (possibility(2)) {
			System.out.println("暴击效果!");
			return true;
		} else {
			System.out.println("一般攻击");
			return false;
		}
	}
}
好了。







贝叶斯分类器是一种基于统计原理的分类方法,它可以用来实现手写数字识别。在使用Java实现贝叶斯分类器时,我们可以按照以下步骤进行: 1. 数据准备:首先,我们需要准备用于训练和测试的手写数字数据集。常用的数据集有MNIST,它包含大量的手写数字图像及其对应的标签。 2. 特征提取:对于手写数字识别,常用的特征是图像的像素值。我们可以将图像转换为一个特征向量,其中每个元素是一个像素的灰度值。这样,每个数字就可以表示为一个向量。 3. 训练模型:使用训练数据集,计算每个数字类别的先验概率和条件概率。先验概率表示每个数字出现的概率,条件概率表示给定某个数字类别下某个像素的灰度值的概率。 4. 分类预测:对于给定的测试样本,计算它属于每个数字类别的概率,然后选择概率最大的类别作为预测结果。计算概率时,利用贝叶斯公式将先验概率和条件概率结合起来。 5. 模型评估:使用测试数据集,计算分类器的准确率、精确率、召回率等指标,评估分类器的性能。 在使用Java实现贝叶斯分类器时,可以通过多维数组、循环和条件判断语句来完成特征提取、模型训练和分类预测的过程。同时,可以利用Java提供的数据结构和算法库来简化计算和数据处理的过程。 此外,还可以利用Java的多线程机制,对于大规模的手写数字数据集进行并行计算,提高分类器的训练和预测速度。 总之,使用Java实现贝叶斯分类器可以实现手写数字识别,通过合理设计和优化,可以得到高效准确的分类器,并在实际应用中取得良好的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值