Java顺序表实现扑克牌Fisher-Yates 洗牌算法

📚 目录

1. 定义牌的类

首先根据扑克牌的规则定义出扑克牌的类:定义一张扑克牌的属性:花色,面值

java 复制代码
//一张扑克牌
public class Card {
    //花色:红桃、黑桃、方块、梅花
    public String suit;
    //面值:A,2...10,j,Q,k
    public String rank;

    @Override
    public String toString() {
        return String.format("[%s %s]",suit,rank);//格式化进行输出花色+面值
    }
}

我们需要重写toString方法,方便后续的打印。
[🔙 返回目录](#🔙 返回目录)


2. 初始化牌

我们买到一副扑克牌,这副扑克牌的顺序都是一样的,相同的在一起,没有经过任何的 洗牌操作,我们需要对其进行模拟。

我们使用顺序表进行模拟:

对每一张排进行构造然后添加到顺序表当中-》完成初始化。

我们先要定义一一个刚买到牌的方法 和 牌的属性 :buyDeck

通过双重循环进行链表的添加,循环生成 4 种花色 × 13 种点数,刚好 52 张牌。。

java 复制代码
package demo1;
import java.util.ArrayList;
import java.util.List;
public class PokerGame {
    //牌的面值:初始化的时候我们不希望这个顺序能够修改,可按照个人习惯进行初始化
    public static final String[] rank = {"A", "2", "3", "4", "5",
                                        "6", "7", "8", "9", "10", "J", "Q", "K"};
    //牌的花色:
    public static final String[] suit = {"♥红桃", "♠黑桃", "♦方块", "♣梅花"};


    public static List<Card> buyDeck() {
        //定义一个顺序表:大小为52
        ArrayList<Card> pokerList = new ArrayList<>(52);
        for (int i = 0; i < suit.length; i++) {
            for (int j = 0; j < rank.length; j++) {
                String getSuit = suit[i];
                String getRank = rank[j];
                Card card = new Card();
                card.rank = getRank;
                card.suit = getSuit;
                pokerList.add(card);
            }
        }
        return pokerList;//返回初始化完成的扑克牌。
    }
		//通过打印看我们的扑克牌是否完成了初始化:
    public static void main(String[] args) {
        List<Card> cards = buyDeck();
        for (int i = 0; i < cards.size(); i++) {
            System.out.print(cards.get(i));
            if((i+1)%13==0) {
                System.out.println();
            }
        }
    }
}

此时,我们能观察到,扑克牌成功进行初始化了。

[🔙 返回目录](#🔙 返回目录)


3. Fisher-Yates 洗牌算法

进行初始化之后,我们需要对扑克牌进行洗牌。
**  我们用经典的「Fisher-Yates 洗牌算法」,遍历牌组,把当前牌和随机位置的牌交换,实现打乱顺序。**

Fisher-Yates 洗牌算法是目前最经典、最高效、最公平的洗牌算法,专门用于把一个数组 / 列表随机打乱,保证每张牌出现在任何位置的概率完全相等。

首先我们需要生成0-52之间的随机数:我们可以使用Random来进行生成。

洗牌方法:

java 复制代码
    //洗牌:shuffle
    public static void shuffle(List<Card> pokerList) {
        Random random = new Random();
        // 从后往前遍历,避免重复交换,保证每张牌只被打乱一次
        for (int i = pokerList.size()-1; i >0 ; i--) {
            // 生成0~i的随机索引,保证每张牌被抽到的概率相同
            int a = random.nextInt(i+1);//生成范围是[0,53)Java中大部分范围都是左闭右开
            swap(pokerList,i,a);
        }
        System.out.println("洗牌完成");
    }

为什么从后面遍历?

为了避免重复打乱,已经确定好位置的牌不再参与交换,逻辑更清晰、效率更高,

交换方法:让i下标与生成的随机数进行交换。

java 复制代码
    public static void swap(List<Card> pokerList,int i ,int a) {
        Card card = pokerList.get(i);
        pokerList.set(i,pokerList.get(a));
        pokerList.set(a,card);
    }

结果:每次结果都不相同说明我们的洗牌成功了。


[🔙 返回目录](#🔙 返回目录)


4. 抓牌

**  我们实现一个给 3 个玩家各发 5 张牌的逻辑,从洗好的牌组里按顺序取牌。
  抓牌的时候都是从上面开始抓牌:抓一张,牌库里面就少一张牌:我们知道remove方法实现了重载,删除后返回删除掉的值。
  每一个人一次抓5张牌:**

首先我们得先检查牌是否够发,然后通过循环让每一个用户抓牌:

java 复制代码
    public static List<List<Card>> dealCards (List<Card> 
    							pokerList,int playerCount,int cardPerPlayer) {
        List<List<Card>> players = new ArrayList<>();
        if(playerCount*cardPerPlayer>pokerList.size()) {
        	//检查牌的合法性
            System.out.println("牌不够发啦");
            return players;
        }
        //循环让每一个玩家抓牌,一次抓5张
        for (int i = 0; i < playerCount; i++) {
            List<Card> playerCards = new ArrayList<>();
            for (int j = 0; j < cardPerPlayer; j++) {
                //从头部开始抓牌。
                playerCards.add(pokerList.remove(0));//巧妙使用remove特性。
            }
            players.add(playerCards);//每一个人一次次抓到的牌添加进去
        }

        return players;
    }


**  这里用 List<List> 表示多个玩家的牌,本质上是一个 "二维顺序表":外层 List 存所有玩家,内层每个 List 存一个玩家的牌,和二维数组的逻辑是一样的,但比数组更灵活。**

我们可以知道结果:抓一次牌的结果,剩余37张牌。

合集:

java 复制代码
//扑克牌
public class Card {
    //花色:红桃、黑桃、方块、梅花
    public String suit;
    //面值:A,2...10,j,Q,k
    public String rank;

    @Override
    public String toString() {
        return String.format("[%s %s]",suit,rank);
    }
}
java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class PokerGame {
    //牌的顺序:初始化的时候我们不希望这个顺序能够修改,可按照个人习惯进行初始化
    public static final String[] rank = {"A", "2", "3", "4", "5",
                                        "6", "7", "8", "9", "10", "J", "Q", "K"};
    //牌的面值:
    public static final String[] suit = {"♥红桃", "♠黑桃", "♦方块", "♣梅花"};


    public static List<Card> buyDeck() {
        //定义一个顺序表:大小为52
        ArrayList<Card> pokerList = new ArrayList<>(52);
        for (int i = 0; i < suit.length; i++) {
            for (int j = 0; j < rank.length; j++) {
                String getSuit = suit[i];
                String getRank = rank[j];
                Card card = new Card();
                card.rank = getRank;
                card.suit = getSuit;
                pokerList.add(card);
            }
        }
        return pokerList;
    }

    public static void swap(List<Card> pokerList,int i ,int a) {
        Card card = pokerList.get(i);
        pokerList.set(i,pokerList.get(a));
        pokerList.set(a,card);
    }

    //洗牌:shuffle
    public static void shuffle(List<Card> pokerList) {
        Random random = new Random();
        for (int i = pokerList.size()-1; i >0 ; i--) {
            //随机生成0-i的数值:避免重复洗牌
            int a = random.nextInt(i+1);
            swap(pokerList,i,a);
        }
        System.out.println("洗牌完成");
    }

    //抓牌
    public static List<List<Card>> dealCards (List<Card> pokerList
    										,int playerCount,int cardPerPlayer) {
        List<List<Card>> players = new ArrayList<>();
        if(playerCount*cardPerPlayer>pokerList.size()) {
            System.out.println("牌不够发啦");
            return players;
        }
        for (int i = 0; i < playerCount; i++) {
            List<Card> playerCards = new ArrayList<>();
            for (int j = 0; j < cardPerPlayer; j++) {
                //从头部开始抓牌。
                playerCards.add(pokerList.remove(0));
            }
            players.add(playerCards);
        }

        return players;
    }

    public static void main(String[] args) {
        List<Card> cards = buyDeck();
        shuffle(cards);//洗牌
        List<List<Card>> players  = dealCards(cards,3,5);
        for (int i = 0; i < players.size(); i++) {
            System.out.println("玩家" + (i+1) + "的牌:" + players.get(i));
        }
        //剩余排数:
        System.out.println(cards.size());
    }


}

[🔙 返回目录](#🔙 返回目录)


相关推荐
YanDDDeat2 小时前
【Spring】事务注解失效与传播机制
java·后端·spring
guygg882 小时前
极化码(Polar Codes)的MATLAB实现
开发语言·数据结构·matlab
SamDeepThinking2 小时前
学数据结构到底有什么用
java·后端·面试
yuannl102 小时前
数据结构----树
数据结构
Xiu Yan2 小时前
Java 转 C++ 系列:函数模板
java·开发语言·c++
ICscholar2 小时前
推荐系统常用指标NDCG含义及公式
人工智能·深度学习·算法
闲人xyz2 小时前
01|把一次用户请求做成可持续执行的回合:主循环才是 Agent 的骨架
算法·面试
程序员清风2 小时前
独立开发者必看:推荐几个可直接用的开源项目!
java·后端·面试
超级码力6662 小时前
【Latex魔术注解+导言区】Latex魔术注解+导言区分类介绍
算法·数学建模