web-dev-qa-db-fra.com

Comment obtenir toutes les combinaisons possibles de deux tableaux en Java?

J'ai les deux tableaux:

String[] operators = {"+", "-", "*"};
int[] numbers = {48, 24, 12, 6};

Et je veux obtenir toutes les combinaisons possibles dans un format de chaîne comme ceci:

48+24+12+6
48+24+12-6
48+24+12*6
48+24-12+6
48+24-12-6
48+24-12*6
..........
48*24*12*6

C'est ce que j'ai essayé:

for(int i = 0; i < operators.length; i++) {
    System.out.println(numbers[0] + operators[i] + numbers[1] + operators[i] + numbers[2] + operators[i] + numbers[3]);
}

Mais cela n'imprime que:

48+24+12+6
48-24-12-6
48*24*12*6

Comment résoudre ceci?

Ce n'est pas une copie parce que je ne veux pas obtenir toutes les deux paires de données, je veux obtenir chaque combinaison de 4 paires. Le duplicata est différent.

18
Oleg Caralanski

Utilisez une triple boucle:

for (int i=0; i < operators.length; ++i) {
    for (int j=0; j < operators.length; ++j) {
        for (int k=0; k < operators.length; ++k) {
            System.out.println(numbers[0] + operators[i] + numbers[1] + operators[j] +
                numbers[2] + operators[k] + numbers[3]);
        }
    }
}

Vous voulez essentiellement prendre le produit croisé du vecteur opérateurs (s’il s’agissait d’un vecteur). En Java, cela se traduit par un ensemble de boucles imbriquées trois fois.

16
Tim Biegeleisen

Alors que la solution @TimBiegeleisen fonctionnerait à merveille, sa complexité pourrait poser problème. La meilleure approche serait un code comme celui-ci:

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<r; j++) 
                System.out.print(data[j]+" "); 

            System.out.println(""); 

        return; 

        } 

        // 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); 
    } 

Source: GeeksForGeeks et mon IDE :)

7
PradyumanDixit

Cela ressemble à un cas d'école pour une solution récursive:

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's 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 = {"48", "24", "12", "6"};
    combineAndPrint(numbers, operators);
}

Essayez-le en ligne!

En passant, pour généraliser cette méthode afin que vous puissiez faire plus de choses avec les expressions générées que de les imprimer, je vous recommande de lui faire accepter un paramètre Consumer<String> supplémentaire. Autrement dit, vous pouvez réécrire la déclaration de la méthode comme suit:

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

et remplacez System.out.println(pieces[0]) par consumer.accept(pieces[0]) et l'appel récursif à combineAndPrint(newPieces, operators) par combine(newPieces, operators, consumer). Ensuite, appelez-le simplement à partir de votre méthode principale, par exemple. comme:

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

Essayez-le en ligne!

(Bien sûr, le faire de cette manière plus flexible nécessite une version assez moderne de Java - Java 8 ou une version ultérieure, pour être spécifique - alors que le premier exemple que j'ai présenté ci-dessus devrait fonctionner même sur les versions anciennes jusque dans Java 1.0. certaines versions futures de Java prendront en charge les coroutines et les générateurs, tels que Python et Kotlin et même JS moderne, et nous n'aurons même plus besoin de faire passer le consommateur.)

5
Ilmari Karonen

J'ai conçu une solution "professionnelle" alternative, trop d'ingénierie (mais flexible!). Les longueurs et valeurs de tableau (numbers et operators) peuvent être flexibles.

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 = {48, 24, 12, 6};

        ArrayList<String> strings = new MainClass().getAllPossibleCombinations(numbers, operators);

        for (String string : strings)
        {
            System.out.println(string);
        }
    }

    private ArrayList<String> 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");

        ArrayList<String> returnList = 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);
    }
}

Fonctionne comme un charme :)

2
Impulse The Fox

Comme déjà souligné par findusl dans sa réponse , le problème ici est, à proprement parler, de ne trouver aucune sorte de "combinaison de deux tableaux". Au lieu de cela, vous voulez simplement trouver toutes les combinaisons possibles des opérateurs disponibles.

(Le fait que vous souhaitiez plus tard les "intercaler" avec des opérandes n'a rien à voir avec le coeur de la question)

Voici donc une autre option pour résoudre ce problème: vous pouvez créer un itérable sur toutes les combinaisons d’un certain nombre d’éléments d’un certain ensemble (dans votre cas: les opérateurs), puis simplement combiner les résultats avec l’autre ensemble ( dans votre cas: les opérandes).

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 = {48, 24, 12, 6};

        CombinationIterable<String> iterable = 
            new CombinationIterable<String>(3, Arrays.asList(operators));
        for (List<String> element : iterable)
        {
            System.out.println(interveave(element, numbers));
        }
    }

    private static String interveave(List<String> operators, int numbers[])
    {
        StringBuilder sb = new StringBuilder();
        for (int i=0; i<operators.size(); i++)
        {
            sb.append(numbers[i]);
            sb.append(operators.get(i));
        }
        sb.append(numbers[numbers.length-1]);
        return sb.toString();
    }

}

class CombinationIterable<T> implements Iterable<List<T>>
{
    private final List<T> input;
    private final int sampleSize;
    private final int numElements;
    public CombinationIterable(int sampleSize, List<T> input)
    {
        this.sampleSize = sampleSize;
        this.input = input;
        numElements = (int) Math.pow(input.size(), sampleSize);
    }

    @Override
    public Iterator<List<T>> iterator()
    {
        return new Iterator<List<T>>()
        {
            private int current = 0;
            private final int chosen[] = new int[sampleSize];

            @Override
            public boolean hasNext()
            {
                return current < numElements;
            }

            @Override
            public List<T> next()
            {
                if (!hasNext())
                {
                    throw new NoSuchElementException("No more elements");
                }

                List<T> result = new ArrayList<T>(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--;
                }
            }
        };
    }
}

La tâche ressemble à celle de la recherche d’un ensemble d’opérations pouvant être effectuées avec un certain nombre d’opérandes et d’opérateurs, et donc, ce Q/A peut être lié. Mais le point de savoir si des questions comme l’associativité ou la commutativité doivent être considérées ici n’est pas mentionné dans la question.

2
Marco13

J'ai développé un cours qui couvre ce cas d'utilisation et bien d'autres. Je l'appelle le TallyCounter . Votre question recevrait une réponse avec ce cours comme ceci:

package app;

import Java.util.HashMap;
import Java.util.Map;

import app.TallyCounter.Type;

public class App {

    public static void main(String args[]) throws Exception {

        Map<Long, String> map = new HashMap<>();
        map.put(0l, "+");
        map.put(1l, "-");
        map.put(2l, "*");

        TallyCounter counter = new TallyCounter(3, Type.NORMAL, 2);
        do {
            System.out.format("48%s24%s12%s6\n",
                map.get(counter.getArray()[2]),
                map.get(counter.getArray()[1]),
                map.get(counter.getArray()[0])
            );
            counter.increment();
        } while (!counter.overflowFlag);
    }
}
0
GuiRitter

Quelques informations de base sur les raisons pour lesquelles les réponses sont telles qu'elles sont. Ce problème n’appelle pas vraiment "toutes les combinaisons possibles" car c’est généralement le problème où vous pouvez représenter les éléments sous forme de bits et les basculer sur 0 ou 1, que l’élément soit inclus ou non. Cela a une complexité de 2 ^ N, où N est le nombre d'opérateurs que vous avez. Cela peut être résolu facilement en une seule boucle. 

Cependant, dans votre cas, vous avez le "problème de remplacement et de séquence de l'urne". La complexité de ceci est N ^ n où n est le nombre de taches que vous devez remplir avec des opérateurs. (Ceci est souvent observé pour les codes PIN où chaque point peut avoir 10 valeurs). Donc, parce que cela est plus complexe que le problème "toutes les combinaisons possibles", vous avez besoin de plusieurs boucles ou d'appels récursifs. 

Donc, pour répondre à la question "comment résoudre ceci?". Vous devez le résoudre avec plusieurs boucles ou récursivité en raison de la complexité du problème sous-jacent.

0
findusl

Vous n'avez pas besoin de plusieurs boucles ou récursivité.

Voici un exemple présentant un nombre limité de boucles et aucune récursivité.

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;
}

Si vous l'utilisez avec trois éléments, [1, 2, 3], comme dans le code ci-dessous:

void testCombine() {
  int[][] combinations = combine(new int[]{1, 2, 3});
  for(int[] combination: combinations) {
    System.out.println(Arrays.toString(combination));
  }
}

Vous vous retrouvez avec le résultat suivant:

[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]
0
Olivier Grégoire