洗牌随机算法的一种Java实现

5 篇文章 0 订阅
2 篇文章 0 订阅

       洗牌随机算法的多样性不言而喻,算法的关键取决于随机性——即每张牌经多次洗牌后在牌堆中分布顺序的均匀性。洗牌活动是人类手工能够完成的一项动作,方法多为左右手双手洗结合单手切牌,因此,本算法结合实际操作进行程序加工进行实现,核心代码如下:

Card类,作为一张卡牌的JavaBean

public class Card {
	private int id;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
Cards类,作为卡组,即一套牌的JavaBean:

import java.util.Vector;

public class Cards {
	private Vector<Card> cardSet = null;
	
	public Cards(Vector<Card> cardSet){
		this.cardSet = cardSet;
	}
	public Vector<Card> getCardSet() {
		return cardSet;
	}

	public void setCardSet(Vector<Card> cardSet) {
		this.cardSet = cardSet;
	}
}
CardsMethod类实现洗牌方法:

import java.util.Iterator;
import java.util.Random;
import java.util.Vector;

public class CardsMethod {
	public Cards shuffleCardsMethod(Cards cards)//洗牌
	{
		Vector<Card> cardSet = cards.getCardSet();
		int num = cardSet.size();//获取卡牌的总数
		Random rand = new Random();

		for(int i = 0;i < 1000;i++)
		{
			Vector<Card> tmpSet_1 = new Vector<Card>(num);//双手洗牌临时卡牌组
			Vector<Card> tmpSet_2 = new Vector<Card>(num);//单手切牌临时卡牌组
			//双手洗牌
			int left = (rand.nextInt(2) == 0) ? num / 2 + rand.nextInt(3) : num / 2 - rand.nextInt(3);
			//左手起牌,从卡牌组中间分开,上下浮动4张牌
			
			
			int sl = 0;//左手开始卡牌ID
			int sr = left;//右手开始卡牌ID
			int el = 0;//左手结束卡牌ID
			int er = 0;//右手结束卡牌ID
			
			while(true)//开始双手洗牌
			{
				el = sl + rand.nextInt(2) + 1;//左手每次下牌1~2张
				er = sr + rand.nextInt(2) + 1;//右手每次下牌1~2张
				if(el >= left - 1)//左手的牌先落完
				{
					for(;sl < left;sl++)
					{
						tmpSet_1.add(cardSet.get(sl));
					}
					for(;sr < num;sr++)//右手剩余的全部牌落到牌组底部
					{
						tmpSet_1.add(cardSet.get(sr));
					}
					break;
					
				}
				else if(er >= num)//右手的牌先落完
				{
					for(;sr < num;sr++)
					{
						tmpSet_1.add(cardSet.get(sr));
					}
					for(;sl < left;sl++)//左手剩余的全部牌落到牌组底部
					{
						tmpSet_1.add(cardSet.get(sl));
					}
					break;
				}
				else//正常洗牌过程
				{
					if(i % 2 == 0)//双数洗牌回合,
					{
						for(;sl < el;sl++)
						{
							tmpSet_1.add(cardSet.get(sl));
						}
						for(;sr < er;sr++)
						{
							tmpSet_1.add(cardSet.get(sr));
						}
					}
					else//单数洗牌回合
					{
						for(;sr < er;sr++)
						{
							tmpSet_1.add(cardSet.get(sr));
						}
						for(;sl < el;sl++)
						{
							tmpSet_1.add(cardSet.get(sl));
						}
					}
					
				}
			}
			cardSet = tmpSet_1;

			//单手切牌
			int sp = rand.nextInt(num/4) + num/10;//切牌的开始卡牌ID
			int ep = sp + rand.nextInt(num/4) + num/10;//切牌的结束卡牌ID
			int p = sp;
			while(true)
			{
				boolean btw = (sp <= ep) ? true : false;//交换卡牌
			
				if(btw)//如果没有交换完毕
				{
					tmpSet_2.add(cardSet.get(p));
					cardSet.remove(p);
					sp++;
				}
				else//交换结束,将其他所有剩余牌按原顺序放入到切出卡牌的下方
				{
					Iterator<Card> intator = cardSet.iterator();
					while(intator.hasNext())
					{
						tmpSet_2.add(intator.next());
					}
					break;
				}
			
			}
			cardSet = tmpSet_2;
		}
		System.out.println("shuffle");
		cards.setCardSet(cardSet);
		return cards;
	}
}
PlayCards类用于测试,统计id为0的卡牌在100000次洗牌中分布在牌堆中的数量情况:

import java.util.Vector;

public class PlayCards {
	
	public static void main(String[] args){
		
		int cardNum = 54;
		int[] sum = new int[cardNum];
		for(int i = 0;i < cardNum; i++){
			sum[i] = 0;
		}
		Vector<Card> cardSet = new Vector<Card>(cardNum);
		CardsMethod cm = new CardsMethod();
		for(int i = 0;i < cardNum; i++){
			Card card = new Card();
			card.setId(i);
			cardSet.add(card);
		}
		Cards cards = new Cards(cardSet);
		
		for(int i = 0;i < 54000; i++){
			cm.shuffleCardsMethod(cards);
			for(int j = 0;j < cardNum; j++){
				if(cards.getCardSet().get(j).getId() == 0){
					sum[j] += 1;
				}
			}
		}
		
		//id为0的卡在循环54000次后的分布情况
		for(int i = 0;i < cardNum;i++){
			System.out.println(sum[i]);
		}
	}
}

统计结果如下:

牌堆id:0 993
牌堆id:1 985
牌堆id:2 982
牌堆id:3 965
牌堆id:4 994
牌堆id:5 1054
牌堆id:6 978
牌堆id:7 1010
牌堆id:8 986
牌堆id:9 954
牌堆id:10 1047
牌堆id:11 1008
牌堆id:12 1003
牌堆id:13 1000
牌堆id:14 978
牌堆id:15 1083
牌堆id:16 1012
牌堆id:17 1029
牌堆id:18 955
牌堆id:19 1019
牌堆id:20 992
牌堆id:21 1060
牌堆id:22 1037
牌堆id:23 986
牌堆id:24 1022
牌堆id:25 1100
牌堆id:26 944
牌堆id:27 1011
牌堆id:28 978
牌堆id:29 1001
牌堆id:30 1039
牌堆id:31 1010
牌堆id:32 1005
牌堆id:33 948
牌堆id:34 954
牌堆id:35 994
牌堆id:36 1056
牌堆id:37 1001
牌堆id:38 941
牌堆id:39 963
牌堆id:40 997
牌堆id:41 970
牌堆id:42 1042
牌堆id:43 1009
牌堆id:44 969
牌堆id:45 983
牌堆id:46 997
牌堆id:47 993
牌堆id:48 981
牌堆id:49 1024
牌堆id:50 974
牌堆id:51 970
牌堆id:52 1003
牌堆id:53 1011
可以通过贝塞尔公式计算标准差方式,评估分布均匀性,计算值越趋近于0说明分布越均匀:

(1)最理想的情况下,在54000次洗牌后,每个牌堆次序id中id为0的卡牌应出现1000次,即其算数平均值;

(2)使用贝塞尔公式计算标准差

其中n取54,Xi为牌堆54个id中id为0的卡牌出现次数的值,计算结果为:34.0216

(3)计算标准差平均值

计算结果为:4.63,即可理解为误差为4.63

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值