给定一组数字,从1到n,我需要对所有可能对的所有组进行建模。
换句话说,一个绘制的集合包含所有数字。数字配对。如果计数是奇数 - 允许一个带一个数字的条目(事实上它是必需的)。集合需要是唯一的,即对[1,2]与[2,1]相同(编辑:和解:[1,2] [3,4]与[3,4] [1]相同, 2])。
例如,当n等于5时,可以创建以下集合:
[1,2] [3,4] [5]
[1,2] [3,5] [4]
[1,2] [4,5] [3]
[1,3] [2,4] [5]
[1,3] [2,5] [4]
[1,3] [4,5] [2]
....
我几乎能够为解决方案建模,但唯一性约束对我来说很难实现。
此外 - 我觉得我的解决方案缺乏性能。我现在认为问题空间很大,但对我来说,即使是n = 12,计算也需要5分钟以上。
public void compute(HashSet<Number> numbers, ArrayList<Pair> pairs) {
if (numbers.size() <= 1) {
print(pairs);
} else {
for (Number number1 : numbers) {
for (Number number2 : numbers) {
if (number1 != number2) {
Set<Number> possibleNumbers = new HashSet<Number>(numbers);
List<Pair> allPairs = new ArrayList<Pair>(pairs);
possibleNumbers.remove(number1);
possibleNumbers.remove(number2);
allPairs.add(new Pair(number1, number2));
compute(possibleNumbers, allPairs);
}
}
}
}
}
compute(numbers, new ArrayList<Pair>());
对于n = 3,我得到了一整套解决方案:
[0 1] [2]
[0 2] [1]
[1 0] [2]
[1 2] [0]
[2 0] [1]
[2 1] [0]
所以:我应该如何实现问题以摆脱重复。如何改进实施以加快处理速度?
答案 0 :(得分:2)
您可以继续使用当前的方法,但在添加对时需要:
a)确保每对总是处于某种顺序,例如总是[smallerNumber, biggerNumber]
b)保持allPairs列表排序
然后,当你完成计算时,删除重复项将是微不足道的。这是一个糟糕的方法,因为它会很慢。
我将在这里作出两个假设:
基本上我们会有一个递归算法(和你的一样)。输入将是“数字” - &gt;有序(升序)列表,输出将是一组对的列表。让我们再次调用此方法compute(列表编号)。
- &GT;输入列表“数字”具有1或2个元素时的基本情况。在这种情况下,返回一个包含一个列表的集合,该列表包含一个“对”,其中包含1个元素或两个元素。即数字= [2,3]或数字= [2]然后返回一个包含一个列表的集合,其中包含一个“对”(2,3)或(3)。
- &GT;对于包含第一个元素的列表“数字”中的每对数字(即在数字递归的第一级= [1,2,3,4,5],它将是[1,2],[1,3] ],[1,4],[1,5],[1,6])致电Set<List<Pair> result = compute(numbers.minus(currentPair))
。迭代该集合并在每个元素的开头添加当前对。
示例:
result = empty
numbers = [1,2,3,4,5]
first = (1,2), compute([3,4,5])
first = (3,4), compute([5])
return (5)
result = result + [(3,4)(5)]
first = (3,5) compute([4])
return (4)
result = result + [(3,5),(4)] (a this point result is [ [(3,4)(5)], [(3,5)(4)] ]
add (1,2) to each list in result which gives us [ [(1,2)(3,4)(5)], [(1,2)(3,5)(4)] ]
first = (1,3), compute([2,4,5])
first = (2,4), compute([5])
return (5)
result = result + [(2,4)(5)]
first = (2,5) compute([4])
return (4)
result = result + [(2,5),(4)] (a this point result is [ [(2,4)(5)], [(2,5)(4)] ]
add (1,3) to each list in result which gives us [ [(1,3)(2,4)(5)], [(1,3)(2,5)(4)] ]
at this point we have:
[ [(1,2)(3,4)(5)], [(1,2)(3,5)(4)], [(1,3)(2,4)(5)], [(1,3)(2,5)(4)] ]
继续(1,4),(1,5)和(1,6),你就完成了。您不必以(2,3)开头并执行计算([1,4,5])这样做,因为这会导致重复。
此算法也应该适用于偶数集。
没有测试过,没有证据但是看起来不错,应该很容易编码,如果它有效,那么它应该非常快,因为它只会进行必要的计算。
在代码中(我在这里编写了整个代码,因此它完全没有经过测试,可能包含编译和逻辑错误!):
public Set<List<Pair>> compute(List<Integer> numbers) {
if(numbers.size() < 3) {
// Base case
List<Pair> list = new ArrayList<>();
list.add(new Pair(numbers));
Set<List<Pair>> result = new HashSet<>();
result.add(list);
return result;
} else {
Set<List<Pair>> result = new HashSet<ArrayList<>>();
// We take each pair that contains the 1st element
for(int i = 1; i < numbers.size(); i++) {
Pair first = new Pair(numbers.get(0), numbers.get(i));
// This is the input for next level of recursion
// Our numbers list w/o the current pair
List<Integers> nextStep = new ArrayList<>(numbers);
nextStep.remove(i);
nextStep.remove(0);
Set<List<Pair>> intermediate = null;
if(nextStep.size() % 2 == 0) {
intermediate = compute(nextStep);
} else {
intermediate = compute(numbers).addAll( firstElementSingle(numbers) ),compute( nextStep );
}
for(List<Pair> list : intermediate ) {
// We add the current pair at the beginning
list.add(0, first);
}
result.addAll(intermediate);
}
return result;
}
}
如果我错过了什么,我很好奇,所以我很期待你的反馈: - )
<强> @EDIT:强>
正如@yusuf指出的那样,当输入是奇数列表时,这会遗漏一些排列。如果[1]是单个元素,它将会遗漏所有结果。
但如果我在这种情况下没有弄错(奇数个元素),这应该有效:
compute(numbers).addAll( firstElementSingle(numbers) )
其中firstElementSingle是:
private Set<List<Integer>> firstElementSingle(List<Integer> numbers) {
Set<List<Integer>> result compute(numbers.subList(1,numbers.size()) );
for(List<Integer> list : result) {
list.add(numbers.get(0));
}
return result;
}
仍然只会产生必要的结果。
答案 1 :(得分:0)
您正在处理每对两次,要更正此项,请更换if条件:比较<
或>
而不是!=
。
这不会提高性能,但至少应该按预期工作。