(Java)使用刷新到磁盘的N列表的排列

时间:2014-04-27 15:27:50

标签: java algorithm permutation

我指的是这个问题/答案:Permutation of N Lists(排列N个列表以创建搜索空间的所有组合)。 我为我的情况实现了这个,但是当我使用巨大的搜索空间(目前数百万或数十亿的组合,增加)时,问题是内存不足。 这是我目前的代码:

private void permute(List<List<T>> listOfLists, int index, Deque<T> tokens, boolean inMemory) {
    if (index == listOfLists.size()) {
        List<T> output = new LinkedList<>();
        for (T item : tokens) {
            output.add(item);
        }
        if (inMemory)
            results.add(output);
        else
            writeToDisk(output);

    } else {
        List<T> types = listOfLists.get(index);
        for (T l : types) {
            tokens.addLast(l);
            permute(listOfLists, index + 1, tokens, inMemory);
            tokens.removeLast();
        }
    }
}

我的案例中的'T'是Enum like

public enum LinkType implements Permutable { // Permutable -> Serializable Marker
    L1(0.95f, 20), L2(0.85f, 12), L3(0.75f, 8);

    protected final float reliability;
    protected final int cost;

    private LinkType(float reliability, int cost) {
        this.reliability = reliability;
        this.cost = cost;
    }

    public float getReliability() {
        return reliability;
    }

    public int getCost() {
         return cost;
    }
}

我当前的问题是对象被写入磁盘但仍然驻留在内存中,因为它是递归所必需的。有什么想法可以解决这个问题?增加堆大小不是(永久)解决方案,因为我的应用程序中的搜索空间肯定会随着时间的推移而增加。非常感谢你的帮助!

编辑: 我知道我永远不会达到超过25的值,但我不想在10岁时停留!

2 个答案:

答案 0 :(得分:2)

如果您不想存储所有排列,但只想有可能迭代所有排列,那么您可以扩展递归。考虑以下实现Iterator<int[]>接口的类:

public class IntPermutationsGenerator implements Iterator<int[]> {
    final int[] permutation;
    private boolean onFirst = true;
    private final int size;

    public IntPermutationsGenerator(int dimension) {
        permutation = new int[dimension];
        for (int i = 0; i < dimension; ++i)
            permutation[i] = i;
        this.size = dimension;
    }


    @Override
    public boolean hasNext() {
        return !isLast() || onFirst;
    }

    private boolean isLast() {
        for (int i = 0; i < size; i++)
            if (permutation[i] != size - 1 - i)
                return false;
        return true;
    }


    @Override
    public int[] next() {
        if (onFirst) {
            onFirst = false;
            return permutation;
        }
        final int end = size - 1;
        int p = end, low, high, med, s;
        while ((p > 0) && (permutation[p] < permutation[p - 1]))
            p--;
        if (p > 0) //if p==0 then it's the last one
        {
            s = permutation[p - 1];
            if (permutation[end] > s)
                low = end;
            else {
                high = end;
                low = p;
                while (high > low + 1) {
                    med = (high + low) >> 1;
                    if (permutation[med] < s)
                        high = med;
                    else
                        low = med;
                }
            }
            permutation[p - 1] = permutation[low];
            permutation[low] = s;
        }
        high = end;
        while (high > p) {
            med = permutation[high];
            permutation[high] = permutation[p];
            permutation[p] = med;
            p++;
            high--;
        }
        return permutation;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}

该类允许迭代所有排列而不存储它们:

public void main(String[] args) throws Exception {
    IntPermutationsGenerator g = new IntPermutationsGenerator(3);
    while (g.hasNext()){
        System.out.println(Arrays.toString(g.next()));
    }

    //gives
    //[0, 1, 2]
    //[0, 2, 1]
    //[1, 0, 2]
    //[1, 2, 0]
    //[2, 0, 1]
    //[2, 1, 0]
}

在上面的代码中,将在next()的调用时生成每个后续排列,而不存储所有其他排列。拥有这样的迭代器,您只需将int[]置换应用于您的数组或对象列表。

您还可以在redberry-core包中找到此类迭代器的实现,该包可从Maven Central获得(参见IntPermutationsGenerator)。

next()中实现的算法以字典顺序生成排列。您可以阅读有关此算法的更多信息,例如here

答案 1 :(得分:1)

这是我基于此Answer of Stanislav Poslavsky的解决方案。 我使用迭代器迭代一个域以获取排列的特定位置。 为了澄清,我添加了一个在特定位置具有最大值(边界)的int []。我计算到达到某个边界的极限,然后我继续下一个地方。

public class IntegerDomainPermutation implements Iterator<int[]> {
private int[] permutation;
private int[] domainBorders;
private List<List<Integer>> domain;
private boolean hasNext = true;

public IntegerDomainPermutation(List<List<Integer>> domain) {
    this.domain = domain;
    permutation = new int[domain.size()];
    domainBorders = new int[domain.size()];

    for (int i = 0; i < domain.size(); i++) {
        domainBorders[i] = domain.get(i).get(domain.get(i).size() - 1);
    }
    for (int i = 0; i < domain.size(); i++) {
        permutation[i] = domain.get(i).get(0);
    }
}

@Override
public boolean hasNext() {
    return hasNext;
}

@Override
public int[] next() {
    int[] perm = Arrays.copyOf(permutation, permutation.length);
    if(checkNext()){
        hasNext = true;
        increment();
    }else{
        hasNext = false;
    }
    return perm;
}

@Override
public void remove() {
    throw new UnsupportedOperationException("Not supported yet.");
}

public boolean checkNext(){
    for (int i = permutation.length - 1; i >= 0; i--) {
        if (!(permutation[i] == domainBorders[i])) {
            return true;
        }
    }
    return false;
}

private void increment() {
    for (int i = permutation.length - 1; i >= 0; i--) {
        if (!(permutation[i] == domainBorders[i])) {
            permutation[i] = domain.get(i).get(domain.get(i).indexOf(permutation[i]) + 1);
            return;
        } else {
            permutation[i] = domain.get(i).get(0);
        }
    }
}

}

最后,可以使用这样的代码:

public static void main(String[] args) {
    List<List<Integer>> domain = new LinkedList<>();
    List<Integer> place0 = new LinkedList<>(Arrays.asList(1,2));
    List<Integer> place1 = new LinkedList<>(Arrays.asList(3,7));
    List<Integer> place2 = new LinkedList<>(Arrays.asList(5,2));
    domain.add(place0);
    domain.add(place1);
    domain.add(place2);
    IntegerDomainPermutation perm = new IntegerDomainPermutation(domain);
    while(perm.hasNext())
        System.out.println(Arrays.toString(perm.next()));
}

这导致以下输出:

[1, 3, 5]
[1, 3, 2]
[1, 7, 5]
[1, 7, 2]
[2, 3, 5]
[2, 3, 2]
[2, 7, 5]
[2, 7, 2]

当然,还有改进的空间,如错误处理,用适当的类型(例如,集合或数组)或动态类型替换java.util.Lists,但是这段代码已经达到我的预期。非常感谢你的帮助!