Java 使用递归回溯时耗尽堆
我目前正在研究递归回溯这个美丽的主题。 我已经尝试过经典的例子,比如从迷宫中寻找最短路径,或者n皇后问题。但我现在正在研究的问题确实让我感到困惑: 实际上,我认为解决一个简单的拼图游戏可能是一个简单的练习: 我有一块大小为n=a*b的电路板,正好有那么多(n)块。Java 使用递归回溯时耗尽堆,java,algorithm,recursion,backtracking,Java,Algorithm,Recursion,Backtracking,我目前正在研究递归回溯这个美丽的主题。 我已经尝试过经典的例子,比如从迷宫中寻找最短路径,或者n皇后问题。但我现在正在研究的问题确实让我感到困惑: 实际上,我认为解决一个简单的拼图游戏可能是一个简单的练习: 我有一块大小为n=a*b的电路板,正好有那么多(n)块。 最后,我希望所有的棋子都按照一定的顺序放在棋盘上,它们遵守一定的限制(比如匹配它们的邻居)。相当简单,我想,我想出了以下算法: public board recursiveSolve(Board board, Piece[] piec
最后,我希望所有的棋子都按照一定的顺序放在棋盘上,它们遵守一定的限制(比如匹配它们的邻居)。相当简单,我想,我想出了以下算法:
public board recursiveSolve(Board board, Piece[] pieces, int position){
// base case
if(position == board.getLength())
return board;
else{
// For each possible piece
for(int i = 0; i < pieces.length; i++){
// if the piece is placeable at that position
// place it and search on recursively
if(board.isValid(piece[i], position)){
board.putPiece(pieces[i], position);
// THIS IS THE FISHY PART!!
// Now I need to pass a set of pieces to the function recursively
// that does not contain the current one (pieces[i])
// But I fear my (following) approach is too heap-intensive
Piece[] subPieces = new Piece[pieces.length - 1];
// fill subPieces with all elements from pieces except from the one
// at position i
for (int j = 0; j < subPieces.length; j++) {
if(j >= i)
subPieces[j] = pieces[j+1];
else
subPieces[j] = pieces[j];
}
if(recursiveSolve(board, subPieces, position + 1) != null)
return board;
else
board.removePiece(position);
}
}
// If this is reached, none of the pieces have worked -> go back
return null;
}
public board recursiveSolve(board-board,Piece[]pieces,int-position){
//基本情况
if(position==board.getLength())
返回板;
否则{
//对于每个可能的部分
对于(int i=0;i=i)
子部分[j]=件[j+1];
其他的
子部分[j]=件[j];
}
if(递归求解(电路板、子部件、位置+1)!=null)
返回板;
其他的
板。拆卸件(位置);
}
}
//如果达到该值,则所有部件均未工作->返回
返回null;
}
好的,基本上,这个算法做它应该做的-但不幸的是,它只适用于“小”板尺寸(n<100)。
因为如果我有一块10 x 10正方形和100块的板,函数会不断搜索,直到JVM因为堆空间不足而崩溃才结束。
我甚至尝试将eclipse的内存大小限制设置为1.2g,这使函数的工作时间更长,但仍然不够
所以我的问题是:是否有可能优化上述算法,使其适用于n>100的电路板尺寸?我做错了什么?或者我采取了完全错误的方法
非常感谢您提前提供的帮助。函数式语言将通过采用尾部递归来帮助您节省堆。不幸的是,JVM似乎不支持尾部递归(至少对于Java),请参阅
您可以尝试手动模拟尾部递归。因为电路板有一种方法可以告诉您工件[i]在某个位置是否有效,所以在继续之前迭代这些位置并尝试该位置的每个(剩余)工件是否更有意义?它不会使用递归(这将解决堆空间问题),但如果您特别追求递归解决方案,则显然不适合 为了更有效地做到这一点,我建议将这些片段放在一个列表中,然后在放置时删除一个片段。类似这样:
List<Piece> remainingPieces = new ArrayList<Piece>(Arrays.asList(pieces));
int numberOfPositions = // I assume you have some way of finding this.
for (int position = 0; position < numberOfPositions; position++) {
Iterator<Piece> it = remainingPieces.iterator();
while (it.hasNext()) {
Piece temp = it.next();
if (board.isValid(temp, position)) {
board.putPiece(temp, position);
it.remove();
break;
}
}
}
List remainingPieces=newarraylist(Arrays.asList(pieces));
int numberOfPositions=//我想你有办法找到这个。
对于(int position=0;position
在初始化新的size-pieces.length-1数组时,程序中的主要堆用法似乎确实是您怀疑的地方。
请注意,您确实可以在此处节省大量空间,因为您实际上只使用“最深”的集合
如果仍要使用数组,可能需要传递一个额外的参数:start
,并实现swap(arr,i,k)
,它交换arr中的第i个和第k个元素,在每个步骤中,而不是分配一个新数组,交换(片段,开始,i)
,并在递归步骤中传递到新函数start+1
。请注意,由于始终交换最后一个元素,因此接下来的步骤不关心交换,因为它们位于数组的start
位置之后。因此,基本上,由于算法从不“回头”,因此交换这些元素不会有任何问题在
应该是这样的:
public board recursiveSolve(Board board, Piece[] pieces, int position,int start){
if(position == board.getLength())
return board;
else {
//starting from start instead from 0
for(int i = start; i < pieces.length; i++){
if(board.isValid(piece[i], position)){
board.putPiece(pieces[i], position);
swap(pieces,start,i); //the swap() method I mentioned above
//sending start+1:
if(recursiveSolve(board, subPieces, position + 1,start+1) != null)
return board;
else
board.removePiece(position);
}
}
return null;
}
public board recursiveSolve(board-board,Piece[]pieces,int-position,int-start){
if(position==board.getLength())
返回板;
否则{
//从开始而不是从0开始
for(int i=开始;i
您可能知道回溯算法非常耗时[指数!],因此,即使使用空间优化版本,算法可能会运行很长时间,直到找到答案。除了语言之外,这应该适用于n>100,没有任何问题。.我在代码中看到一些奇怪的事情,比如你每次构建子块,手动移除电路板上的东西,这应该由ba自动完成也许有你