【问题标题】:How to get all possible combinations from two arrays?如何从两个数组中获取所有可能的组合?
【发布时间】:2019-05-15 19:07:38
【问题描述】:

我有两个数组:

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 对中的每个组合。副本不同。

【问题讨论】:

    标签: java arrays algorithm combinations


    【解决方案1】:

    使用三重循环:

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

    你本质上想要取操作符向量的叉积(如果它是一个向量)。在 Java 中,这转化为一组三重嵌套的循环。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

        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&lt;String&gt; 参数。也就是说,您可以将方法声明重写为:

        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 已经有,然后我们甚至不需要再传递消费者了。)

        【讨论】:

          【解决方案4】:

          正如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】:

            我做了一个替代的、过度设计(但灵活!)的“业务”解决方案。数组长度和值(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);
                }
            }
            

            像魅力一样工作:)

            【讨论】:

              【解决方案6】:

              一些背景信息,为什么答案是这样的。这个问题并不真正被称为“所有可能的组合”,因为这通常是您可以将元素表示为位并将它们切换为 0 或 1 的问题,无论该元素是否包含。这具有 2^N 的复杂性,其中 N 是您拥有的运算符的数量。这可以在一个循环中轻松解决。

              但是,在您的情况下,您遇到了“替换和排序问题”。其复杂性为 N^n,其中 n 是您必须用运算符填充的点数。 (这在每个点可以是 10 个值的 pincode 中很常见)。因此,因为这比“所有可能的组合”问题更复杂,所以您需要多个循环或递归调用。

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

              【讨论】:

                【解决方案7】:

                我开发了一个涵盖此用例和许多其他用例的类。我称之为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】:

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

                  这是一个示例,展示了有限数量的循环并且根本没有递归。

                  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]
                  

                  【讨论】:

                    【解决方案9】:

                    您可以使用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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-02-29
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-09-20
                      • 2021-02-24
                      相关资源
                      最近更新 更多