답변:
카드 셔플 링은 직관적으로 작성하기 쉬운 알고리즘이며 그렇게함으로써 완전히 잘못 될 수 있습니다. Wikipedia에서 카드 셔플 링을 올바르게 구현하기위한 좋은 참고 자료가 있습니다 . 내가 여기에 제시하는 것은 해당 페이지에서 다루는 알고리즘의 약간 단순화 된 버전입니다. The modern algorithm .
기본 아이디어는 다음과 같습니다.
한 벌의 카드를 고려하십시오. 이 토론을 위해, 당신은 덱에 몇 장의 카드를 가질 수 있으며, 어떤 순서로든 시작할 수 있습니다.
우리는 덱에서 "포지션"에 대해 이야기 할 것입니다. 여기서 "포지션"은 그 위치에있는 카드보다 덱에 몇 개의 카드가 더 많은가입니다. 예를 들어, 덱 맨 위의 카드는 위치 0에 있고, 그 아래의 카드는 1 번 위치에 있습니다 (맨 위의 카드보다 1 카드가 높기 때문에). 카드는 51 장의 카드가 덱의 카드보다 높기 때문에 51 번 위치에 있습니다.
이제 우리는 갑판에서 각 위치를 한 번에 하나씩 바닥에서 시작하여 위쪽으로 진행하는 것을 고려합니다.
각 위치마다 해당 위치 또는 번호가 낮은 위치 에있는 카드 중 하나를 무작위로 선택합니다. 합니다 (데크의 상단이 0이고 덱의 맨 아래에서 작업하고 있음을 기억하십시오. 각 위치마다 해당 위치 이상에서 모든 카드를 효과적으로 집어 들고 해당 카드 중 하나를 무작위로 선택합니다.
무작위 선택을하면, 현재 고려중인 위치에서 무작위로 선택한 카드로 카드를 교환합니다. 해당 위치에있는 카드를 무작위로 선택한 경우 스왑이 수행되지 않습니다.
스왑 후 (또는 스왑하지 않은 경우, 이미 고려중인 위치에있는 카드를 무작위로 선택한 경우), 갑판에서 다음 위치로 이동하여 계속합니다.
의사 코드와 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)를 내뿜는 암호화 알고리즘을 만들거나 찾아야합니다. 데크에서 다음 카드를 가져 오려면 0에서 시작하는 색인 변수가 있어야합니다. 색인을 암호화하고 색인을 증가시키고 암호에서 나온 값을보십시오. 값이> = 52이면 값을 무시하고 새 숫자를 생성합니다 (물론 인덱스를 다시 증가시킵니다). 0-63을 암호화하면 0-63이 다른 순서로 출력되므로 결과는> = 52로 나오는 값을 무시하므로 0-51을 사용하고 0-51을 내뿜는 알고리즘을 갖습니다.
데크를 다시 섞으려면 색인을 다시 0으로 설정하고 암호화 키 (셔플 시드)를 변경하십시오.
알고리즘은 암호화 품질 일 필요는 없습니다 (그리고 계산 비용이 많이 드는 원인이되어서는 안됩니다!). 이와 같은 사용자 지정 크기의 암호화 알고리즘을 제시하는 좋은 방법 중 하나는 필요에 따라 크기와 품질을 사용자 지정할 수있는 feistel 네트워크를 사용하는 것입니다. feistel 네트워크의 라운드 기능의 경우 murmurhash3과 같은 것이 좋습니다. 빠르고 눈사태 효과가 좋기 때문에 셔플이 무작위로 잘 나타납니다.
더 자세한 정보 및 소스 코드는 내 블로그 게시물을 확인하십시오. http://blog.demofox.org/2013/07/06/fast-lightweight-random-shuffle-functionality-fixed/
자바 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;
}
}
단순히 파이썬에서와 같이 itertools와 같은 함수를 사용하십시오. Java에서 동일한 기능의 이름을 모릅니다 . ". http://code.google.com/p/neoitertools/ "
"카드"라는 개체의 모든 순열을 찾으십시오.