Exemplo Livro Java Deitel 6ª Edição "Embaralhar e distribuir cartas"

Caros participantes do Fórum,
Solicito ajuda para explicar como funciona o exemplo da figura 7.10, página 214 do livro, o método de embaralha cartas.
Já fiz várias simulações e não consigo visualizar como a classe consegue embaralhar as cartas e ao final mostrar todas as cartas sem repetir.
Nas simulações que faço o método shuffle, da classe DeckOfCards, embaralha as cartas, porém apresenta um resultado com 52 cartas, mas contém cartas repetidas, contudo ao rodar o programa ele apresenta as 52 cartas embaralhadas e sem repetição.
Será que alguém pode explicar como isso funciona.
Seguem as classes abaixo:

[code]// Fig. 7.9: Card.java
// Classe Card representa uma carta de baralho.

public class Card
{
private String face; // face da carta (“Ace”, “Deuce”, …)
private String suit; // naipe da carta (“Hearts”, “Diamonds”, …)

// construtor de dois argumentos inicializa face e naipe da carta
public Card( String cardFace, String cardSuit )
{
face = cardFace; // inicializa face da carta
suit = cardSuit; // inicializa naipe da carta
} // fim do construtor Card de dois argumentos

// retorna representação String de Card
public String toString()
{
return face + " of " + suit;
} // fim do método toString
} // fim da classe Card[/code]

[code]// Fig. 7.10: DeckOfCards.java
// classe DeckOfCards representa um baralho.
import java.util.Random;

public class DeckOfCards
{
private Card deck[]; // array de objetos Card
private int currentCard; // índice do próximo Card a ser distribuído
private final int NUMBER_OF_CARDS = 52; // número constante de Cards
private Random randomNumbers; // gerador de número aleatório

// construtor preenche baralho de cartas
public DeckOfCards()
{
String faces[] = { “Ace”, “Deuce”, “Three”, “Four”, “Five”, “Six”,
“Seven”, “Eight”, “Nine”, “Ten”, “Jack”, “Queen”, “King” };
String suits[] = { “Hearts”, “Diamonds”, “Clubs”, “Spades” };

  deck = new Card[ NUMBER_OF_CARDS ]; // cria array de objetos Card
  currentCard = 0; // configura currentCard então o primeiro Card distribuído é deck[ 0 ]
  randomNumbers = new Random(); // cria gerador de número aleatório

  // preenche baralho com objetos Card
  for ( int count = 0; count < deck.length; count++ )        
     deck[ count ] =                                         
        new Card( faces[ count % 13 ], suits[ count / 13 ] );

} // fim do construtor DeckOfCards

// embaralha as cartas com um algoritmo de uma passagem
public void shuffle()
{
// depois de embaralhar, a distribuição deve iniciar em deck[ 0 ] novamente
currentCard = 0; // reinicializa currentCard

  // para cada Card, seleciona outro Card aleatório e os compara
  for ( int first = 0; first < deck.length; first++ )
  {
     // seleciona um número aleatório entre 0 e 51
     int second =  randomNumbers.nextInt( NUMBER_OF_CARDS );

     // compara Card atual com Card aleatoriamente selecionado
     Card temp = deck[ first ];     
     deck[ first ] = deck[ second ];
     deck[ second ] = temp;         
  } // for final

} // fim do método shuffle

// distribui um Card
public Card dealCard()
{
// determina se ainda há Cards a serem distribuídos
if (currentCard < deck.length)
return deck[ currentCard++ ]; // retorna Card atual no array
else
return null; // retorna nulo p/ indicar que todos os Cards foram distribuídos
} // fim do método dealCard
} // fim da classe DeckOfCards[/code]

[code]// Fig. 7.11: DeckOfCardsTest.java
// Aplicativo de embaralhar e distribuir cartas.

public class DeckOfCardsTest
{
// executa o aplicativo
public static void main( String args[] )
{
DeckOfCards myDeckOfCards = new DeckOfCards();
myDeckOfCards.shuffle(); // coloca Cards em ordem aleatória

  // imprime todas as 52 cartas na ordem em que elas são distribuídas
  for ( int i = 0; i < 13; i++ )
  {
     // distribui e imprime 4 Cards
     System.out.printf( "%-20s%-20s%-20s%-20s\n",
        myDeckOfCards.dealCard(), myDeckOfCards.dealCard(), 
        myDeckOfCards.dealCard(), myDeckOfCards.dealCard() );
  } // for final

} // fim de main
} // fim da classe DeckOfCardsTest[/code]

Comentado:

// Para cada carta no baralho
for ( int first = 0; first < deck.length; first++ )
{
   // Seleciona segunda uma carta aleatoriamente para trocar de lugar com a primeira
   int second =  randomNumbers.nextInt( NUMBER_OF_CARDS );

   // A primeira carta vai para o lugar da segunda, e a segunda vem para o lugar da primeira.
   Card temp = deck[ first ];     
   deck[ first ] = deck[ second ];
   deck[ second ] = temp;         
}

Caro renrutal,
Eu sei que esse é o método de embaralhar as cartas e entendo como funciona, só que esse método sózinho embaralha e apresenta 52 combinações com algumas repetições, ou seja, se você mandar imprimir a chamada a esse método ele vai mostrar 52 combinações com alguns valores repetidos.
O método dealCard é que faz a verificação se ainda existem cartas que não foram distribuídas, ou seja, acredito que aqui esteja a resposta para a minha pergunta “como a classe trabalha para utilizar os dois métodos e apresentar as 52 cartas sem haver reptição”.
Já fiz várias simulações, mas não consigo entender como realmente a classe DeckOfCards consegue fazer a distribuição das cartas aleatóriamente, garantindo que todas as 52 cartas serão distribuídas e sem repetição.

Não sei do que você está falando.

Acabei de rodar o exemplo aqui e chequei todos os resultados, não há repetições.

Sim, ao rodar o programa ele apresenta o resultado sem repetições, como é o esperado. E essa é a minha dúvida, como o programa faz isso.

Se você inserir uma linha para mostrar o resultado de cada passagem no método ele apresenta um resultado com alguns dados repetidos.

Para exemplificar, roda o programa modificado abaixo:

[code]// Fig. 7.9: Card.java
// Classe Card representa uma carta de baralho.

public class Card
{
private String face; // face da carta (“Ace”, “Deuce”, …)
private String suit; // naipe da carta (“Hearts”, “Diamonds”, …)

// construtor de dois argumentos inicializa face e naipe da carta
public Card( String cardFace, String cardSuit )
{
face = cardFace; // inicializa face da carta
suit = cardSuit; // inicializa naipe da carta
} // fim do construtor Card de dois argumentos

// retorna representação String de Card
public String toString()
{
return face + " de " + suit; //MODIFIQUEI
} // fim do método toString
} // fim da classe Card[/code]

[code]// Fig. 7.10: DeckOfCards.java
// classe DeckOfCards representa um baralho.
import java.util.Random;

public class DeckOfCards
{
private Card deck[]; // array de objetos Card
private int currentCard; // índice do próximo Card a ser distribuído
private final int NUMBER_OF_CARDS = 52; // número constante de Cards
private Random randomNumbers; // gerador de número aleatório

// construtor preenche baralho de cartas
public DeckOfCards()
{
//MODIFIQUEI PARA FICAR MAIS FÁCIL DE VERIFICAR
String faces[] = { “0”, “1”, “2”, “3”, “4”, “5”,
“6”, “7”, “8”, “9”, “10”, “11”, “12” };
String suits[] = { “A”, “B”, “C”, “D” };

  deck = new Card[ NUMBER_OF_CARDS ]; // cria array de objetos Card
  currentCard = 0; // configura currentCard então o primeiro Card distribuído é deck[ 0 ]
  randomNumbers = new Random(); // cria gerador de número aleatório

  // preenche baralho com objetos Card
  for ( int count = 0; count < deck.length; count++ )        
     deck[ count ] =                                         
        new Card( faces[ count % 13 ], suits[ count / 13 ] );

} // fim do construtor DeckOfCards

// embaralha as cartas com um algoritmo de uma passagem
public void shuffle()
{
// depois de embaralhar, a distribuição deve iniciar em deck[ 0 ] novamente
currentCard = 0; // reinicializa currentCard

  // para cada Card, seleciona outro Card aleatório e os compara
  for ( int first = 0; first < deck.length; first++ )
  {
     // seleciona um número aleatório entre 0 e 51
     int second =  randomNumbers.nextInt( NUMBER_OF_CARDS );

     // compara Card atual com Card aleatoriamente selecionado
     Card temp = deck[ first ];     
     deck[ first ] = deck[ second ];
     deck[ second ] = temp;     
//MODIFIQUEI  PARA VERIFICAR A SAÍDA	
  System.out.printf( "%-20s%-20s%-20s%-20s\n",
        myDeckOfCards.dealCard(), myDeckOfCards.dealCard(), 
        myDeckOfCards.dealCard(), myDeckOfCards.dealCard() );

  } // for final

} // fim do método shuffle

// distribui um Card
public Card dealCard()
{
// determina se ainda há Cards a serem distribuídos
if (currentCard < deck.length)
return deck[ currentCard++ ]; // retorna Card atual no array
else
return null; // retorna nulo p/ indicar que todos os Cards foram distribuídos
} // fim do método dealCard
} // fim da classe DeckOfCards[/code]

[code]// Fig. 7.11: DeckOfCardsTest.java
// Aplicativo de embaralhar e distribuir cartas.

public class DeckOfCardsTest
{
// executa o aplicativo
public static void main( String args[] )
{
DeckOfCards myDeckOfCards = new DeckOfCards();
myDeckOfCards.shuffle(); // coloca Cards em ordem aleatória

  // imprime todas as 52 cartas na ordem em que elas são distribuídas

// imprime todas as 52 cartas na ordem em que elas são distribuídas
// for ( int i = 0; i < 13; i++ )
// {
// // distribui e imprime 4 Cards
// System.out.printf( “%-20s%-20s%-20s%-20s\n”,
// myDeckOfCards.dealCard(), myDeckOfCards.dealCard(),
// myDeckOfCards.dealCard(), myDeckOfCards.dealCard() );
// } // for final } // fim de main
} // fim da classe DeckOfCardsTest[/code]

Ele mostra resultados repetidos justamente por que você está imprimindo enquanto ele embaralha.

Imagine que você esteja embaralhando as cartas com as suas mãos, e de repende você pára e checa as primeiras 10 cartas de cima, devolve as cartas para o baralho e continua embaralhando, pára e checa as 10 cartas de cima novamente.

Pergunta: As chances de você ver cartas repetidas são maiores que zero? Por quê?

Por que uma mesma carta que foi trocada de lugar na primeira embaralhagem pode voltar ao seu lugar original, ou lugares próximos do original na segunda embaralhagem e embaralhagems subsequentes.

O espaço amostral é grande, mas a chance disso acontecer nunca será zero.

Caro renrutal,
Fico agradecido pela tua atenção, por estar tentando me ajudar e, por esse motivo, vou continuar abusando de tua boa vontade.

Eu sou um iniciante em Java e por esse motivo ainda tenho algumas dúvidas básicas.

Na classe DeckOfCardsTest que chama os métodos é criado uma instancia myDeckOfCards que chama o método void shuffle, nesse momento o método coloca 52 cartas distribuídas aleatóriamente, porém com cartas repetidas.

Na hora em que faz o loop “for” é chamado o método dealCard e nesse momento é que aparece a minha dúvida, pois é aqui que as cartas repetidas são descartadas e é apresentada a sequência das 52 cartas distribuídas aleatóriamente e sem repetição.

Como isso é feito? Eu não consigo visualizar como o método está sendo utilizado. Será que você pode me ajudar?

Esse é o ponto, shuffle embaralha as cartas direitinho, sem repetições, exatamente como mandado. O código está certo. Você tem olhar o problema inteiro, com todas as suas 52 cartas, e não fazendo amostragem de 4 cartas, esse é o erro.

Se quiser uma prova que ele não está repetindo:

// Para cada carta no baralho
for ( int first = 0; first < deck.length; first++ )
{
   // Seleciona segunda uma carta aleatoriamente para trocar de lugar com a primeira
   int second =  randomNumbers.nextInt( NUMBER_OF_CARDS );

   // A primeira carta vai para o lugar da segunda, e a segunda vem para o lugar da primeira.
   Card temp = deck[ first ];     
   deck[ first ] = deck[ second ];
   deck[ second ] = temp;

   Set<String> set = new HashSet<String>(52); // Set vai cuidar para que não hajam cartas repetidas
   
   // Coloca todas as cartas do set com dealCard()
   for (int i = 0; i < 52; i++)
      set.add(this.dealCard().toString()); // Comparações baseadas no toString() do Card

   // Se não tiverem 52 cartas únicas, lança a exceção.
   if (set.size() != 52)
      throw new IllegalStateException("Baralho deveria ter 52 cartas não-repetidas.");
   else 
      System.out.println("Baralho tem 52 cartas não-repetidas.");

   currentCard = 0; // reinicializa currentCard
}

Substitua o código no lugar certo e rode. Não deverá nunca lançar uma exceção.

Ele imprime que tem 52 cartas não repetidas 52 vezes.

:smiley: :thumbup: Valeu.
Forte abraço.

e depois de embaralhadas essas cartas, existe um jeito de mostrá-las divididas em 2 massos(sem repetiçao)?
como se dividissimos um baralho inteiro em duas pessoas!!

Uai, corta o baralho em dois.

Distribui metade pra um cara, metade pro outro.