简体   繁体   中英

Create all possible ways of putting n users into k groups

Given n users (u_1, u_2,..., u_n) and k groups (g_1, g_2, ..., g_k), create all possible combinations of all groups. basically, in the end, each combination is a Map<Integer,Integer>, where the first Integer is user ID, and the second Integer is group ID.For example, [(u_1,g_1), (u_2,g_1)....,(u_n, g_1)] is one possible combination.

There will be k^n combinations.

I searched and saw similar problems, but they do have some extra conditions that do not apply for my problem. In my case, there is no limit in each group, and there is no evenness or uniform distribution.

Can you suggest a quick way to do this in Java?

Thanks

My tries so far: I tried to create a for loop for each possibility for each user, but I face the problem that I cannot define the number of for loops.

So I switched to recursion, but stuck at creating parameters for inside calls to the functions. Still working on it though.

Please, also note that this is not "n choose k". "n choose k" is when all users are identical, but here users are obviously not identical.


OK. I created a solution for this. Basically, it's a dynamic programming problem. Assume you have created a List of Maps (combinations) for j users and k locations. TO create for j+1 users and k locations, need 2 loop: for each Map, for each i=1 to k, Map.put(user_j+1, k)). Is is both recursive and iterative. Recursive because you need to pass the old maps to the new iterations. That's it.

The traditional solution to these kinds of problems is using recursion: If there are n = 0 users the only grouping possible is the empty group. Otherwise, take out the first user and generate the solution for the other n-1 users. Using the solution for the subproblem, generate the final solutions by assigning the first user to each of the k possible groups.

In code:

import java.util.*;

class Grouping {
    public static void main(String[] args) {
        List<?> groups = grouping(Arrays.asList(1,2,3), Arrays.asList(4,5,6,7));
        System.out.println(groups.size());
        System.out.println(groups);
    }

    static List<Map<Integer,Integer>> grouping(List<Integer> users, List<Integer> groups) {
        if (users.isEmpty()) {
            Map<Integer,Integer> empty = Collections.emptyMap();
            return Collections.singletonList(empty);
        } else {
            Integer user = users.get(0);
            List<Map<Integer,Integer>> subs = grouping(users.subList(1,users.size()), groups);

            List<Map<Integer,Integer>> solutions = new ArrayList<>();
            for (Integer group: groups) {
                for (Map<Integer,Integer> sub : subs) {
                    Map<Integer,Integer> m = new HashMap<>(sub);
                    m.put(user, group);
                    solutions.add(m);
                }
            }
            return solutions;
        }
    }
}

Ok, here is simple idea.
Lets first assume that each user_id is in {0, ... n-1}, and group_id is in {0, ... k-1}, we can map this numbers back to real ids later.

Now, what you basically want to do is to iterate thru all n-digit numbers in Base k numeral system, ie where each digit 0 <= base < k.
So, you start with number is 0000...00 (n-zeroes) and you end with kkkk....kk (n digits of nominal k ). For each such number the position indicates user_id and digit value is this user's group_id.

So, the main workhorse you need to implement is the class Combination representing such n-digits sequence(try ArrayList or simple int[] array ), with increment() operation implemented correctly (I imagine recursion is the best way to do this implementation). May be you could also have method isMaxReached() to check if all digits are of value k .

Then you would have a single loop :

Combination combination = new Combination(); // initialized with n -zeroes

while (!combination.isMaxReached()) {
    combination.increment();
}

Please let me know if you would need more details on implementation.

Hope that helps.

It looks like we have the following:

  1. n different numbers (users)
  2. k sets (groups)

Objective is:

  • Find all the k-tuples with k sets S_k, such that for all i,j in 1..k the pairwise intersection of S_i and S_j is empty and the union S_1 .. S_k is the set 1..n

If this is what you want, then I would attack this recursively:

  • If n is 0, then the result is 1 k-tuple of empty sets.
  • If n is 1, then the results are k tuples, where for all elements S_j with j = 1..k it is the empty set except for the j-th element that is set {1}.
  • If n is m, then compute the list of tuples for m-1. Then replicate the list k times, and let the i-th set be the union of the i-th set and {n}. (Note that this is just a more general form of the previous rule for n=1).

Example:

n = 3, k = 2
    n = 2, k = 2
        n = 1, k = 2
            n = 0, k= 2
            result: [({},{})]
        result: [({1},{}), ({},{1})]
    result: [({1,2},{}), ({2},{1}),         // j=1, union 1st with {2}
             ({1},{2}), ({},{1,2})]         // j=2, union 2nd with {2}
result: [
    ({1,2,3},{}), ({2,3},{1}), ({1,3},{2}), ({3},{1,2}), // j=1, union 1st with {3}
    ({1,2},{3}), ({2},{1,3}), ({1},{2,3}), ({},{1,2,3})  // j=2, union 2nd with {3}
]

So u want maps like k1,n1, k1,nN .. kN,nN..

  1. You will need 2 loops
  2. Start by looping the groups . In each group, loop over all the users... and in the second loop , put(group,user) in a hashmap...
  3. Code using the above algorithm.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM