回答:
カードシャッフルは、直感的に簡単に書くことができ、そうすることで完全に間違ってしまうアルゴリズムです。ウィキペディアにカードシャッフルを正しく実装するための良いリファレンスがあります。ここで紹介しているのは、「現代のアルゴリズム」のページに記載されているアルゴリズムの非常に単純化されたバージョンです。
基本的な考え方は次のとおりです。
一組のカードを考えてみましょう。このディスカッションでは、デッキに任意の数のカードを置くことができ、それらは任意の順序で開始できます。
ここで、デッキの「位置」について説明します。「位置」とは、その位置のカードよりもデッキ内で高いカードの数です。たとえば、デッキの上部のカードは位置0にあり、その下のカードは位置1にあり(それよりも1枚上のカードがあるため-上部のカード)、標準の52カードのデッキでは、下部にあります51枚のカードがデッキのそれよりも高いため、カードは位置51にあります。
ここで、デッキの各位置を1つずつ、下から始めて上に向かって検討します。
各位置について、その位置またはより低い番号の位置にあるカードの1つをランダムに選択します(デッキの上部は0であり、デッキの下部から上に向かって作業していることに注意してください。ポジションごとに、そのポジション以上のすべてのカードを効果的にピックアップし、それらのカードの1つをランダムにピックアップします)。
ランダムに選択したら、現在検討している位置のカードを、ランダムに選択したカードと交換します。すでにその位置にあったカードをランダムに選択した場合、スワップは実行されません。
交換した後(または交換していない場合、既に検討中の位置にあったカードをランダムに選択した場合)、デッキの次の位置に移動して続行します。
擬似コードでは、nはデッキ内のカードの数であり、aはデッキを表す配列であるため、アルゴリズムは次のようになります。
for each i in [n .. 1] do
j ← random integer in [ 0 .. i ]
exchange a[j] and a[i]
最初に、シャッフルするすべてのカードのシーケンスを定義します。
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
、あなたのすべてのカードのランダムなシーケンスです。
ゲームでカードをシャッフルする方法として、「フォーマット保存暗号化」に言及して言及したいと思います。
基本的には、値0〜51とキー(シャッフルシード)を取り、値0〜51を出力する暗号化アルゴリズムです。暗号化は定義により可逆であるため、2つの入力番号は暗号化できません。同じ出力番号。つまり、0〜51を暗号化した場合、異なる順序で0〜51が出力されます。つまり、シャッフルがあり、実際のシャッフルを行う必要さえありません。
この場合、6ビットを取り込み、6ビット(0-63)を吐き出す暗号化アルゴリズムを作成または見つける必要があります。デッキから次のカードを引くには、ゼロから始まるインデックス変数を使用します。そのインデックスを暗号化し、インデックスをインクリメントし、暗号から得られた値を調べます。値が> = 52の場合、それを無視して新しい数値を生成します(そしてもちろんインデックスを再び増やします)。0-63を暗号化すると、異なる順序で0-63が出力されるため、52以上の値を無視するだけなので、0-51を取り込んで0-51を吐き出すアルゴリズムが得られます。
デッキをシャッフルするには、インデックスをゼロに戻し、暗号化キーを変更します(シードをシャッフルします)。
アルゴリズムは暗号品質である必要はありません(そうであるべきではありません、原因は計算コストが高くなります!)。このようなカスタムサイズの暗号化アルゴリズムを考え出すための本当に良い方法の1つは、feistelネットワークを使用することです。これにより、ニーズに応じてサイズと品質をカスタマイズできます。feistelネットワークのラウンド関数には、murmurhash3のようなものをお勧めします。これは高速であり、雪崩効果があり、シャッフルがランダムに表示されるためです。
さらに詳しい情報とソースコードについては、私のブログ投稿をご覧ください:http : //blog.demofox.org/2013/07/06/fast-lightweight-random-shuffle-functionality-fixed/
Javaの1.5列挙型のチュートリアルでは、デッキを構築シャッフルと対処、カードのデッキを実装するための興味深い方法があります。すべての非常に簡単な使用してenum
sであり、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;
}
}
Pythonのitertoolsのような関数を使用するだけです。Javaの同じ関数の名前が「。http://code.google.com/p/neoitertools/」であることに気付いていません
「カード」と呼ばれるオブジェクトのすべての順列を見つけます
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);