我指的是这个问题/答案: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岁时停留!
答案 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,但是这段代码已经达到我的预期。非常感谢你的帮助!