Выберите несколько случайных элементов из списка в Java

Так сказать, у меня

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

Есть ли простой способ выбора... скажите 3 из 6 элементов в этом списке рандомизированным образом, не выбирая один и тот же элемент дважды (или более раз)?

Ответы

Ответ 1

Попробуйте следующее:

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);
}

Я предполагаю, что во входном списке нет повторяющихся элементов, также я принимаю меры предосторожности при перетасовке копии, чтобы оставить исходный список без изменений. Он назывался так:

List<String> randomPicks = pickNRandom(teamList, 3);

Ответ 2

Создайте набор int и поместите случайные числа между 0 и длиной списка минус один в него в цикле, а размер набора не равен желаемому количеству случайных элементов. Пройдите через набор и выберите элементы списка, как указано числами в наборе. Таким образом, ваш исходный список не будет сохранен.

Ответ 3

Подход shuffle является самым идиоматическим: после этого первые элементы K - это именно то, что вам нужно.

Если K намного меньше длины списка, вы можете захотеть быстрее. В этом случае перебирайте список, случайно обменивая текущий элемент с собой или с любым из элементов после него. После K-го элемента остановите и верните K-префикс: он будет уже полностью перетасован, и вам не нужно заботиться о остальной части списка.

(очевидно, вы хотели бы использовать ArrayList здесь)

Ответ 4

Вы также можете использовать выборку коллектора.

Преимущество состоит в том, что вам не нужно заранее знать размер исходного списка (например, если вам предоставляется Iterable вместо List.) Также он эффективен, даже если исходный список не произвольный доступ, например LinkedList в вашем примере.

Ответ 5

Используйте

Collections.shuffle(teamList);

чтобы рандомизировать список, затем удалите команды по одному из списка через teamList.remove(0);

Например:

  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);
  }

Ответ 6

Все хорошие идеи, но перетасовка - это дорого. Более эффективный метод (IMO) будет выполнять цикл, контролируемый счетчиком, и выбор случайного int между 0 и n; где n изначально равно длине вашего списка.

В каждой итерации цикла вы меняете выбранный элемент с элементом в n-1 в списке и уменьшаете n на единицу. Таким образом, вы избегаете выбора одного и того же элемента два раза и не должны содержать отдельный список выбранных элементов.

Ответ 7

Вот как это сделать, используя потоки Java, без необходимости создавать копию исходного списка или перетасовывать его:

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());
}

Примечание. Это может стать неэффективным, если n слишком близко к размеру списка для огромных списков.

Ответ 8

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;
}

Использовать класс

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;
    }
}

используя

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

он будет случайным 3 пункта в вашем списке, кроме пункта 0 и пункта 1