程序模拟洗扑克牌(算法)

前一段时间找实习,腾讯面试中一轮面试官被问到这个题目,我回答了以下解法中的第一种,太搓了。直接遭面试官歧视了,回来搜了搜,发现一种更好的解法(以下解法中的另外一种)。今天偶尔发现解法2事实上有毛病。于是改进了。有了算法3和算法4.

前提:一副扑克牌有54张。因此我们能够一个整型数组array[54]或者map来存储。"A"用0~3,"2"用4~7,"3"用8~11......"K"用48~51,小鬼用52,大鬼用53表示。关于花色,我们能够这样表示:4i表示红桃,4i+1表示黑桃。4i+2表示梅花。4i+3表示方块(0<=i<=12)。这种话。就退化成一个数组问题。而不关心其表示形式。从而达到逻辑与表现形式脱离。

解法1:随机产生0~53的乱数并将之存入数组中,后来产生的乱数存入数组前必须先检查数组中是否已有反复的数字,假设有这个数字就不存入。在又一次产生下一个数。

	//使用hashmap。目的是为了加快查找速度,hashmap中的key即为扑克牌序列
	//产生随机数,若该随机数在hashmap有。则一直生成随机数,直到没有为止
	public int [] Create_1(){
		HashMap<Integer,Integer> hashmap=new HashMap<Integer,Integer>();
		int [] array =new int [54];
		Random rand;
		int num;

		for(int i=0;i<cardNum;i++){
			rand=new Random(System.currentTimeMillis());
			num=rand.nextInt(cardNum-1);

			while(hashmap.containsKey(num)){
				rand=new Random(System.currentTimeMillis());
				num=rand.nextInt(cardNum-1);
			}
			hashmap.put(num, i);
			array[i]=num;
		}

		return array;
	}

分析:随着产生的随机数的数目的添加,可以正确生成下一个随机数的概率在下降,比方说能正确生成第一个数的概率是54/54,能正确生成第二个数的概率降为53/54,能正确生成第i个数的概率变为(55-i)/54,能正确生成最后一个数的概率是1/54,这种话就须要远不止54次了。

解法2:将数组先依序由0到53填入,然后使用一个回圈走訪数组。并随机产生0~53的乱数。将产生的乱数当作索引取出数组值,并与眼下数组走訪到的值相交换。

	//依靠交换数组中的两个值
	public int [] Create_2(){
		int [] array=new int [cardNum];
		Random rand;
		int num;

		for(int i=0;i<array.length;i++){
			array[i]=i;
		}

		for(int i=0;i<array.length;i++){
			rand=new Random(System.currentTimeMillis());
			num=rand.nextInt(cardNum-1);
			array[num]=i;
			array[i]=num;
		}

		return array;
	}
	

分析:算法2的确比算法1快非常多。并且看起来像是正确的。事实上生成的某个序列在全部序列中并非等可能的。

终于可能的序列有n!

种,而在交换的过程中。有n^n种,这样当n>2时。n^n/(n!)并不为一个整数。因此生成序列的概率并不相等。

算法3:在算法2的基础上改进,我们仅仅交换当前须要交换的和后面没有交换的。

	//依靠交换数组中的两个值
	// 仅仅交换i和i后面的
	public int [] Create_3(){
		int [] array=new int [cardNum];
		Random rand;
		int num;

		for(int i=0;i<array.length;i++){
			array[i]=i;
		}

		for(int i=0;i<array.length;i++){
			rand=new Random(System.currentTimeMillis());
			num=i+rand.nextInt(cardNum-i);
			array[num]=i;
			array[i]=num;
		}

		return array;
	}

分析:该算法是正确,并且效率比較高。实际上我们能够使用库函数。从而达到更高的效率。

算法4:使用库函数,效率更高。

	public int [] Create_4(){
		int [] array=new int [cardNum];
		for(int i=0;i<array.length;i++){
			array[i]=i;
		}

		Collections.shuffle(Arrays.asList(array));
		return array;
	}

总结:以上的四种想法是一步步的改进与提炼,随着时间的增长。人才会慢慢成长吧。

时间: 2024-10-22 09:07:04

程序模拟洗扑克牌(算法)的相关文章

C程序模拟实现银行家算法

上周又做操作系统实验,题目是用程序模拟实现银行家算法,写了半天还真有点晕,主要是因为想尽可能符合课本上的描述,所以写出来的程序就比较恶心了,好了,银行家算法就不多说了,不了解的可以先看一下百度百科上的描述,分段上代码吧.完整代码包下载地址:http://files.cnblogs.com/pianoid/Banker.rar 首先要定义一些结构体,为了符合课本描述,我只定义了一个结构体: typedef struct { int A; int B; int C;}RESOURCE; 结构体里面的

编程序模拟银行家算法

一.编程序模拟银行家算法 1)对用银行家算法来避免死锁的方法有较深入的了解,给出系统的初始状态,模拟避免死锁的动态过程. 2)银行家算法中的数据结构 (1)可利用资源向量Available.这是一个含有m个元素的数组,其中的每一个元素代表一类可利用的资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随该类资源的分配和回收而动态地改变.Available[j]=K,则表示系统中现有Rj类资源K个. (2)最大需求矩阵Max.这是一个n*m的矩阵,它定义了系统中n个进程中的每一个进程对

扑克牌的完美洗牌算法

思路: 递归思想.我们有n张牌,不妨先假设有一个洗牌函数shuffle(....),能完美的洗出n-1张牌 .拿第n张牌来打乱前面n-1的洗牌顺序,从而得到n张牌的最终结果. 代码如下: 1 #include <iostream> 2 #include <cstdlib> 3 using namespace std; 4 5 //随机指定区域内的数 6 int MyRand(int low, int high) 7 { 8 return low + rand() % (high -

洗牌算法Fisher-Yates以及C语言随机数的产生

前些天在蘑菇街的面试中碰到一道洗牌的算法题,拿出来和大家分享一下! 原题是:54张有序的牌,如何无序的发给3个人? 这个题是运用经典的洗牌算法完成.首先介绍一种经典的洗牌算法--Fisher-Yates.现在大家在网上看到,大多是Fisher-Yates算法的变形.将本来O(n2),简化到了O(n).代码如下: #include<stdio.h> #include <stdlib.h> void func(char *, int); void main() { char a[7]

【转】完美洗牌算法

转自:https://yq.aliyun.com/articles/3575 题目 有个长度为2n的数组{a1,a2,a3,…,an,b1,b2,b3,…,bn},希望排序后{a1,b1,a2,b2,….,an,bn},请考虑有无时间复杂度o(n),空间复杂度0(1)的解法. 来源 2013年UC的校招笔试题 思路一 第①步.确定b1的位置,即让b1跟它前面的a2,a3,a4交换: a1,b1,a2,a3,a4,b2,b3,b4 第②步.接着确定b2的位置,即让b2跟它前面的a3,a4交换: a

C# 洗牌算法

最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精   C#洗牌算法如下: class Program { static void Main(string[] args) { List<string> list = new List<string>(); Init(list); XiPai(list); Print(list); DiPai(list); list.Clear(); } st

洗牌算法与蓄水池抽样

今儿看到了,就在此记录一下吧. 洗牌算法 递归做法:先将1~n-1洗牌,然后取随机数k(0<k<n),并交换n与k,代码很简单: 1 int[] shuffle(int[] cards, int n){ 2 if(n == 1){ 3 return cards; 4 } 5 shuffle(cards, n-1); 6 int k = random(1, n-1); 7 swap(card[k], card[n]); 8 return card; 9 } 也可以转成非递归的: 1 void s

[经典面试题]完美洗牌算法

题目 有个长度为2n的数组{a1,a2,a3,-,an,b1,b2,b3,-,bn},希望排序后{a1,b1,a2,b2,-.,an,bn},请考虑有无时间复杂度o(n),空间复杂度0(1)的解法. 来源 2013年UC的校招笔试题 思路一 第①步.确定b1的位置,即让b1跟它前面的a2,a3,a4交换: a1,b1,a2,a3,a4,b2,b3,b4 第②步.接着确定b2的位置,即让b2跟它前面的a3,a4交换: a1,b1,a2,b2,a3,a4,b3,b4 第③步.b3跟它前面的a4交换位

(笔试题)洗牌算法

题目: 给定N张扑克牌和一个随机函数,设计一个洗牌算法 思路: 假设数组A存的是扑克牌代表的数字,则洗牌的过程就是数组中元素交换的过程. 洗牌是个随机的过程,也是一个排列组合的过程. 假设有N张牌,则其排列组合的可能情况为N!=N*(N-1)*....*2*1. 有两种的随机洗牌函数: 1.A[i]=A[rand()%N] 2.A[i]=A[rand()%(N-i)] 但第一种情况得到的选择可能为N^N,而第二种情况为N! 因此第二种随机算法更符合. 代码: void shuffle(int a