Java 所有可能的对

Java 所有可能的对,java,math,combinations,probability,Java,Math,Combinations,Probability,给定一组从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]

给定一组从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>());

那么:我应该如何实现这个问题来消除重复项呢。如何改进实现以加快处理速度?

要更正此问题,请替换if条件:比较
,而不是
=。
这不会大大提高性能,但至少应该像您预期的那样工作。

您可以做两件事:

1)坏事: 您可以继续使用当前方法,但在添加对时,您需要:

a) 确保每一对始终以某种顺序排列,例如始终
[smallerNumber,biggerNumber]

b) 保持所有对列表的排序

然后,当您完成计算时,删除重复项将变得微不足道。这是一个糟糕的方法,因为它将非常缓慢

2)您可以使用不同的算法: 我将在这里作出两个假设:

  • 这是一套,所以我们没有复制品
  • 它们可以很容易地进行分类,即从1到5,我们知道它将是1,2,3,4,5(但它也适用于案例1,3,5,6)
基本上我们会有一个递归算法(像你的一样)。输入为“数字”->有序(升序)列表,输出为一组成对列表。让我们再次调用这个方法compute(列出数字)

->输入列表“数字”有1或2个元素时的基本情况。在这种情况下,返回一个集合,该集合包含一个列表,该列表包含一个“对”,其中包含该1个元素或两个元素。例如,numbers=[2,3]或numbers=[2],然后返回一个集合,其中包含一个列表,该列表包含一个(2,3)或(3)对


->对于列表“numbers”中包含第一个元素的每一对数字(即,在数字的第一级递归中=[1,2,3,4,5],它将是[1,2]、[1,3]、[1,4]、[1,5]、[1,6]),调用
设置您有实际问题吗。谢谢指点。成对的顺序重要吗?
[1 2][3 4]
[3 4][1 2]
相同吗?没关系。两种解决方案都是相同的,即“一组从1到n的数字”。你的意思是没有重复的数字吗?i、 e.1到5始终是1,2,3,4,5,而不是1,1,3,4,5?这实际上消除了一些重复项,但我仍然面临例如:[01][23]对[23][01]你几乎是对的!当我们失去排列时,有一个问题。由于奇异元素始终位于数组的末尾,因此不可能将元素的前半部分设置为这些奇异元素。例如,对于1,2,3,4,5,永远不会出现1单独存在的情况([1])。我会介绍一些有意义的东西,你说得对!但是在这种情况下,首先对整个输入数组运行它,例如(1,2,3,4,5),它将给出result1,然后再次对没有第一个元素的数组(2,3,4,5)运行它,并将(1)添加到该集合的每个列表中,这将给出result2并合并result1和result2,这难道还不够吗?它仍然只生成必要的列表。只有当(偶数)计算(数字)或其他连接(计算(数字),计算(数字。不首先))时,您才需要一个开关,所以事实上-不是真的。。。我已经指出了1是单数元素的情况,但是对于n=5,我们也可以得到[2]单数(其中一种情况是[3]单独存在)。我添加了额外的标志,它确定给定路径中是否已经使用了单个对象,但这会使某些结果再次重复。正如您提到的,删除重复项不是一个好方法。有趣的是,我发现,添加一个数字使计数变为偶数并不会改变结果的计数。因此,将n对齐为偶数并最终忽略这个增加的数字可能是个好主意。嗯,你确定吗?我可以理解为什么[1]是单个元素的结果不会出现(例如,[(2,3),(4,5),(1)],比如[1,2,3,4,5],但我认为我的算法会生成所有可能性,其中[2]、[3]、[4]、[5]是单个元素。你能给我举一个仍然被忽略的例子吗?这样在你的例子中就很容易看到了。请看第一点:“在结果中的每个列表中添加(1,2),该列表将给出[(1,2)(3,4)(5)],[(1,2)(3,5)(4)]”。对于(1,2),我们在这里遗漏了(3)单独存在的情况->(1,2)(3)(4,5)。这个数字越大(离1越远),它出现的频率就越高。例如:(5)在每种情况下单独出现,但(1)没有出现。(示例中的第二个案例缺失(2))
[0 1] [2] 
[0 2] [1] 
[1 0] [2] 
[1 2] [0] 
[2 0] [1] 
[2 1] [0]
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)] ]
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;
    }
}
compute(numbers).addAll( firstElementSingle(numbers) )
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;
}