在Recursion中防止StackOverflow的优雅方法

时间:2017-04-24 14:52:09

标签: c# algorithm recursion genetic-algorithm

我试图移植this Genetic Algorithm, 我做了一个递归函数,从一代传到另一代。

但是,由于我在C#(以及一般情况下)的递归新手,当有太多代(超过4500)时,我显然遇到了StackOverflowException。

为了解决这个问题,我让Generation()返回一个bool,所以当遗传算法达到最大适应度(目标)时,它返回true。否则它返回Generation()。

如果它即将溢出(生成> 4500),则返回false。

现在在Main()中,为了保持Generation()运行直到它返回true,我使用while循环,所以它将从递归开始直到它完成。

这比执行Task.Run更有效,所以我采用了这种方法。

这是好习惯吗?是否有更优雅的方法可以在不牺牲性能的情况下阻止StackOverflow?

Population.cs:

class Population
{

    public int GenerationNumber { get; private set; }
    public int TotalGenerationNumber { get; private set; }
    public const int StackGenerationLimit = 4500;

    public Population()
    {

        GenerationNumber = 0;
        TotalGenerationNumber = 0;


    }
    public bool Generation()
    {
        // Work
        // if(HasReachedGoal) return true;

        GenerationNumber++;


        if(GenerationNumber > StackGenerationLimit)
        {
            return false;
        } else
        {
            return Generation();
        }


    }

    public void ResetStack()
    {
        TotalGenerationNumber += GenerationNumber; // I store the total number of generation for information purposes
        GenerationNumber = 0; // Reset the recursion depth value
    }




}

Program.cs的

class Program
{
    static void Main(string[] args)
    {
        Population population = new Population();

        while (!population.Generation()) // Until it reaches its goal
        {
            population.ResetStack();
        }

        Console.WriteLine("End. Generations: " + population.TotalGenerationNumber);


    }
}

2 个答案:

答案 0 :(得分:1)

避免堆栈溢出的最佳方法是不使用递归。您的解决方法已经在答案的一半了。现在你只需要问问自己从递归中获得什么的问题了?如果return Generation();函数中的Generation语句改为return false;,那么您将返回主循环,再次调用Generation()

当然,做了这个改变之后,你可以做很多其他整洁的事情。您不再需要重置堆栈,不再需要if语句来检查生成限制,并且所有重复都是从while循环完成的。

所以你的两种方法:

public bool Generation()
{
    TotalGenerationNumber++;
    // Work
    return HasReachedGoal;
}

static void Main(string[] args)
{
    Population population = new Population();
    bool hasCompleted = false;
    while (!hasCompleted) // Until it reaches its goal
    {
        hasCompleted = population.Generation();
    }

    Console.WriteLine("End. Generations: " + population.TotalGenerationNumber);
}

请注意,在tidyup中,我引入了一个名为hasCompleted的bool变量,因为我发现在while条件中使用变量更具可读性,并且更喜欢在循环内部进行工作。

答案 1 :(得分:0)

我认为在这种情况下,最好先准备好while循环并将要检查的数据发送到.Generation调用中。然后,如果它返回false,则更新数据。像这样:

    Population population = new Population();
    var input = new InputDto() { ... };
    while (!population.Generation(input)) // Until it reaches its goal
    {
        // update input
    }

这可以防止过于嵌套的调用导致您描述的错误。