Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate a list of arrays (all have the length M) with N possible elements (M > N) in Java?

Tags:

java

For example, if elements are {1, 2} (n = 2) and m = 3, the method should generate a list of arrays like this {[1,1,1],[1,1,2],[1,2,1],[2,1,1],[1,2,2],[2,2,1],[2,1,2],[2,2,2]}.

I know Python can do things like y = itertools.product((1, 2), repeat=3), but how do I implement this in Java efficiently. I have tried feed some initial List and used the following to get what I wanted but the time complexity is too high and performance is extremely bad when the input is big.

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);
        }
    }
}
like image 906
Tao Zhang Avatar asked Jul 24 '16 12:07

Tao Zhang


2 Answers

In fact you can't reduce the complexity. The minimum number of operations you have to do is the creation of your Lists. The number of lists can't be reduced (it's always equal to n^m) and the creation of these lists is the thing which takes a lot of time during the execution.

I added my code which I used to make some tests if it could help you.

//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;
}`
like image 194
Quentin Seiwert Avatar answered Oct 26 '22 12:10

Quentin Seiwert


Using libary StreamEx solution could look like:

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

you could gain efficiency using parallel processing or by consuming only part of lazily generated result.


Another lazy solution in plain old java would create Iterator that produce permutations lazily

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

        };
    }
}

such generator can be used lazily in loops

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

Output:

[one, one, one]
[two, one, one]
[one, two, one]
[two, two, one]
[one, one, two]
[two, one, two]
[one, two, two]
[two, two, two]
like image 43
Nazarii Bardiuk Avatar answered Oct 26 '22 12:10

Nazarii Bardiuk