Java все возможные комбинации массива

Как получить все возможные комбинации из двух массивов в Java?

Как это решить? Это не дубликат, потому что я не хочу получать каждые две пары данных, я хочу получить каждую комбинацию в 4 парах. Дубликат отличается.

7 ответов

По сути, вы хотите взять кросс-произведение вектора операторов (если бы оно было вектором). В Java это переводится в тройной набор циклов.

Чтобы ответить всем: Java не ориентирован на данные. Честно говоря, если вам нужны такие манипуляции в долгосрочной перспективе, то, возможно, вам следует работать в R или Matlab, где можно сформулировать более общее решение без явных циклов.

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

Это основано на очевидном совпадении того, что число определенных операторов совпадает с количеством позиций для операторов (т.е. на одно число меньше, чем число). Если они не гарантированы, тогда нужно совершенно другой подход.

Хотя решение @TimBiegeleisen будет работать как шарм, его сложность может быть проблемой. Лучшим подходом был бы такой код:

static void combinationUtil(int[] arr, int n, int r, int index, int[] data, int i) < // Current combination is ready to be printed, print it if (index == r) < for (int j=0; j// When no more elements are there to put in data[] if (i >= n) return; // current is included, put next at next location data[index] = arr[i]; combinationUtil(arr, n, r, index+1, data, i+1); // current is excluded, replace it with next (Note that // i+1 is passed, but index is not changed) combinationUtil(arr, n, r, index, data, i+1); > // The main function that prints all combinations of size r // in arr[] of size n. This function mainly uses combinationUtil() static void printCombination(int arr[], int n, int r) < // A temporary array to store all combination one by one int data[]=new int[r]; // Print all combination using temprary array 'data[]' combinationUtil(arr, n, r, 0, data, 0); >

Стоит отметить, что у рекурсии есть свои проблемы: слишком много итераций, и вы получите StackOverflowException

Небольшое дополнение, но предпочитаю записывать скобки массива перед именем и после типа: int[] data а не стиль int data[]

@DavidLavender, да, но с рекурсией в этом коде я не думаю, что будут проблемы с довольно большими числами.

@DavidLavender Если бы массив был достаточно длинным для переполнения стека, вывод был бы слишком велик для этой вселенной.

Это похоже на случай учебника для рекурсивного решения:

public static void combineAndPrint(String[] pieces, String[] operators) < if (pieces.length < 1) < // no pieces? do nothing! >else if (pieces.length == 1) < // just one piece? no need to join anything, just print it! System.out.println(pieces[0]); >else < // make a new array that one piece shorter String[] newPieces = new String[pieces.length - 1]; // copy all but the first two pieces into it for (int i = 2; i < pieces.length; i++) < newPieces[i - 1] = pieces[i]; >// combine the first two pieces and recurse for (int i = 0; i < operators.length; i++) < newPieces[0] = pieces[0] + operators[i] + pieces[1]; combineAndPrint(newPieces, operators); >> > public static void main(String[] args) < String[] operators = ; String[] numbers = ; combineAndPrint(numbers, operators); > 

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

public static void combine(String[] pieces, String[] operators, Consumer consumer)  

и замените System.out.println(pieces[0]) на combineAndPrint(newPieces, operators) consumer.accept(pieces[0]) и рекурсивный вызов функции combineAndPrint(newPieces, operators) с combine(newPieces, operators, consumer) . Затем просто вызовите его из основного метода, например:

combine(numbers, operators, s -> System.out.println(s)); 

(Конечно, для того, чтобы сделать это более гибким способом, требуется более современная версия Java - точнее, Java 8 или новее - тогда как первый пример, который я показал выше, должен работать даже на древних версиях вплоть до Java 1.0. В какой-то будущей версии Java мы получим надлежащую поддержку сопрограмм и генераторов, таких как Python и Kotlin, и даже современные JS уже есть, и тогда нам даже не нужно будет больше обгонять потребителя.)

@IlmariKaronen Не правильнее ли повторять, начиная с 1, а не с 2? for (int i = 1; i < pieces.length; i++)

@OlegCaralanski: Мы хотим скопировать все, кроме первых двух элементов pieces в newPieces , оставив первый слот newPieces свободным для комбинации первых двух элементов pieces . Мы можем сделать это с помощью либо for (int i = 2; i < pieces.length; i++) newPieces[i - 1] = pieces[i]; или for (int i = 1; i < newPieces.length; i++) newPieces[i] = pieces[i + 1]; , В любом случае работает. (Конечно, это также ничего не сломало бы, если бы мы начали цикл за один элемент ранее, но тогда мы будем делать бесполезную работу, копируя pieces[1] в newPieces[0] только для того, чтобы они были перезаписаны ниже.)

Я сделал альтернативное, сверхинженерное (но гибкое!) "Бизнес" решение. Длина и значения массива ( numbers и operators ) могут быть гибкими.

package test1; import java.io.IOException; import java.util.ArrayList; public class MainClass < public static void main(String[] args) throws IOException < String[] operators = ; int[] numbers = ; ArrayList strings = new MainClass().getAllPossibleCombinations(numbers, operators); for (String string : strings) < System.out.println(string); >> private ArrayList getAllPossibleCombinations(int[] numbers, String[] operators) < if (numbers.length < 2) throw new IllegalArgumentException("Length of numbers-array must be at least 2"); if (operators.length < 1) throw new IllegalArgumentException("Length of operators-array must be at least 1"); ArrayListreturnList = new ArrayList<>(); int[] indexes = new int[numbers.length - 1]; while (true) < StringBuilder line = new StringBuilder(); for (int i = 0; i < numbers.length; i++) < int number = numbers[i]; line.append(number); if (i < indexes.length) < line.append(operators[indexes[i]]); >> returnList.add(line.toString()); try < this.updateIndexes(indexes, operators.length - 1); >catch (NoMoreCombinationsException e) < break; >> return returnList; > private void updateIndexes(int[] currentIndexes, int maxValue) throws NoMoreCombinationsException < if (this.intArrayIsOnly(currentIndexes, maxValue)) < throw new NoMoreCombinationsException(); >for (int i = currentIndexes.length - 1; i >= 0; i--) < int currentIndex = currentIndexes[i]; if (currentIndex < maxValue) < currentIndexes[i] = currentIndex + 1; break; >else < currentIndexes[i] = 0; >> > private boolean intArrayIsOnly(int[] array, int value) < for (int iteratedValue : array) < if (iteratedValue != value) return false; >return true; > > class NoMoreCombinationsException extends Exception < public NoMoreCombinationsException() < >public NoMoreCombinationsException(String message) < super(message); >public NoMoreCombinationsException(String message, Throwable cause) < super(message, cause); >public NoMoreCombinationsException(Throwable cause) < super(cause); >public NoMoreCombinationsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) < super(message, cause, enableSuppression, writableStackTrace); >> 

Я бы не назвал это printAllPossible . Этот метод на самом деле ничего не печатает. Может генерировать.

Вы можете упростить это . Сделайте ваши методы статичными и верните boolean в updateIndexes . Избегайте использования исключений в управлении потоком данных; и отдавайте предпочтение интерфейсу List вместо реализации ArrayList в ваших API.

Как уже указывал findusl в своем ответе, проблема здесь, строго говоря, не в том, чтобы найти какую-либо "комбинацию двух массивов". Вместо этого вы просто хотите найти все возможные комбинации доступных операторов.

(Тот факт, что вы позже захотите "перемешать" их с операндами, скорее не имеет отношения к сути вопроса)

Итак, вот еще один вариант решения этой проблемы: вы можете создать итерацию по всем комбинациям определенного количества элементов из определенного набора (в вашем случае: операторы), а затем просто объединить результаты с другим набором (в вашем случае: операнды).

import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; public class OperatorsTest < public static void main(String[] args) < String[] operators = ; int[] numbers = ; CombinationIterable iterable = new CombinationIterable(3, Arrays.asList(operators)); for (List element : iterable) < System.out.println(interveave(element, numbers)); >> private static String interveave(List operators, int numbers[]) < StringBuilder sb = new StringBuilder(); for (int i=0; isb.append(numbers[numbers.length-1]); return sb.toString(); > > class CombinationIterable implements Iterable < private final Listinput; private final int sampleSize; private final int numElements; public CombinationIterable(int sampleSize, List input) < this.sampleSize = sampleSize; this.input = input; numElements = (int) Math.pow(input.size(), sampleSize); >@Override public Iterator iterator() < return new Iterator() < private int current = 0; private final int chosen[] = new int[sampleSize]; @Override public boolean hasNext() < return current < numElements; >@Override public List next() < if (!hasNext()) < throw new NoSuchElementException("No more elements"); >List result = new ArrayList(sampleSize); for (int i = 0; i < sampleSize; i++) < result.add(input.get(chosen[i])); >increase(); current++; return result; > private void increase() < int index = chosen.length - 1; while (index >= 0) < if (chosen[index] < input.size() - 1) < chosen[index]++; return; >chosen[index] = 0; index--; > > >; > > 

Задача напоминает задачу поиска набора операций, которые могут быть выполнены с определенным количеством операндов и операторов, и, таким образом, этот Q/A может быть связан. Но вопрос о том, следует ли рассматривать такие вещи, как ассоциативность или коммутативность, здесь не упоминался.

Вам не нужно многократные циклы или рекурсия.

Вот пример, демонстрирующий ограниченное количество циклов и никакой рекурсии.

int[][] combine (int[] values) < int size = values.length; int combinations = 1; for(int i = 0; i < size; i++) < combinations *= size; >// or int combinations = (int)Math.pow(size, size); int[][] result = new int[combinations][size]; for(int i = 0; i < combinations; i++) < int index = i; for(int j = 0; j < size; j++) < result[i][j] = values[index % size]; index /= size; >> return result; > 

Если вы используете его с тремя элементами, [1, 2, 3] , как в коде ниже:

void testCombine() < int[][] combinations = combine(new int[]); for(int[] combination: combinations) < System.out.println(Arrays.toString(combination)); >> 

В итоге вы получите следующий результат:

[1, 1, 1] [2, 1, 1] [3, 1, 1] [1, 2, 1] [2, 2, 1] [3, 2, 1] [1, 3, 1] [2, 3, 1] [3, 3, 1] [1, 1, 2] [2, 1, 2] [3, 1, 2] [1, 2, 2] [2, 2, 2] [3, 2, 2] [1, 3, 2] [2, 3, 2] [3, 3, 2] [1, 1, 3] [2, 1, 3] [3, 1, 3] [1, 2, 3] [2, 2, 3] [3, 2, 3] [1, 3, 3] [2, 3, 3] [3, 3, 3] 

Источник

Как получить все возможные комбинации элементов группы массивов

Знаю что эту задачу многие гуглят, т.к. сам недавно столкнулся с этим. Поскольку рабочего решения я так и не нашел, пришлось придумать свое.

Итак, вводные данные. Имеем группу массивов, например:

models = [ "audi", "bmw", "toyota", "vw" ]; colors = [ "red", "green", "blue", "yellow", "pink" ]; engines = [ "diesel", "gasoline", "hybrid" ]; transmissions = [ "manual", "auto", "robot" ]; 

Теперь представим, что нам надо собрать набор ассоциативных массивов (map) примерно такого вида:

variant1 = < "model": "audi", "color": "red", "engine": "diesel", "transmission": "manual" >variant2 = < "model": "audi", "color": "red", "engine": "diesel", "transmission": "auto" >variant3 = < "model": "audi", "color": "red", "engine": "diesel", "transmission": "robot" >variant4 = < "model": "audi", "color": "red", "engine": "gasoline", "transmission": "manual" >… variantN =

В упрощенном виде алгоритм подобной работы выглядит так:

Т.е. по сути вкладываем каждый набор внутрь другого набора, и перебираем в цикле. Теперь остается придумать как сделать то же самое без привязки к конкретному числу наборов.

Для начала определимся с терминами:

Параметр — то, как называется элемент набора, например model, color и т.д.
Набор элементов параметра — список, присвоенный параметру (например, [ «audi», «bmw», «toyota», «vw» ])
Элемент набора — отдельный элемент списка, например audi, bmw, red, blue и т.п.
Итоговые наборы — то что мы должны сгенерировать

Как это будет выглядеть? Нам потребуется функция, при каждом вызове которой будет сдвигаться на одну позицию условный счетчик итератора, контролирующего перебор параметров (model, color и т.п.). Внутри этой функции помимо сдвига счетчика будет проходить перебор элементов параметра (audi, bmw…; red, blue… и т.д.). И внутри этого вложенного цикла наша функция будет рекурсивно вызывать сама себя.

Далее рабочий пример на языке Java с комментариями:

public class App < public static void main(String[] args) < Map> source = Map.of( "model", Arrays.asList("audy", "bmw", "toyota", "vw"), "color", Arrays.asList("red", "green", "blue", "yellow", "pink"), "engine", Arrays.asList("diesel", "gasoline", "hybrid"), "transmission", Arrays.asList("manual", "auto", "robot") ); Combinator combinator = new Combinator<>(source); List result = combinator.makeCombinations(); for(Map variant : result) < System.out.println(variant); >> public static class Combinator < //Тут в виде ассоциативного массива хранятся исходные данные private Map> sources; //Итератор для перебора параметров. В нашем случае это обязательно //ListIterator, т.к. потребуется вызывать метод previous private ListIterator keysIterator; //Счетчик текущего положения в итерации для каждого параметра //где ключ - имя параметра, а значение - текущая позиция в наборе элементов private Map counter; //Тут будут храниться итоговые наборы private List result; public Combinator(Map> sources) < this.sources = sources; counter = new HashMap<>(); keysIterator = new ArrayList<>(sources.keySet()) .listIterator(); > //Этот метод вызываем для генерации набора public List makeCombinations() < result = new ArrayList<>(); //Запускаем перебор параметров loop(); return result; > private void loop() < //Проверяем, есть ли еще параметры в источнике if(keysIterator.hasNext())< //Сдвигаем счетчик вперед K key = keysIterator.next(); //Активируем набор элементов (указываем в счетчике, //что находимся на первом элементе набора) counter.put(key, 0); //Перебираем элементы набора while(counter.get(key) < sources.get(key).size())< //Рекурсивно вызываем метод loop чтобы активировать следующий набор элементов loop(); //Сдвигаем счетчик элементов набора counter.put(key, counter.get(key) + 1); >//Если мы уже перебрали элементы набора - сдвигаем итератор параметров назад keysIterator.previous(); > else < //Если параметров в источнике нет, т.е. мы активировали все наборы попеременно //заполняем очередной итоговый набор fill(); >> //В этом методе наполняем очередной итоговый набор private void fill() < Mapvariant = new HashMap<>(); //Перебираем все параметры for(K key : sources.keySet()) < //Получаем значение текущего элемента в наборе Integer position = counter.get(key); //Вставляем в итоговый набор variant.put(key, sources.get(key).get(position)); >result.add(variant); > > > 

Источник

Читайте также:  Html div вертикальное выравнивание
Оцените статью