【发布时间】:2018-11-06 15:22:25
【问题描述】:
拥有一个 x 唯一 Integers 的 ArrayList,我需要在 y 大小为 z 的 ArrayLists 之间随机分配它们。请记住:
-
x y z是变量值。 - 数字不能在结果数组上重复。
- 结果列表不能包含相同的数字! (订购它们必须不同)
- 如果您计算结果数组中的出现次数,则原始数组中的每个数字的使用次数必须尽可能与其他次数相同。
- 必须使用原始数组的所有数字,没有一个 它们不能不被使用。
- 如果可能,必须在 Java 7 中工作。不是 100% 强制性的,但...
- 产生的组合将用于类似于彩票的事情,因此它们不能太连续并且必须非常随机。它们还将按从最小值到最大值排序。
- 最初我尝试生成所有可能的组合,目的是获得所需的数量,但这不可行,因为如果您选择 40 个数字和 11 个组合的高值,则有数百万个并且 CPU 会卡住计算很多时间,所以我尝试开发一个更简单的算法而不计算所有组合(我在下面发布代码)。
当你有一个由 8 个元素组成的数组的原点并且你想要输出 3 个大小为 6 的数组时,一个示例是这样的:
原始数组列表:[1, 2, 3, 4, 5, 6, 7, 8]
结果输出:[7, 5, 3, 6, 4, 8], [7, 5, 1, 8, 2, 3], [8, 1, 2, 3, 4, 6]
我开发了一个在 cmets 上解释的算法。首先,我创建了一个包含总位置数的数组,并计算每个数字必须重复多少次才能填充输出数组。然后我用重复必要次数的每个数字填充数组,如果数组不满(因为当我除以得到placesByNumber时,我四舍五入为整数)我用原始集合中的随机数填充它的数字。之后我打乱了数字,最后我填充了结果数组,记住我不能在每个结果数组中重复数字。
问题来了,有时,我会遇到最后一个数组没有完全填充的情况,因为洗牌后的numbersGroup 变量的最后一个数字包含在最后一个数组中。
这是一个失败的例子:
原始数组列表:[1, 2, 3, 4, 5, 6, 7, 8]
用于填充结果数组的随机数字组:
[8, 2, 4, 4, 5, 7, 2, 3, 8, 2, 1, 5, 7, 1, 6, 3, 6, 1]
结果数组:(第三个没有 6 个元素,因为 6 和 1 是 包含在上面)
[[8, 2, 4, 5, 7, 3], [4, 2, 8, 1, 5, 7], [2, 1, 6, 3]]
我发现了一些非常丑陋的方法来解决它,但效率非常低,我正在尝试找到一种更好、更有效的算法来实现这一点。
这是我的源代码:
public static List<List<Integer>> getOptimizedCombinations(List<Integer> numbers, int numbersPerCombination, int desiredCombinations){
List<List<Integer>> result = new ArrayList<>();
//calculate total places and how many places correspond to each number.
int totalPlaces = numbersPerCombination * desiredCombinations;
int placesByNumber = totalPlaces / numbers.size();
//instantiating array with the total number of places
Integer[] numbersGroup = new Integer[totalPlaces];
//filling the array with the numbers, now we know how many times a number must be inside the array,
//so we put the numbers. First we do it in order, later we will shuffle the array.
int pos = 0;
for (int n : numbers) {
for (int i=0; i<placesByNumber; i++) {
numbersGroup[pos] = n;
pos++;
}
}
//if there are places for fill, we fill it with random numbers. This can be possible because when we divide the total places between the
//numbers size, it can give a decimal as a result, and we round it to lower binary number without decimals, so it is possible to
//have non filled places.
if (pos<totalPlaces) {
while(pos<totalPlaces) {
numbersGroup[pos] = numbers.get(getRandom(0, numbers.size()));
pos++;
}
}
shuffleArray(numbersGroup);
//we instantiate the arraylists
for (int i=0; i<desiredCombinations; i++) {
result.add(new ArrayList<Integer>());
}
//filling the arraylists with the suffled numbers
for (int i=0; i<numbersGroup.length; i++) {
for (int j=0; j<result.size(); j++) {
//if the combination doesn't have the number and the combination is not full, we add the number
if (!result.get(j).contains(numbersGroup[i]) && result.get(j).size()<numbersPerCombination) {
result.get(j).add(numbersGroup[i]);
break;
}
}
}
return result;
}
static void shuffleArray(Integer[] ar){
Random rnd = new Random();
for (int i = ar.length - 1; i > 0; i--)
{
int index = rnd.nextInt(i + 1);
// Simple swap
int a = ar[index];
ar[index] = ar[i];
ar[i] = a;
}
}
public static int getRandom(int min, int max) {
return (int)(Math.random() * max + min);
}
这样称呼的:
ArrayList<Integer> numbers = new ArrayList<Integer>() {{
add(1);
add(2);
add(3);
add(4);
add(5);
add(6);
add(7);
add(8);
}};
getOptimizedCombinations(numbers, 6, 3);
【问题讨论】:
-
为什么不直接打乱原始数组,将其截断为所需的大小,然后重复此过程所需的次数?
-
@MitchelPaulin 你是什么意思?你能用一个样本解释一下吗?我也忘了告诉如果你计算结果数组中的出现次数,原始数组的每个数字都必须与其他数字或多或少地使用相同的次数
-
您的工作样本在每个输出数组中包含 8、3,那么您的失败样本不能包含 6、1。为什么?
-
@NullPointerException 好的,现在我看到了问题所在。我假设每次洗牌都彼此无关
-
@zlakad 数字不能在结果数组上重复,第三个数组已经有 6,1
标签: java arrays algorithm math statistics