如何为纸牌游戏洗牌?


Answers:


21

卡洗牌是一种算法,它很容易直观地编写,并且这样做会完全出错。对于在Wikipedia上正确实现卡片改组有很好的参考。我在这里介绍的是该页面上“现代算法”下算法的一个非常简化的版本。

这是简单的英语基本概念:

考虑一副纸牌。在本次讨论中,您可以在牌组中放置任意数量的卡,并且它们可以以任何顺序开始。

我们将要讨论牌组中的“位置”,其中“位置”是指牌组中的卡数高于该位置中的牌数。例如,卡组顶部的卡位于位置0,卡组下方的卡位于位置1(因为比卡高1卡-顶部卡),而标准52卡组的卡底部卡片在位置51,因为51张卡片比卡组中的卡片高。

现在,我们考虑甲板上的每个位置,一次从底部开始,一直到顶部。

对于每个位置,我们随机选择位于该位置或编号较低位置的卡片之一(请记住,牌组的顶部为0,我们从牌组的底部开始向上移动,因此对于每个位置,您实际上是在该位置及其上方拾取所有纸牌,并随机选择其中一张纸牌)。

进行随机选择后,我们会将当前正在考虑的位置的卡与我们随机选择的卡交换。如果我们随机选择已经在该位置的卡,则不会执行任何交换。

交换(或不交换,如果我们随机选择了已经在考虑的位置的卡)之后,我们继续进入牌组中的下一个位置并继续。

用伪代码,其中n是卡组中的卡数,而a是代表卡组的数组,该算法如下所示:

for each i in [n .. 1] do
     j  random integer in [ 0 .. i ]
     exchange a[j] and a[i]

1
该算法在此处也可以很好地看到:bost.ocks.org/mike/algorithms/#shuffling
Felsir 2015年

9

首先,定义要洗牌的所有卡的序列:

List<Card> shuffled = new ArrayList<Card>();
shuffled.addAll(allCards);

然后,您遍历序列中的每个位置并随机分配一张卡片。

Random random = new Random();
for (int i = shuffled.size() - 1; i >= 0; i--) {
    int j = random.nextInt(i + 1);

    /* swap cards i,j */
    Card card = shuffled.get(i);
    shuffled.set(i, shuffled.get(j));
    shufflet.set(j, card);
}

现在shuffled是所有卡片的随机序列。


3
这被称为Knuth的洗牌:en.wikipedia.org/wiki/Knuth_shuffle
krolth

2

我想提一下,并提及“格式保留加密”作为在游戏中改组卡片的方法。

从本质上讲,您所拥有的是一种加密算法,该算法接受0到51的值,以及一个密钥(随机种子)并吐出0到51的值。由于定义上是可逆的,这意味着任何2个输入数字都不能加密为相同的输出编号,这意味着如果您将0到51加密,则会以不同的顺序获得0到51的输出。换句话说,您已经洗牌,甚至不需要进行任何实际的洗牌。

在这种情况下,您必须制定或找到一个采用6位并吐出6位(0-63)的加密算法。为了从卡组中抽取下一张卡片,您将有一个从零开始的索引变量,您需要加密该索引,增加索引并查看从密码中得出的值。如果该值> = 52,则忽略它并生成一个新数字(当然,再增加一次索引)。由于加密0-63将导致以0-63作为输出,因此以不同的顺序,您只是忽略了> = 52的任何值,因此您的算法接受0-51并吐出0-51。

要重新洗牌,请将索引重新设置为零并更改加密密钥(洗牌种子)。

您的算法不必具有加密质量(也不应具有加密质量,因为这在计算上会非常昂贵!)。提出像这样的自定义大小的加密算法的一种非常好的方法是使用feistel网络,该网络可以让您根据需要自定义大小和质量。对于feistel网络的圆形函数,我建议使用murmurhash3之类的东西,因为它速度快且具有良好的雪崩效果,这会使随机播放显得很好随机。

查看我的博客文章,以获取更多详细信息和源代码:http : //blog.demofox.org/2013/07/06/fast-lightweight-random-shuffle-functionality-fixed/


当URL不可避免地脱离Internet表面时,此短语的当前答案无济于事。考虑详细说明链接文章的要点,以便答案可以独立存在。
Lars Viklund

1
好点拉斯(Lars),已更新了更多信息,以便读者至少可以找到有关使用格式保留加密的洗牌解决方案的所有特定组件的更多信息。谢谢!
艾伦·沃尔夫

1

Java 1.5的枚举教程必须实现的一副牌一个有趣的方式,建立了甲板,洗牌和处理。使用enum和都非常简单Collections

public class Card {
    public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX,
        SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE }

    public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES }

    private final Rank rank;
    private final Suit suit;
    private Card(Rank rank, Suit suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public Rank rank() { return rank; }
    public Suit suit() { return suit; }
    public String toString() { return rank + " of " + suit; }

    private static final List<Card> protoDeck = new ArrayList<Card>();

    // Initialize prototype deck
    static {
        for (Suit suit : Suit.values())
            for (Rank rank : Rank.values())
                protoDeck.add(new Card(rank, suit));
    }

    public static ArrayList<Card> newDeck() {
        return new ArrayList<Card>(protoDeck); // Return copy of prototype deck
    }
}

和班级来管理甲板。

public class Deal {
    public static void main(String args[]) {
        int numHands = Integer.parseInt(args[0]);
        int cardsPerHand = Integer.parseInt(args[1]);
        List<Card> deck  = Card.newDeck();
        Collections.shuffle(deck);
        for (int i=0; i < numHands; i++)
            System.out.println(deal(deck, cardsPerHand));
    }

    public static ArrayList<Card> deal(List<Card> deck, int n) {
         int deckSize = deck.size();
         List<Card> handView = deck.subList(deckSize-n, deckSize);
         ArrayList<Card> hand = new ArrayList<Card>(handView);
         handView.clear();
         return hand;
     }
}


-2
    ArrayList deckCards = new ArrayList<Card>();
    //add your cards to the deck
    deckCards.add(card1);
    deckCards.add(card2);
    deckCards.add(card3);
    ....
    //shuffle the array list
    Collections.shuffle(deckCards);

1
不鼓励仅使用代码的答案。
SurvivalMachine
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.