如何从Java中的两个数组获取所有可能的组合?

时间:2018-12-14 13:46:36

标签: java arrays algorithm

我有两个数组:

String[] operators = {"+", "-", "*"};
int[] numbers = {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
..........
48*24*12*6

这是我尝试过的:

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

但它仅打印:

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

如何解决这个问题?

这不是重复的,因为我不想获取每两对数据,我想获取4对中的每个组合。重复项不同。

9 个答案:

答案 0 :(得分:16)

使用三重循环:

themes

您本质上想获取运算符向量的叉积(如果它是向量)。在Java中,这转化为三重嵌套的循环集。

答案 1 :(得分:8)

虽然@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<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); 
    } 

来源:GeeksForGeeks和我的IDE:)

答案 2 :(得分:5)

这听起来像是教科书的递归解决方案:

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

Try it online!

顺便说一句,为使此方法通用化,以便您可以对生成的表达式进行更多处理而不仅仅是打印它们,我建议使它接受额外的Consumer<String>参数。也就是说,您可以将方法声明重写为:

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

,将System.out.println(pieces[0])替换为consumer.accept(pieces[0]),并将递归调用combineAndPrint(newPieces, operators)替换为combine(newPieces, operators, consumer)。然后只需从您的主要方法调用它,例如为:

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

Try it online!

(当然,以这种更灵活的方式进行操作需要使用某种现代Java版本-具体来说是Java 8或更高版本-而我在上面显示的第一个示例甚至可以一直使用到Java 1.0之前的较旧版本。 。也许在Java的将来版本中,我们将获得对协程和生成器的适当支持,例如Python和Kotlin,甚至现代JS都已经有了,然后我们甚至不需要将消费者传递给其他人。)

答案 3 :(得分:2)

我提出了一种替代的,过度设计的(但很灵活!)“业务”解决方案。数组的长度和值(numbersoperators)可以灵活。

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

像魅力一样工作:)

答案 4 :(得分:2)

正如findusl in his answer所指出的那样,严格来说,这里的问题是找不到任何类型的“两个数组的组合”。相反,您基本上只想找到可用运算符的所有可能组合。

(您以后要与操作数“交织”的事实与问题的核心无关)

因此,这是解决此问题的另一种选择:您可以创建一个iterable over all combinations of a certain number of elements from a certain set(在您的情况下为:运算符),然后将结果与另一组(在您的情况下为:操作数)组合起来。

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

该任务类似于查找可以用一定数量的操作数和运算符完成的一组操作的任务,因此this Q/A可能是相关的。但是,在此问题中并未提及是否应考虑结合性或可交换性。

答案 5 :(得分:0)

一些背景信息,说明了答案为何如此。这个问题并不是真正的“所有可能的组合”,因为通常这是一个问题,您可以将元素表示为位并将其切换为0或1(无论是否包含元素)。这具有2 ^ N的复杂度,其中N是您拥有的运算符的数量。只需一个循环即可轻松解决。

但是,在您的情况下,您会遇到“替换和顺序ur问题”。复杂度为N ^ n,其中n是必须用运算符填充的斑点数量。 (对于通常每个位置可以为10个值的密码,通常会看到这种情况)。因此,由于此方法比“所有可能的组合”问题具有更高的复杂性,因此您需要多个循环或递归调用。

因此要回答“如何解决这个问题”这个问题。由于潜在问题的复杂性,您必须使用多个循环或递归来解决它。

答案 6 :(得分:0)

您不需要多个循环或递归。

这是一个显示有限数量的循环且完全没有递归的示例。

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[]{1, 2, 3});
  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]

答案 7 :(得分:0)

我已经开发了一个涵盖此用例和许多其他用例的类。我称它为TallyCounter。这样的课程将回答您的问题:

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

答案 8 :(得分:0)

您可以使用 streams 获得两个数组的所有可能组合。首先,遍历 numbers 数组并将运算符符号附加到每个数字,您会得到一个这样的数组:{"48+", "48-", "48*"} 代表每个数字。操作员的数量可能会有所不同。然后reduce这个的数组通过顺序相乘数组对成为单个数组,你得到一个可能组合的数组。

Try it online!

String[] operators = {"+", "-", "*"};
int[] numbers = {48, 24, 12, 6};
// an array of possible combinations
String[] comb = IntStream.range(0, numbers.length)
        // append each substring with possible
        // combinations, except the last one
        // return Stream<String[]>
        .mapToObj(i -> numbers.length - 1 > i ?
                Arrays.stream(operators)
                        .map(op -> numbers[i] + op)
                        .toArray(String[]::new) :
                new String[]{"" + numbers[i]})
        // reduce stream of arrays to a single array
        // by sequentially multiplying array pairs
        .reduce((arr1, arr2) -> Arrays.stream(arr1)
                .flatMap(str1 -> Arrays.stream(arr2)
                        .map(str2 -> str1 + str2))
                .toArray(String[]::new))
        .orElse(null);

// column-wise output (three columns in this case)
int columns = (int) Math.pow(operators.length, numbers.length - 2);
IntStream.range(0, columns)
        .mapToObj(i -> IntStream.range(0, comb.length)
                .filter(j -> j % columns == i)
                .mapToObj(j -> comb[j])
                .collect(Collectors.joining(" | ")))
        .forEach(System.out::println);

输出:

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 | 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 | 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
48+24*12-6 | 48-24*12-6 | 48*24*12-6
48+24*12*6 | 48-24*12*6 | 48*24*12*6

另见:Generate all possible string combinations by replacing the hidden “#” number sign