Java - Mega sena

Pessoal da uma força ae, preciso desenvolver um destes.
Programa em Java (Console ou Swing) para simbolizar o sorteio da Mega-Sena.

  • Os valores devem ser escolhidos automaticamente pelo programa, dentro do intervalo de 1 a 60.
  • Sortear 6 números diferentes
  • Mostrar valores sorteados

achei algo assim , vcs acham q ta bom?

[code]import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class MegaSena {
public static void main(String[] args) {
List urnaDeBolas = new ArrayList(60); //cria uma urna para 60 bolas
for(int i = 1; i<=60; i++)
urnaDeBolas.add(i); //adciona as bolas de 1 ate 60 na urna…

    int[] resultados = new int[6]; //lugar onde guardar os resultados

    Random roleta = new Random(); //cria uma roleta para o sorteio

    for(int i = 0 ; i < 6; i++) {
        Collections.shuffle(urnaDeBolas); //embaralhando as bolas da urna.
        int indexSorteado = roleta.nextInt(urnaDeBolas.size());
        resultados[i] = urnaDeBolas.remove(indexSorteado);
        //remove uma bola da urna, sorteada entre o index 0 e o numero de bolas
        //na urna, esse index não quer dizer quer será o proprio numero
        //e raramente será... ele é somento o indice da bola q será sorteada
        //que foi previamente embaralhada
    }
    System.out.println(Arrays.toString(resultados)); //imprime os resultados
}

}

O código parece funcionar. Se vc for usá-lo mesmo, eu apenas sugeriria fazer a seguinte substituição:

// Isso
List urnaDeBolas = new ArrayList(60);
// Por isso
List<Integer> urnaDeBolas = new ArrayList<>(60);

Fora isso, eu posso estar deixando passar alguma coisa, mas eu acho que criar um array e usar o shuffle parece redundante. Acredito que vc pode reduzir seu código a isso:

import java.util.Arrays;
import java.util.Random;

public class Main {
  public static void main(String... args) {
    final int[] resultados = new int[6];
    final Random roleta = new Random();

    for (int i = 0; i < 6; i++)
      resultados[i] = roleta.nextInt(60) + 1;

    System.out.println(Arrays.toString(resultados));
  }
}

Aquele +1 ali é porque o nextInt() retorna de 0 até 59.

E se vc quiser pode ficar ainda mais simples assim:

import java.util.Arrays;
import java.util.Random;

public class Main {
  public static void main(String... args) {
    int[] resultados = new Random().ints(6, 1, 61).toArray();
    System.out.println(Arrays.toString(resultados));
  }
}

Coloquei 61 ali porque assim ele vai de 1 a 60 certinho.

Quais comentários vc acha pertinente colocar?
Explicar como funciona a array ?
sla!

O problema de fazer assim é que pode acabar gerando números repetidos. Eu rodei umas 10 vezes e tive 3 casos de repetição (claro que por ser aleatório isso varia, mas o ponto é que pode acontecer e nem é tão improvável assim).

Enfim, se a ideia é pegar 6 números aleatórios de uma lista de 60, basta embaralhá-los uma vez e pegar os 6 primeiros.

Caso esteja usando Java 8, pode usar streams:

List<Integer> urnaDeBolas = new ArrayList<>(60); //cria uma urna para 60 bolas
for(int i = 1; i <= 60; i++)
    urnaDeBolas.add(i);

Collections.shuffle(urnaDeBolas);
int[] resultados = urnaDeBolas.subList(0, 6).stream().mapToInt(i -> i).toArray();
System.out.println(Arrays.toString(resultados));

Ou com um loop normal mesmo:

Collections.shuffle(urnaDeBolas);
int[] resultados = new int[6];
for(int i = 0; i < resultados.length; i++)
    resultados[i] = urnaDeBolas.get(i);

Dessa forma você garante que não haverá repetição.

Inclusive, esta ideia de embaralhar e depois pegar os N primeiros elementos é baseada no algoritmo Fisher-Yates.

1 curtida

É verdade, nem tinha pensado em embaralhar só uma vez e pegar os 6 primeiros, parece bem melhor.

E seguindo sua sugestão de usar Stream, poderia IntStream pra gerar os números:

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
  public static void main(String... args) {
    final List<Integer> numbers = IntStream.rangeClosed(1, 60).boxed().collect(Collectors.toList());
    Collections.shuffle(numbers);
    final List<Integer> result = numbers.subList(0, 6);
    System.out.println(result);
  }
}

Ih, não sei. Foca em entender e depois descrever com suas palavras.

Acho mais performático ao invés de embaralhar a lista, escolher uma posição aleatória para remover o número.

Eu discordo. Em um ArrayList, a operação remove tem complexidade O(n), pois há o custo de rearranjar os elementos internamente (da documentação: “Shifts any subsequent elements to the left”). E você vai ter que fazer isso 6 vezes (uma para cada número que for sorteado).

Collections.shuffle também tem complexidade O(n) e muda os elementos de lugar, mas só é feito uma vez (não tem o custo de rearranjar todos os elementos subsequentes 6 vezes).

Claro que performance mesmo só se sabe medindo em situações reais, mas considerando a documentação e dando uma olhada no código fonte do JDK, fazer o shuffle uma vez me parece bem menos custoso que remover 6 vezes. A exceção, claro, é se os 6 últimos elementos forem sorteados, pois remover o último é o único caso em que não é feito o rearranjo interno.

Sem contar que não gosto da ideia de remover os elementos da “urna”, pois e se eu precisar sortear várias vezes? Vou precisar reconstruir a urna com os 60 números todas as vezes? Não sei, ainda me parece melhor apenas embaralhá-la sempre.

E se estamos pensando em performance, nem deveria estar usando List<Integer> e sim um int[], para evitar o unboxing implícito que ocorre ao copiar os elementos para o array de resultados. Mas nem quis entrar nesse mérito, para um exercício essas coisas não costumam fazer diferença. De qualquer forma, é uma discussão interessante… :slight_smile:

1 curtida

Mas você tem um conjunto finito de dados, não tem porque usar ArrayList.
Usa o LinkedList onde a operação remove não precisa rearranjar os elementos, só atualizar o ponteiro entre 2 nós e isso tem custo O(1).

1 curtida

Concordo, mas se o tamanho é fixo, por que usar qualquer lista em vez de usar logo um int[]? Aí embaralha e pega os 6 primeiros1.

Pois como eu já disse, não gosto dessa ideia de remover elementos da urna (se precisar sortear várias vezes, terei que adicionar os valores de volta nela - ou reconstruí-la - todas as vezes). Mas claro que se for rodar apenas uma vez (ou poucas vezes), aí tanto faz, pois a diferença será imperceptível :slight_smile:

E na verdade, mesmo se usar LinkedList, remover de uma posição específica é O(n) sim, pois existe o custo de se navegar até o elemento que está na referida posição (somente a atualização dos ponteiros é O(1), mas para se chegar ao elemento tem que percorrer a lista desde o início). Os únicos casos em que não é necessário percorrer é quando se quer remover o primeiro ou o último, para qualquer outro elemento será preciso percorrer a lista até chegar no índice.


1: a única desvantagem de usar int[] é que você vai ter que embaralhar “na mão”:

public static void embaralha(int arr[]) {
    Random r = new Random();

    for (int i = arr.length - 1; i > 0; i--) {
        int j = r.nextInt(i + 1);
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

int[] urna = new int[60];
for (int i = 0; i < urna.length; i++) {
    urna[i] = i + 1;
}
embaralha(urna);
int[] resultados = new int[6];
for (int i = 0; i < resultados.length; i++) {
    resultados[i] = urna[i];
}
2 curtidas