ti-enxame.com

Escolha vários elementos aleatórios de uma lista em Java

Então diga que eu tenho

List<String> teamList = new LinkedList<String>()
teamList.add("team1");
teamList.add("team2");
teamList.add("team3");
teamList.add("team4");
teamList.add("team5");
teamList.add("team6");

Existe uma maneira simples de escolher ... digamos 3 dos 6 elementos nesta lista de maneira aleatória sem escolher o mesmo elemento duas vezes (ou mais vezes)?

23
Yokhen

Tente o seguinte:

public static List<String> pickNRandom(List<String> lst, int n) {
    List<String> copy = new LinkedList<String>(lst);
    Collections.shuffle(copy);
    return copy.subList(0, n);
}

Estou assumindo que não há elementos repetidos na lista de entrada, também tomo a precaução de embaralhar uma cópia para deixar a lista original imperturbável. É assim chamado:

List<String> randomPicks = pickNRandom(teamList, 3);
56
Óscar López

Crie um conjunto de entradas e coloque números aleatórios entre 0 e o comprimento da lista menos um em um loop, enquanto o tamanho do conjunto não é igual ao número desejado de elementos aleatórios. Percorra o conjunto e escolha os elementos da lista, conforme indicado pelos números no conjunto. Dessa forma, manteria sua lista original intacta.

6
dasblinkenlight

A abordagem shuffle é a mais idiomática: depois disso, os primeiros elementos K são exatamente o que você precisa.

Se K for muito menor que o comprimento da lista, convém ser mais rápido. Nesse caso, percorra a lista, trocando aleatoriamente o elemento atual com ele ou com qualquer um dos elementos após ele. Após o elemento K-ésimo, pare e retorne o prefixo K: ele já estará perfeitamente embaralhado e você não precisará se preocupar com o resto da lista.

(obviamente, você gostaria de usar ArrayList aqui)

5
alf

Você também pode usar amostragem de reservatório .

Tem a vantagem de que você não precisa conhecer o tamanho da lista de fontes com antecedência (por exemplo, se você receber um Iterable em vez de um List.) Também é eficiente mesmo quando o lista de fontes não tem acesso aleatório, como o LinkedList no seu exemplo.

4
finnw

Aqui está uma maneira de fazê-lo usando os fluxos Java, sem precisar criar uma cópia da lista original ou embaralhá-la:

public static List<String> pickRandom(List<String> list, int n) {
    if (n > list.size()) {
        throw new IllegalArgumentException("not enough elements");
    }
    Random random = new Random();
    return IntStream
            .generate(() -> random.nextInt(list.size()))
            .distinct()
            .limit(n)
            .mapToObj(list::get)
            .collect(Collectors.toList());
}

Nota: Pode se tornar ineficiente quando n está muito perto do tamanho da lista para listas enormes.

3
Helder Pereira

Todas as boas idéias, mas baralhar, são caras. O método mais eficiente (IMO) seria fazer um loop controlado por contagem e escolher um int aleatório entre 0 e n; onde n inicialmente é igual ao comprimento da sua lista.

Em cada iteração do loop, você troca o item selecionado pelo item n-1 na lista e diminui n por um. Dessa forma, você evita escolher o mesmo elemento duas vezes e não precisa manter uma lista separada de itens selecionados.

2
Mr1159pm

Usar

Collections.shuffle(teamList);

para randomizar a lista e, em seguida, remova equipes uma de cada vez da lista via teamList.remove(0);

Por exemplo:

  List<String> teamList = new LinkedList<String>();
  teamList.add("team1");
  teamList.add("team2");
  teamList.add("team3");
  teamList.add("team4");
  teamList.add("team5");
  teamList.add("team6");

  Java.util.Collections.shuffle(teamList);

  String[] chosen3 = new String[3];
  for (int i = 0; i < chosen3.length && teamList.size() > 0; i++) {
     chosen3[i] = teamList.remove(0);
  }
2
Hovercraft Full Of Eels
int[] getRandoms(int[] ranges, int n, int[] excepts) {
    int min = ranges[0];
    int max = ranges[1];

    int[] results = new int[n];
    for (int i = 0; i < n; i++) {
        int randomValue = new Random().nextInt(max - min + 1) + min;
        if (ArrayUtils.contains(results, randomValue) || ArrayUtils.contains(excepts, randomValue)) {
            i--;
        } else {
            results[i] = randomValue;
        }
    }
    return results;
}

classe util

public static class ArrayUtils {

    public static boolean contains(int[] array, int elem) {
        return getArrayIndex(array, elem) != -1;
    }

    /** Return the index of {@code needle} in the {@code array}, or else {@code -1} */
    public static int getArrayIndex(int[] array, int needle) {
        if (array == null) {
            return -1;
        }
        for (int i = 0; i < array.length; ++i) {
            if (array[i] == needle) {
                return i;
            }
        }
        return -1;
    }
}

usando

int[] randomPositions = getRandoms(new int[]{0,list.size()-1}, 3, new int[]{0,1});

aleatoriamente 3 itens em sua lista, exceto o item 0 e o item 1

0
Phan Van Linh