How to iteratively generate k elements subsets from a set of size n in java?

后端 未结 7 1800
忘掉有多难
忘掉有多难 2020-11-29 09:14

I\'m working on a puzzle that involves analyzing all size k subsets and figuring out which one is optimal. I wrote a solution that works when the number of subsets is small,

7条回答
  •  野性不改
    2020-11-29 09:44

    I've had the same problem today, of generating all k-sized subsets of a n-sized set.

    I had a recursive algorithm, written in Haskell, but the problem required that I wrote a new version in Java.
    In Java, I thought I'd probably have to use memoization to optimize recursion. Turns out, I found a way to do it iteratively. I was inspired by this image, from Wikipedia, on the article about Combinations.

    Method to calculate all k-sized subsets:

    public static int[][] combinations(int k, int[] set) {
        // binomial(N, K)
        int c = (int) binomial(set.length, k);
        // where all sets are stored
        int[][] res = new int[c][Math.max(0, k)];
        // the k indexes (from set) where the red squares are
        // see image above
        int[] ind = k < 0 ? null : new int[k];
        // initialize red squares
        for (int i = 0; i < k; ++i) { ind[i] = i; }
        // for every combination
        for (int i = 0; i < c; ++i) {
            // get its elements (red square indexes)
            for (int j = 0; j < k; ++j) {
                res[i][j] = set[ind[j]];
            }
            // update red squares, starting by the last
            int x = ind.length - 1;
            boolean loop;
            do {
                loop = false;
                // move to next
                ind[x] = ind[x] + 1;
                // if crossing boundaries, move previous
                if (ind[x] > set.length - (k - x)) {
                    --x;
                    loop = x >= 0;
                } else {
                    // update every following square
                    for (int x1 = x + 1; x1 < ind.length; ++x1) {
                        ind[x1] = ind[x1 - 1] + 1;
                    }
                }
            } while (loop);
        }
        return res;
    }
    

    Method for the binomial:
    (Adapted from Python example, from Wikipedia)

    private static long binomial(int n, int k) {
        if (k < 0 || k > n) return 0;
        if (k > n - k) {    // take advantage of symmetry
            k = n - k;
        }
        long c = 1;
        for (int i = 1; i < k+1; ++i) {
            c = c * (n - (k - i));
            c = c / i;
        }
        return c;
    }
    

    Of course, combinations will always have the problem of space, as they likely explode.
    In the context of my own problem, the maximum possible is about 2,000,000 subsets. My machine calculated this in 1032 milliseconds.

提交回复
热议问题