2017-04-24 65 views
1

我试图端口this Genetic Algorithm, ,我做了一个递归函数从一代推进到另一代。但是,因为我是C#中的新递归(一般情况下),当代数太多(大约4500)时,我显然遇到了StackOverflowException。优雅的方法来防止递归中的StackOverflow

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

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

Now在Main()中,为了使Generation()继续运行直到它返回true,我使用一个while循环,因此它将在递归开始之前一直运行,直到它完成。

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

这是一个很好的做法吗?有没有更好的方法来防止StackOverflows而不牺牲性能?

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); 


    } 
} 
+0

通常你不必保持递归方法运行 – EpicKip

+4

如果你得到了一个stackoverflow的点,你应该考虑改变方法到一个非递归的方法。在这种情况下,它实际上非常简单,因为算法基本上是“配对生成,变异代,过滤器生成,重复”。听起来更像是一个循环,而不是递归。事实上,这在性能方面比递归更好(如果正确实施)。 – Paul

回答

1

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

当然,做了这样的改变,现在还有很多其他的整洁你可以做。您不再需要重置堆栈,您不再需要if语句来检查生成限制,并且所有重复操作都是在while循环中完成的。

所以你的方法有两种:

注意的是,在tidyup我推出了一个名为hasCompleted,因为我觉得它更具可读性使用变量while条件,并希望有工作的一个布尔变量在循环内部。

+1

这很有道理。谢谢! –

0

我认为在这种情况下,你会过得更好preping while循环,并在数据发送你想要检查.Generation电话。那么如果它返回false,则更新数据。类似这样的:

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

这可以防止得到您所描述的错误的过深的嵌套调用。