Идея
Чтобы сделать это, нам нужно
z < x
(длина каждого нового списка <длина входного списка) или мы не можем заполнить новые списки без дубликатов. </li>
y·z
(количество списков · длина списков) должно быть кратным x
, иначе некоторые числа должны появляться чаще, чем другие.
Идея состоит в том, чтобы
- Перемешать список ввода.
- Повторите список ввода так, чтобы мы получили
y·z
числа. Это можно сделать без повторения списка. Хитрость заключается в использовании оператора по модулю %
.
- Равномерно разделить повторный список ввода на
y
списки длины z
.
- Перемешать каждый новый список.
Input
1 2 3 4 5 6 7 8
Перемешать
3 5 8 6 7 2 4 1
Повторите
3 5 8 6 7 2 4 1 3 5 8 6 7 2 4 1 3 5 8 6 7 2 4 1
Split
3 5 8 6 7 2 4 1 3 5 8 6 7 2 4 1 3 5 8 6 7 2 4 1
Перемешать каждый список
7 3 5 6 2 8 1 3 4 8 6 5 3 4 1 5 7 2 2 7 4 1 8 6
Перемешать список списков
1 3 4 8 6 5 2 7 4 1 8 6 7 3 5 6 2 8 3 4 1 5 7 2
Программа
Эта программа должна работать на Java 7. Однако я тестировал ее только на Java 11.
import java.util.*;
public class Shuffle {
public static void main(String[] args) {
System.out.println(splitShuffle(Arrays.asList(1,2,3,4,5,6,7,8), 6, 3));
}
public static List<List<Integer>> splitShuffle(
List<Integer> input, int newLength, int listCount) {
assert newLength * listCount % input.size() == 0 : "Cannot distribute numbers evenly";
input = new ArrayList<>(input);
Collections.shuffle(input);
List<List<Integer>> result = new ArrayList<>(listCount);
for (int i = 0; i < listCount; ++i) {
result.add(rotatingCopy(input, i * newLength, newLength));
}
Collections.shuffle(result);
return result;
}
private static List<Integer> rotatingCopy(List<Integer> input, int startIndex, int length) {
assert length < input.size() : "copy would have to contain duplicates";
List<Integer> copy = new ArrayList<>(length);
for (int i = 0; i < length; ++i) {
copy.add(input.get((startIndex + i) % input.size()));
}
Collections.shuffle(copy);
return copy;
}
}
Пример выходов
Я запускал программу четыре раза. Вот его выводы. Каждая строка - это один прогон программы.
[[2, 6, 7, 8, 1, 3], [4, 3, 7, 5, 2, 8], [1, 2, 6, 5, 4, 8]]
[[2, 7, 5, 4, 6, 1], [4, 7, 2, 6, 8, 3], [1, 3, 5, 8, 6, 4]]
[[4, 1, 2, 5, 6, 3], [5, 3, 8, 4, 6, 7], [5, 1, 2, 7, 3, 8]]
[[5, 3, 8, 2, 6, 4], [1, 7, 4, 5, 6, 3], [1, 6, 2, 8, 7, 4]]
Как мы видим, каждое число появляется ровно два раза, и каждый подсписок имеет только уникальные числа.
Полнота
По крайней мере для списка ввода [1, 2, 3]
и y=3, z=2
Я мог бы убедиться, что все возможные 48 выходов могут быть сгенерированы. Я знаю, что есть 48 комбинаций с помощью следующей команды bash:
printf %s\\n {1..3}{1..3},{1..3}{1..3},{1..3}{1..3} | grep -Pv '(\d)\1' |
tr -d , | awk '{print $1, gsub(1,""), gsub(2,""), gsub(3,"")}' |
grep -F ' 2 2 2' | cut -d' ' -f1 | sort -u | wc -l