如何在Java中生成具有N个可能元素(M> N)的数组列表(全部具有长度M)?

时间:2016-07-24 12:11:00

标签: java

例如,如果元素是{1, 2} (n = 2)m = 3,则该方法应生成一个像{[1,1,1],[1,1,2],[1,2,1],[2,1,1],[1,2,2],[2,2,1],[2,1,2],[2,2,2]}这样的数组列表。

我知道Python可以执行y = itertools.product((1, 2), repeat=3)之类的操作,但是我如何有效地在Java中实现它。我已尝试提供一些初始List并使用以下内容来获得我想要的但时间复杂度太高而且当输入很大时性能非常差。

public static List<List<Integer>> permute (List<Integer> list, int need) {

    List<List<Integer>> result = new ArrayList<>();
    if (need--==0) {
        result.add(list);
        return result;
    }
    for (int current : list)
        insert(permute(list,need), current, result);
    return result;
}


private static void insert(List<List<Integer>> currentLists, int currentInt, List<List<Integer>> list) {
    for (List<Integer> currentList : currentLists) {
        int size = currentList.size();
        for (int i = 0; i <= size; i++) {
            List<Integer> newList = new LinkedList<>();
            newList.addAll(currentList);
            newList.add(i, currentInt);
            list.add(newList);
        }
    }
}

3 个答案:

答案 0 :(得分:4)

实际上你无法降低复杂性。您需要执行的最少操作数是创建列表。列表的数量不能减少(它总是等于n ^ m)并且这些列表的创建是在执行期间花费大量时间的事情。

我添加了我用来做一些测试的代码,如果它能帮到你的话。

//Main method who generate the resultList
public static ArrayList<ArrayList<Integer>> generateList(ArrayList<Integer> elements, int size) {
    //Initialisation
    ArrayList<ArrayList<Integer>> resultList = new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> indexes = new ArrayList<Integer>();

    for(int i = 0; i < size;i++){
       indexes.add(0);
    }


    resultList.add(generateCurrentList(indexes,elements)); //Add the first element

    for(int i = 0; i < Math.pow(elements.size(),size)-1;i++){ //Add the other elements by incrementing indexes at each add
        incrementIndexes(indexes,elements.size());
        resultList.add(generateCurrentList(indexes,elements));
    }

    return resultList;  
}


//Increment indexes
public static void incrementIndexes(ArrayList<Integer> indexes,int nbrElements){
    indexes.set(indexes.size()-1, indexes.get(indexes.size()-1)+1); //Increment the last index
    for(int i = indexes.size()-1;i > 0;i--){//For each index increment the previous one if the current is greater than the number of element
        if(indexes.get(i)==nbrElements)
            indexes.set(i-1, indexes.get(i-1)+1);
    }
    for(int i = 0;i < indexes.size();i++){
        indexes.set(i, indexes.get(i)%nbrElements);
    }
}

//Generate an arrayList using the current indexes and the list of elements
public static ArrayList<Integer> generateCurrentList(ArrayList<Integer> indexes,ArrayList<Integer> elements){
    ArrayList<Integer> currentList = new ArrayList<Integer>();
    for(int i = 0; i < indexes.size();i++){
        currentList.add(elements.get(indexes.get(i)));
    }
    return currentList;
}`

答案 1 :(得分:2)

使用libary StreamEx解决方案可能如下所示:

buildscript {
repositories {
    jcenter()
}
 dependencies {
    classpath 'com.android.tools.build:gradle:2.2.0-alpha6'
    classpath 'com.google.gms:google-services:3.0.0' //rename this

    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
 }
}

allprojects {
   repositories {
     jcenter()
  }
}

task clean(type: Delete) {
   delete rootProject.buildDir
}

您可以使用并行处理或仅消耗部分延迟生成的结果来提高效率。

普通旧java中的另一个懒惰解决方案会创建Iterator,它会懒惰地生成排列

    List<Integer> list = Arrays.asList(1, 2);
    int need = 3;
    StreamEx<List<Integer>> product = StreamEx.cartesianPower(need, list);

这样的生成器可以在循环中懒惰地使用

class Permutator<T> implements Iterable<List<T>> {

    final List<T> items;
    final int homMuch;

    Permutator(List<T> items, int homMuch) {
        this.items = items;
        this.homMuch = homMuch;
    }

    @Override
    public Iterator<List<T>> iterator() {
        return new Iterator<List<T>>() {
            static final int OVERFLOW = -1;
            final int[] permutation = new int[homMuch];
            final int max = items.size();

            @Override
            public boolean hasNext() {
                for (int item : permutation) {
                    if (item == OVERFLOW) {
                        return false;
                    }
                }
                return true;
            }

            @Override
            public List<T> next() {
                ArrayList<T> result = new ArrayList<>(permutation.length);
                for (int index : permutation) {
                    result.add(items.get(index));
                }

                inc(permutation, 0);           // generate next permutation

                return result;
            }

            private void inc(int[] indexes, int index) {
                if (index >= indexes.length) {
                    for (int i = 0; i < indexes.length; i++) {
                        indexes[i] = OVERFLOW;
                    }
                    return;
                }
                int nextValue = indexes[index] + 1;
                if (nextValue < max) {
                    indexes[index] = nextValue;
                } else {
                    indexes[index] = 0;
                    inc(indexes, index + 1);
                }
            }

        };
    }
}

输出:

List<String> list = Arrays.asList("one", "two");
int need = 3;
for (List<String> permutation : new Permutator<>(list, need)) {
    System.out.println(permutation);
}

答案 2 :(得分:0)

这是一种方法。我初始化行。然后我按列填充每一行。让它在右边迭代有点棘手,但没什么太难的。

public class GeneratePermutations {

  public static void main(String[] args) {
      int[] inputs = {1, 2, 3};
      List<int[]> results = permute(inputs, 4);
      printResults(results);
  }

  private static List<int[]> permute(int[] inputs, int size) {
      // set up the rows
      List<int[]> results = new ArrayList<int[]>();
      int count = (int)Math.pow(inputs.length, size);
      for (int row = 0; row < count; row++) {
          results.add(new int[size]);
      }

      // fill the rows by column
      for (int column = 0; column < size; column++) {
          permute(results, inputs, column);
      }

      return results;
  }

  private static void permute(List<int[]> results, int[] inputs, int column) {
      int inputIndex = 0;
        int input = inputs[inputIndex];

      // How often should we increment through the inputs?
      // If we use "column" as the exponent then we iterate more frequently
      // on the left (e.g. [1,1,1], [2,1,1], [1,2,1], etc.)
      // In order to iterate more right to left, use "size - column"
      // as the exponent.
      int incrIndex = 0;
      final int exponent = results.get(0).length - 1 - column;
      final int incrLength = (int)Math.pow(inputs.length, exponent);

      for (int[] result : results) {
          result[column] = input;

          // How often we step through the inputs depends on the column.
          incrIndex = (incrIndex + 1) % incrLength;
          if (incrIndex == 0) {
              inputIndex = (inputIndex + 1) % inputs.length;
              input = inputs[inputIndex];
          }
      }
  }

  private static void printResults(List<int[]> results) {
      for (int[] result : results) {
          System.out.println(Arrays.toString(result));
      }
  }
}