Выбрать несколько случайных элементов из списка в 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 элементов в этом списке в случайном порядке, не выбирая один и тот же элемент дважды (или более раз)?

8 ответов

Решение

Попробуй это:

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

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

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

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

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

Вот способ сделать это, используя потоки 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 слишком близко к размеру списка для огромных списков.

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

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

Использование

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

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

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

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

Другие вопросы по тегам