获取由数组定义的dynimcally多个集合的笛卡尔积

时间:2017-05-26 19:46:58

标签: java algorithm cartesian-product

我想获得由上界数组定义的集合的笛卡尔积。例如

int[] ub = [1,2]

描述集合{0,1}和{0,1,2}。笛卡尔乘积为{(0,0),(0,1),(0,2),(1,0),(1,1),(1,2)}

由于缺乏替代方案,我编写了以下代码,这些代码非常麻烦,可能效率不高。

public static int[][] combineRecursive(int[] ub) {
    ArrayList<int[]> container = new ArrayList<int[]>();
    combineRecursive(new int[0], 0, ub, container);
    return container.toArray(new int[container.size()][]);
}

private static void combineRecursive(int[] node, int i, int[] ub, ArrayList<int[]> leafs) {
    if (i == ub.length) {
        leafs.add(node);
        return;
    }
    for (int val = 0; val <= ub[i]; val++) {
        int[] newNode = new int[node.length + 1];
        System.arraycopy(node, 0, newNode, 0, node.length);
        newNode[node.length] = val;
        combineRecursive(newNode, i + 1, ub, leafs);
    }
}

我的问题是

  1. 我可以更简单,更重要吗
  2. 是否有一个图书馆在一行中做那种事情。

1 个答案:

答案 0 :(得分:0)

考虑到您的确切问题,我认为有一种更简单,更有效的方法来构建您的解决方案。

你想在一系列仅由上界数组描述的序列中找到项目的笛卡尔积 - 让我们用你的两个上界[1,2]的例子代表两个序列{0,1}, {0,1,2}。

为了使它更简单 - 让我们假设在上界中排序没有重要性 - 数组[1,2]和数组[2,1]应该给我们带来相同的结果。

在这种情况下,您可以简单地将上限数组中的最大数字(即2)加1作为数字基数。我们现在将生成一系列数字,并将其基数为3的表示写入数组。不用担心,您不必知道如何计算基数3或进行任何特殊的基数3计算。我们仍然是十进制的,在此过程中使用日常数字。只需按照以下步骤操作:

1)按升序排列上限数组。这样可以在以后比较简单。

2)计算基数 - 最高上限加1。

3)计算一个数字,它是我们正在创建的序列的上限。我们将使用此数字后来循环。它是从右到左计算的:基于位置的力量,乘以位置的值。

在[1,2]的情况下,数字将是5:3 ^ 0 * 2,加上3 ^ 1 * 1 ==&gt; 2 + 3 ==&gt; 5.

4)进入从0到5的循环(前面步骤中找到的数字),查看Integer.toString(i,base)。检查基本表示字符串是否按字典顺序低于上限。如果是这样,请将其添加为结果整数数组。

这是一个代码示例。

public static void main(String[] args) {

  //step 1 - getting the array in sorted order, and adding a string representation.
  int[] upperBounds = {1,2};
  String upperBoundsStr = "12";
  int length = upperBounds.length;
  int base=0;
  Double rangeLimit=0.0;
  List<int[]> cartesianProduct = new ArrayList<int[]>();

  //step 2 - Find the base.
  for (int i=0;i<length;i++) {
      if (upperBounds[i]>base) {
          base=upperBounds[i];
      }
  }
  base++;

  //step 3 - Find the range limit
  for (int i=0;i<length;i++) {
      Double upperBoundsNum=new Double(upperBounds[length-i-1]*java.lang.Math.pow(base, i));
      rangeLimit+=upperBoundsNum;
  }

  //step 4 - Run over the range, and filter the non-relevant numbers
  for (int i=0;i<=rangeLimit.intValue();i++) {

      //Create a String representation of the number in the base. Add leading zeros.
      String number = Integer.toString(i, base);
      if (number.length()<upperBoundsStr.length()) {
          String leadingZero = "";
          for (int j=0;j<upperBoundsStr.length()-number.length();j++) {
              leadingZero+="0";
          }
          number=leadingZero+number;
      }

      //Compare the base representation to the upper bounds string, lexicographically.
      if (number.compareTo(upperBoundsStr)<=0) {
          char[] numberToAddChr= number.toCharArray();
          int[] numberToAdd = new int[numberToAddChr.length];
          for (int j=0;j<numberToAddChr.length;j++){
              numberToAdd[j] = (int) numberToAddChr[j];
          }
          cartesianProduct.add(numberToAdd);
      }
   }
}