Как создать декартово произведение над произвольными группами чисел в Java?

предположим, у меня есть 2 группы чисел:

{1, 2, 3},
{4, 5}

Я хотел бы создать алгоритм (на Java), который выводит следующие 6 комбинаций:

1,4
1,5
2,4
2,5
3,4
3,5

в каждой группе может быть произвольное количество групп и произвольное количество членов. Таким образом, в приведенном выше примере есть 2 группы с первой группой, имеющей 3 члена, и второй группой, имеющей 2 члена. Другим примером является следующее (3 группы, 3 члена в первых группах и 2 члена в вторая и третья группы):

{1, 2, 3},
{4, 5},
{6, 7}

что дало бы следующие 12 комбинаций:

1,4,6
1,4,7
1,5,6
1,5,7

2,4,6
2,4,7
2,5,6
2,5,7

3,4,6
3,4,7
3,5,6
3,5,7

как я могу сделать это в Java? Я пытаюсь использовать рекурсию, и я посмотрел на аналогичный вопрос уже, но мне все еще не хватает. Спасибо за помощь! (P. S. Это не домашнее задание)

4 ответов


немного заскучал и решил попробовать. Должно быть именно то, что вам нужно:

public static void main(String args[]) {

    ArrayList<int[]> input = new ArrayList<int[]>();
    input.add(new int[] { 1, 2, 3 });
    input.add(new int[] { 4, 5 });
    input.add(new int[] { 6, 7 });

    combine(input, new int[input.size()], 0);
}

private static void combine(ArrayList<int[]> input, int[] current, int k) {

    if(k == input.size()) {
        for(int i = 0; i < k; i++) {
            System.out.print(current[i] + " ");
        }
        System.out.println();
    } else {            
        for(int j = 0; j < input.get(k).length; j++) {
            current[k] = input.get(k)[j];
            combine(input, current, k + 1);
        }       
    }
}

Если вы можете использовать библиотеки, гуава это Sets.cartesianProduct(List<Set<E>>) тут ровно то, что вы ищете. (Раскрытие: я вношу свой вклад в гуаву.)


один из возможных подходов (не обязательно самый эффективный) может заключаться в подходе "разделяй и властвуй". Относительно просто найти все перестановки двух групп (самый тупой способ просто вложен для циклов). Допустим, вы пишете функцию permute и это permute(A,B) где A (например {(1), (2), (3)}) и B(например, {(4), (5)} - группы чисел, и он возвращает вам все перестановки A & B как одну группу (e.g {(1,4), (1,5), (2,4), (2,5), (3,4), (3,5)}).

поэтому, когда у вас есть N групп вместо 2, проще всего просто выбрать небольшие части проблемы. Предположим, у вас есть группы A, B и C. Вместо того, чтобы беспокоиться о них всех отдельно, вы можете думать об этом как о чем-то вроде:

permute(permute(A,B),C)

сначала найдите все перестановки A и B. Как только у вас есть этот результат, найдите все перестановки этого результата с C. и четыре группы A, B, C, D могут выглядеть например:

permute(permute(permute(A,B),C),D)

и так далее. На каждом шаге по пути вы берете текущий результат перестановки и переставляете его со следующей группой в списке групп, которые вы получили в качестве входных данных. Вы объединяете только две группы одновременно, поэтому алгоритм не должен меняться в зависимости от количества групп, которые вы получаете в качестве входных данных.

когда вы делаете рекурсию, есть несколько основных вопросов, на которые вам нужно ответить:

  1. вы можете рекурсивно разбить проблему на меньшие, более разрешимые проблемы? Я думаю, что приведенные выше примеры доказывают, что можно.

  2. Что такое базовый? Каково решение, которое приведет к остановке и размотке рекурсии? Как правило, это должно быть что-то очень простое, к чему может работать ваша рекурсия. В этом случае, вероятно, сводится к чему-то вроде permute(A,{}) где {} - пустой набор.

  3. Что такое рекурсивный случай? Как вы оторвете кусок проблемы и обратиться на меньшем подмножестве проблема? Я думаю, что первоначальное объяснение дает вам один способ потенциально сделать это. Просто отрывайтесь от одной группы за раз и перемежайте ее своим постоянно растущим результатом.

конечно, есть и другие решения этой проблемы, это только первое, что пришло мне в голову. Как N становится больше и больше, этот алгоритм будет существенно замедлен, так как это не очень эффективный.

так что даже если вы не используете это решение, я надеюсь, что это получает Вы на правильном пути!


Как насчет следующего псевдо-кода (без рекурсии)

// Create the initial result filled with the first set of numbers
List result = new List()
For each number in the first set
   result.add(new List(number))

// Iterate over the following sets to permutate over
For each remaining set S
   List newResult = new List()
   For each list L in result
     For each number N in S
       newResult.add(copy of L with N added)
   result = newResult