从数组中获得大小n的所有组合的算法(java)?

uelo1irk  于 2021-07-09  发布在  Java
关注(0)|答案(3)|浏览(319)

**结束。**此问题需要详细的调试信息。它目前不接受答案。
**想改进这个问题吗?**更新问题,使其成为堆栈溢出的主题。

5年前关门了。
改进这个问题
现在我正在尝试编写一个函数,它接受一个数组和一个整数n,并给出每个大小n的组合的列表(也就是int数组的列表)。我可以使用n个嵌套循环来编写它,但这只适用于特定大小的子集。我不知道如何把它推广到任何大小的组合。我想我需要使用递归?
这是3个元素的所有组合的代码,我需要一个任意数量的元素算法。

import java.util.List;
import java.util.ArrayList;

public class combinatorics{
    public static void main(String[] args) {

        List<int[]> list = new ArrayList<int[]>();
        int[] arr = {1,2,3,4,5};
        combinations3(arr,list);
        listToString(list);
    }

    static void combinations3(int[] arr, List<int[]> list){
        for(int i = 0; i<arr.length-2; i++)
            for(int j = i+1; j<arr.length-1; j++)
                for(int k = j+1; k<arr.length; k++)
                    list.add(new int[]{arr[i],arr[j],arr[k]});
    }

    private static void listToString(List<int[]> list){
        for(int i = 0; i<list.size(); i++){ //iterate through list
            for(int j : list.get(i)){ //iterate through array
                System.out.printf("%d ",j);
            }
        System.out.print("\n");
        }
    }
}
ijnw1ujt

ijnw1ujt1#

你完全可以用迭代来实现这一点。
下面是一个解决方案,它计算我们应该创建多少个数组,然后使用数学方法构建它们,以计算源数组中的哪个项应该位于哪个位置:

public static void combinations(int n, int[] arr, List<int[]> list) {
    // Calculate the number of arrays we should create
    int numArrays = (int)Math.pow(arr.length, n);
    // Create each array
    for(int i = 0; i < numArrays; i++) {
        int[] current = new int[n];
        // Calculate the correct item for each position in the array
        for(int j = 0; j < n; j++) {
            // This is the period with which this position changes, i.e.
            // a period of 5 means the value changes every 5th array
            int period = (int) Math.pow(arr.length, n - j - 1);
            // Get the correct item and set it
            int index = i / period % arr.length;
            current[j] = arr[index];
        }
        list.add(current);
    }
}

更新:
这是一个优化的版本,大大减少了呼叫 Math.pow ```
public static void combinations(int n, int[] arr, List<int[]> list) {
// Calculate the number of arrays we should create
int numArrays = (int)Math.pow(arr.length, n);
// Create each array
for(int i = 0; i < numArrays; i++) {
list.add(new int[n]);
}
// Fill up the arrays
for(int j = 0; j < n; j++) {
// This is the period with which this position changes, i.e.
// a period of 5 means the value changes every 5th array
int period = (int) Math.pow(arr.length, n - j - 1);
for(int i = 0; i < numArrays; i++) {
int[] current = list.get(i);
// Get the correct item and set it
int index = i / period % arr.length;
current[j] = arr[index];
}
}
}

u7up0aaq

u7up0aaq2#

如果我正确地理解了你的问题,这篇文章似乎指出了你要做的事情。
引用这篇文章:
方法1(固定元素并重复)
我们创建一个临时数组“data[]”,它逐个存储所有输出。其思想是从数据[]中的第一个索引(index=0)开始,一个接一个地修复该索引中的元素,并对其余索引进行重复。让输入数组为{1,2,3,4,5},r为3。我们首先在数据[]中的索引0处固定1,然后对其余索引重复,然后在索引0处固定2并重复。最后,我们修复3并对剩余的索引进行重复。当数据[]中的元素数等于r(组合的大小)时,我们打印数据[]。
方法2(包括和排除每个元素)
像上面的方法一样,我们创建一个临时数组data[]。这里的思想类似于子集和问题。我们逐一考虑输入数组中的每个元素,并重复两种情况:
元素包含在当前组合中(我们将该元素放在数据[]中,并在数据[]中递增下一个可用索引)
元素被排除在当前组合中(我们不放置元素,也不更改索引)
当数据[]中的元素数等于r(组合大小)时,我们打印它。

axr492tv

axr492tv3#

这是一个研究得很好的生成所有k-子集或k-组合的问题,不需要递归就可以很容易地完成。
这个想法是有一个数组的大小 k 保持输入数组中元素的索引顺序(即 0n - 1 )以递增的顺序(然后可以通过从初始数组中获取这些索引项来创建子集。)因此我们需要生成所有这样的索引序列。
第一个索引序列是 [0, 1, 2, ... , k - 1] ,在第二步它切换到 [0, 1, 2,..., k] ,然后到 [0, 1, 2, ... k + 1] 等等。最后一个可能的顺序是 [n - k, n - k + 1, ..., n - 1] .
在每一步中,算法都会查找最接近最终项的值,该值可以递增,递增并填充到该项的右边。
为了说明,请考虑 n = 7 以及 k = 3 . 第一个索引序列是 [0, 1, 2] ,那么 [0, 1, 3] 等等。。。在某个时候我们 [0, 5, 6] :

[0, 5, 6] <-- scan from the end: "6" cannot be incremented, "5" also, but "0" can be
[1, ?, ?] <-- "0" -> "1"
[1, 2, 3] <-- fill up remaining elements

next iteration:

[1, 2, 3] <-- "3" can be incremented
[1, 2, 4] <-- "3" -> "4"

因此, [0, 5, 6] 后面是 [1, 2, 3] ,然后去 [1, 2, 4] 等。
代码:

int[] input = {10, 20, 30, 40, 50};    // input array
int k = 3;                             // sequence length   

List<int[]> subsets = new ArrayList<>();

int[] s = new int[k];                  // here we'll keep indices 
                                       // pointing to elements in input array

if (k <= input.length) {
    // first index sequence: 0, 1, 2, ...
    for (int i = 0; (s[i] = i) < k - 1; i++);  
    subsets.add(getSubset(input, s));
    for(;;) {
        int i;
        // find position of item that can be incremented
        for (i = k - 1; i >= 0 && s[i] == input.length - k + i; i--); 
        if (i < 0) {
            break;
        }
        s[i]++;                    // increment this item
        for (++i; i < k; i++) {    // fill up remaining items
            s[i] = s[i - 1] + 1; 
        }
        subsets.add(getSubset(input, s));
    }
}

// generate actual subset by index sequence
int[] getSubset(int[] input, int[] subset) {
    int[] result = new int[subset.length]; 
    for (int i = 0; i < subset.length; i++) 
        result[i] = input[subset[i]];
    return result;
}

相关问题