递归调用导致Java堆栈溢出。

huangapple go评论103阅读模式
英文:

Recursive calls causes Java Stack Overflow

问题

问题

你好,

我正在为我的大学课程做一个Java项目,遇到了一个我无法自己解决的问题,这是我第一次在这里提问,所以如果我做错了什么,请告诉我。

我正在尝试使用"墙随行"算法解决一个简单连通的迷宫问题,算法在迷宫中行走,并始终尝试向右转,然后前进,最后右转。当遇到死路时,算法会掉头并开始回退。

我的递归解决方案在迷宫尺寸超过 500 x 500 时会导致堆栈溢出错误。我想知道代码中是否存在错误,或者仅仅是使用递归算法解决这种问题在某些情况下不可行。

注意:问题不是算法不起作用,而是堆栈溢出!

问题

是否存在循环或内存泄漏,或者在一定数量的调用之后就无法继续使用递归。

我有一个迭代解决方案,适用于任何迷宫尺寸。

项目仓库

程序

迷宫被表示为一个二维数组,每个方块都有自己的类来保存它在迷宫中的坐标。我的想法是,Java 在递归方法运行时会保存这些 Square 对象而没有清理它们。move() 方法调用了一个叫做 getSquareInDirection 的方法,该方法创建了一个新的 Square 对象,这可能导致错误。其他方法仅修改枚举类型 Direction,不应该是错误的原因,但我不能确定。

父方法

public SquareQue solveRecursive() {
    Square start = maze.getStart();
    SquareQue path = new SquareQue();
    move(start, Direction.DOWN, path);

    maze.setSquareValue(start.getWidth(), start.getHeight(), 3);

    return path;
}

递归方法

public SquareQue move(Square pos, Direction dir, SquareQue path) {
    if (maze.reachedFinish(pos)) {
        path.add(pos);
        return path;
    }

    if (canMove(pos, getDirectionToRight(dir))) {
        pos = getSquareInDirection(pos, getDirectionToRight(dir));
        path.add(pos);
        return move(pos, getDirectionToRight(dir), path);

    } else if (canMove(pos, dir)) {
        pos = getSquareInDirection(pos, dir);
        path.add(pos);
        return move(pos, dir, path);

    } else if (canMove(pos, getDirectionToLeft(dir))) {
        pos = getSquareInDirection(pos, getDirectionToLeft(dir));
        path.add(pos);
        return move(pos, getDirectionToLeft(dir), path);

    } else {
        pos = getSquareInDirection(pos, reverse(dir));
        return move(pos, reverse(dir), path);
    }
}

递归方法调用了这个方法

public static Square getSquareInDirection(Square pos, Direction dir) {
    int col = pos.getWidth();
    int row = pos.getHeight();
    if (dir == Direction.UP) {
        return new Square(col, row - 1);
    }
    if (dir == Direction.DOWN) {
        return new Square(col, row + 1);
    }
    if (dir == Direction.RIGHT) {
        return new Square(col + 1, row);
    }
    if (dir == Direction.LEFT) {
        return new Square(col - 1, row);
    }
    return null;
}

重现错误

最简单的方法是克隆该项目并编辑 GUI 类。

在 GUI 类的 getSolveScene 方法中,更改按钮设置以调用大型递归结果的方法:

if (selectedString.equals("Large")) {
    stage.setScene(getSolveScene(81, 81, 12, setting));
    stage.setTitle("Maze generator - Recursive Large");
}

将 getSolveScene 替换为例如:(getSolveScene(501, 501, 12, setting)

在此之后,您可以保存并运行具有 "Large" 和 "Recursive" 设置的程序。

提前感谢您的回复!

英文:

Problem

Hello,

I'm doing Java project for my university studies and encountered a problem I could not solve myself, this is the first time I'm askin a question here so tell me if I did anything wrong.

I am trying to solve a simply connected maze using a Wall follower algorithm, the algorithm walks the maze and always trys to turn right then move forward and lastly turns right. Whe encountering a dead the algorithm it will turn around and start moving back.

My recursive soution causes a StackOverflow error when the maze size is above 500 x 500. I am wondering is there a error in the code or is it just not viable to solve this kind of problem with recursive algorithm.

NOTE: The problem is not that the algorithm does not work but that it runs out of Stack!

** Question **

Is there a loop or memory leak or is it just not possible to use recursion after a certain number of calls.

I have a iterative solution that works at any maze size.

Project repository

Program

the Maze is represented as a 2D-array and each square has its own class that holds it coordinates inside the maze. My thinking is that Java stores these Square objects without cleaning them while the recursive method is running. The move() method calls a method getSquareInDirection that creates a new Square object, that might cause the error. The other methods only alter the enum Dircetion and should not be the cause of the error, but I am not sure of this.

Parent method

    public SquareQue solveRecursive() {
        Square start = maze.getStart();
        SquareQue path = new SquareQue();
        move(start, Direction.DOWN, path);

        maze.setSquareValue(start.getWidth(), start.getHeight(), 3);

        return path;
    }

Recursive method

public SquareQue move(Square pos, Direction dir, SquareQue path) {
        if (maze.reachedFinish(pos)) {
            path.add(pos);
            return path;
        }

        if (canMove(pos, getDirectionToRight(dir))) {
            pos = getSquareInDirection(pos, getDirectionToRight(dir));
            path.add(pos);
            return move(pos, getDirectionToRight(dir), path);

        } else if (canMove(pos, dir)) {
            pos = getSquareInDirection(pos, dir);
            path.add(pos);
            return move(pos, dir, path);

        } else if (canMove(pos, getDirectionToLeft(dir))) {
            pos = getSquareInDirection(pos, getDirectionToLeft(dir));
            path.add(pos);
            return move(pos, getDirectionToLeft(dir), path);

        } else {
            pos = getSquareInDirection(pos, reverse(dir));
            return move(pos, reverse(dir), path);
        }
    }

Recrsuvie method calls this

public static Square getSquareInDirection(Square pos, Direction dir) {
        int col = pos.getWidth();
        int row = pos.getHeight();
        if (dir == Direction.UP) {
            return new Square(col, row - 1);
        }
        if (dir == Direction.DOWN) {
            return new Square(col, row + 1);
        }
        if (dir == Direction.RIGHT) {
            return new Square(col + 1, row);
        }
        if (dir == Direction.LEFT) {
            return new Square(col - 1, row);
        }
        return null;
    }

Reproduce error

Easiest way to reproduce the error is to clone the project and edit the GUI class.

Inside GUI class inside the method getSolveScene and alter the button settings for calling a large recursive result
method:

if (selectedString.equals("Large")) {
                    stage.setScene(getSolveScene(81, 81, 12, setting));
                    stage.setTitle("Maze generator - Recursive Large");

                }

Repalce the getSolveScene with for example: (getSolveScene(501, 501, 12, setting).

After this you can save and run the progra with settings Large and Recursive.

Thanks in advance for responding!

答案1

得分: 1

递归方法在堆栈大小足够容纳所需的递归深度时应该是可行的(这取决于迷宫的大小)。

然而,我还看到另一个问题 - getSquareInDirection 似乎没有检查是否避免返回到已经访问过的路径。如果发生这种情况,您的程序可能会陷入“循环”即无限递归中。尝试在 move 中打印当前坐标,并查看它们是否重复。(提示:它们不应该重复)。

英文:

Recursive approach should work as long as your stack size is large enough to accomodate the required depth of recursion (that depends on the size of your maze).

However, I also see another problem - getSquareInDirection doesn't seem to be checking not to go back to the path that was already visited. If that happens, your program might get stuck in a "loop" a.k.a. infinite recursion. Try printing current coordinates in move and see if they repeat themselves. (Hint: they should not)

答案2

得分: 0

默认堆栈大小相当小(对于递归算法来说通常太小了)。在大多数虚拟机中,默认值只有1兆字节。

要更改堆栈大小,请使用虚拟机参数:-Xss

但是要检查堆栈大小是否是您唯一的问题,请先尝试解决一个较小的迷宫。

英文:

The default stack size is quite small (at least often too small for recursive algorithms). Default is in most VMs only 1MByte.

To change the stack size use the VM-parameter: -Xss

But to check if the stack size is your only problem, try to solve a smaller maze first.

huangapple
  • 本文由 发表于 2020年8月28日 21:02:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/63634322.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定