Generate All Combinations from Multiple (N) Lists

Generate all Combinations from Multiple (n) Lists

Hope this helps.

class NListBuilder

{
Dictionary<int, List<string>> tags = new Dictionary<int, List<string>>();

public NListBuilder()
{
tags.Add(1, new List<string>() { "A", "B", "C" });
tags.Add(2, new List<string>() { "+", "-", "*" });
tags.Add(3, new List<string>() { "1", "2", "3" });
}

public List<string> AllCombos
{
get
{
return GetCombos(tags);
}
}

List<string> GetCombos(IEnumerable<KeyValuePair<int, List<string>>> remainingTags)
{
if (remainingTags.Count() == 1)
{
return remainingTags.First().Value;
}
else
{
var current = remainingTags.First();
List<string> outputs = new List<string>();
List<string> combos = GetCombos(remainingTags.Where(tag => tag.Key != current.Key));

foreach (var tagPart in current.Value)
{
foreach (var combo in combos)
{
outputs.Add(tagPart + combo);
}
}

return outputs;
}


}
}

Generating All Combinations of List n Levels Deep in Java

I believe I made what you are looking for. The code is split up into four separate methods(You didn't make any distinction if it had to be only 1):

public static <T extends Comparable<? super T>> List<List<List<T>>> level(List<List<T>> items, int level) {
List<List<List<T>>> result = new ArrayList<>();
if(level == 1) {
for(List<T> item : items) {
result.add(Collections.singletonList(item));
}
return result;
}

for(int i = 0; i < level; i++) {
if(i == 0) {
for(List<T> item : items)
result.add(Collections.singletonList(item));
continue;
}

List<List<List<T>>> newResult = new ArrayList<>();
for(List<List<T>> item : result) {
List<List<List<T>>> combined = new ArrayList<>();
List<T> first = item.get(0);
for(int j = 0; j < items.size(); j++) {
List<List<T>> current = new ArrayList<>();
List<T> it = items.get(j);
current.addAll(item);
current.add(it);

combined.add(current);
}

newResult.addAll(combined);
}

result = newResult;
}

clean(result);
return result;
}

This is what does the majority of the algorithm. First, like in the function you provided, it checks to see if the level is 1, in which case you can just return a list of list, like in the given method. After that, we loop over however many levels we have. We first check to see if the current level is 1, in which case it will just do the same thing as if the method were called with a level of 1. Next we create a new list called newResult. This variable is basically a temporary value of result to prevent a concurrent modification exception. Then we loop through every value already in result. We create a few new values based on whatever value we have, and add them to combined. We then add combined into newResult, and the algorithm is basically over. Now, those loops don't count for when all of the values are the same, for example, [1, 2], [1, 2], [1, 2], [1, 2]. So we call the clean method to remove those cases.

public static <T extends Comparable<? super T>> void clean(List<List<List<T>>> list) {
List<List<List<T>>> removals = new ArrayList<>();
for(List<List<T>> item : list) {
if(!check(item))
removals.add(item);
}
for(List<List<T>> item : removals) {
list.remove(item);
}
}

This method runs through everything inside of the given list, and runs the check method on the element. That method is explained further down. If the element is not valid, it is marked for removal, and is removed in the next loop.

public static <T extends Comparable<? super T>> boolean check(List<List<T>> list) {
if(list.size() < 2) return true;

for(int i = 1; i < list.size(); i++) {
List<T> previous = list.get(i-1);
List<T> item = list.get(i);

if(notEqual(previous, item)){
return true;
}
}

return false;
}

This loop checks to see if a given list is valid, by comparing one list against another, until it finds two that aren't the same. When that happens, the list is valid, and returns true. If not, it will never return, will break out of the loop, and return false.

public static <T extends Comparable<? super T>> boolean notEqual(List<T> a, List<T> b) {
for(int i = 0; i < Math.min(a.size(), b.size()); i++) {
T ao = a.get(i);
T bo = b.get(i);

if(ao.compareTo(bo) != 0)
return true;
}

return false;
}

This method takes two input lists, and checks to see if the elements inside of them aren't equal. It loops over both lists, getting the elements at the same index, and compares them to one another. If they are not equal, it returns true, and otherwise, it finishes the loop and returns false.

Please note that this is simply a proof of concept and not a finished version. A lot of aspects of it could definitely be improved upon, but this is a working version of what you asked for.

Link to working version on jDoodle

If you have any questions about any aspect of it, or want anything clarified, don't hesitate to ask!

Edit:
I have revised the algorithm to incorporate what you have asked for. Here is the new code:

public static <T extends Comparable<? super T>> List<List<List<T>>> level(List<List<T>> items, int minLevel, int maxLevel) {
List<List<List<T>>> result = new ArrayList<>();

for(int i = minLevel; i < maxLevel+1; i++) {
result.addAll(level(items, i));
}

return result;
}

This is the overloaded method that will allow you to specify the range of levels you want. Given a minimum and maximum level, it will return a new list containing all of the levels within that range, inclusive. As you said, it is relatively trivial, as a simple loop.

public static <T extends Comparable<? super T>> List<List<List<T>>> level(List<List<T>> items, int level) {
List<List<List<T>>> result = new ArrayList<>();
if(level == 1) {
for(List<T> item : items) {
result.add(Collections.singletonList(item));
}
return result;
}

for(int i = 0; i < level; i++) {
if(i == 0) {
for(List<T> item : items)
result.add(Collections.singletonList(item));
continue;
}

List<List<List<T>>> newResult = new ArrayList<>();
for(List<List<T>> item : result) {
if(item.size() < i)
continue;

List<List<List<T>>> combined = new ArrayList<>();
List<T> first = item.get(0);
for(int j = 0; j < items.size(); j++) {
List<List<T>> current = new ArrayList<>();
List<T> it = items.get(j);
current.addAll(item);
current.add(it);

combined.add(current);
}

newResult.addAll(combined);
}

result = newResult;
}

List<List<List<T>>> removals = new ArrayList<>();
for(List<List<T>> item : result) {
if(!check(item))
removals.add(item);
}
for(List<List<T>> item : removals) {
result.remove(item);
}

return result;
}

Here is the revised method. I removed the clean method, and just put it inside of the level method, as it was only called once. I don't think it is really possible, at least with the current code to run that clean method during the algorithm, because at this point in time, the way it works is it generates all possible combinations for a given level, then goes to the next one. If combinations that are the same were removed, on the next level, those combinations wouldn't be added.

Here is an example:
Say I have [1, 2], [1, 3], [2, 3]. If I went to level two, I would have the combinations specified in your question. Pretty obvious right? Well, if I then went on to level 3, only using the results from level 2, I would miss out on all combinations containing [1, 2], [1, 2] [...], since that is not in the given list. This is an issue with the algorithm, and could definitely be improved upon.

I plan on further refactoring this, to make it check inside the algorithm, but it might take me a hot minute to do that.

New working version in jDoodle

Edit 2:
Incorporating the clean method inside of the algorithm was actually much simpler than I initially thought. Here is the new code with a couple of comments:

public static <T extends Comparable<? super T>> List<List<List<T>>> level(List<List<T>> items, int level) {
List<List<List<T>>> result = new ArrayList<>();
for(int i = 0; i < level; i++) {
if(i == 0) { // If level is 0, we can just add the items as singleton lists to the result
for(List<T> item : items)
result.add(Collections.singletonList(item));
continue;
}

List<List<List<T>>> newResult = new ArrayList<>(); // Temporary items that will be added
for(List<List<T>> item : result) {
if(item.size() < i) // Make sure we are manipulating items that are on the previous level
continue;

List<List<List<T>>> combined = new ArrayList<>(); // The temporary values for this specific item
for(int j = 0; j < items.size(); j++) {
List<List<T>> current = new ArrayList<>(); // The current list with the value
current.addAll(item); // Add the current items from result to the list
current.add(items.get(j)); // Add the current item from items to the list

if (i == level-1 && !check(current)) { // If this is the last level, and the current list shouldn't be added, skip adding
continue;
}

combined.add(current); // Add the current list to the combined values
}

newResult.addAll(combined); // Add all of the lists in combined to the new result
}

result = newResult; // Make result equal to the new result
}

return result;
}

Now what it does is, when adding a new combination to the list, it first checks if the current level is the final one. If so, it actually checks the list, and if it is not valid, it skips actually adding it.

I again plan on completely rewriting the algorithm in a much more intelligent format, but this code completely works right now.

Working version on jDoodle

All combinations of a list of lists

you need itertools.product:

>>> import itertools
>>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
>>> list(itertools.product(*a))
[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]

Generate all combinations from multiple lists

You need recursion:

Let's say all your lists are in lists, which is a list of lists. Let result be the list of your required permutations. You could implement it like this:

void generatePermutations(List<List<Character>> lists, List<String> result, int depth, String current) {
if (depth == lists.size()) {
result.add(current);
return;
}

for (int i = 0; i < lists.get(depth).size(); i++) {
generatePermutations(lists, result, depth + 1, current + lists.get(depth).get(i));
}
}

The ultimate call will be like this:

generatePermutations(lists, result, 0, "");

How to get all possible combinations of a list’s elements?

Have a look at itertools.combinations:

itertools.combinations(iterable, r)

Return r length subsequences of elements from
the input iterable.

Combinations are emitted in lexicographic sort order. So, if the
input iterable is sorted, the
combination tuples will be produced in
sorted order.

Since 2.6, batteries are included!

All possible combinations of the items in n lists

itertools.product is what you're looking for. It takes multiple Iterables (lists are iterables) and produces a generator that loops over all combinations for each of them.

See example:

>>> for a, b, c in itertools.product([1, 2, 3], "abc", [True, False]):
... print(a, b, c)
...
1 a True
1 a False
1 b True
1 b False
1 c True
1 c False
2 a True
2 a False
2 b True
2 b False
2 c True
2 c False
3 a True
3 a False
3 b True
3 b False
3 c True
3 c False
>>>

So your use case would turn into:

itertools.product(*chords)

How to get all combination from multiple lists?

User itertools, combinations:

import itertools
a = ['11', '12']
b = ['21', '22']
c = ['31', '32']
list(itertools.combinations(itertools.chain(a,b,c), 3))
[('11', '12', '21'), ('11', '12', '22'), ('11', '12', '31'), ('11', '12', '32'), ('11', '21', '22'), ('11', '21', '31'), ('11', '21', '32'), ('11', '22', '31'), ('11', '22', '32'), ('11', '31', '32'), ('12', '21', '22'), ('12', '21', '31'), ('12', '21', '32'), ('12', '22', '31'), ('12', '22', '32'), ('12', '31', '32'), ('21', '22', '31'), ('21', '22', '32'), ('21', '31', '32'), ('22', '31', '32')]

or product:

list(itertools.product(a,b,c))
[('11', '21', '31'), ('11', '21', '32'), ('11', '22', '31'), ('11', '22', '32'), ('12', '21', '31'), ('12', '21', '32'), ('12', '22', '31'), ('12', '22', '32')]


Related Topics



Leave a reply



Submit