2017-04-24 4 views
1

포트 this Genetic Algorithm, 을 포트에 연결하려고 시도했으며 한 세대에서 다른 세대로 발전하기 위해 재귀 함수를 만들었습니다.재귀에서 StackOverflow를 방지하는 우아한 방법

그러나 C# (및 일반적으로)에서 재귀를 처음 접했을 때 분명히 너무 많은 세대 (약 4500 개가 넘었을 때)의 StackOverflowException이 발생했습니다.

문제를 해결하기 위해 Generation()에서 bool을 반환 했으므로 유전자 알고리즘이 최대 적합성 (목표)에 도달하면 true를 반환합니다. 그렇지 않으면 Generation()을 반환합니다.

오버플로가 발생하면 (생성> 4500) 거짓을 반환합니다.

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


    } 
} 
+0

일반적으로 재귀 메서드 실행을 유지하지 않아도됩니다. – EpicKip

+4

스택 오버 플로우가 발생하는 지점에 이르면 비 재귀 적 방법으로 변경하는 것이 좋습니다. 알고리즘은 기본적으로 "친구 생성, 돌연변이 생성, 필터 생성, 반복"이므로이 경우 실제로는 매우 간단합니다. 재귀보다 내 루프처럼 들린다.실제로 이것은 재귀 (제대로 구현 된 경우)보다 성능면에서 훨씬 좋습니다. – Paul

답변

1

스택 오버플로를 방지하는 가장 좋은 방법은 재귀를 사용하지 않는 것입니다. 해결 방법은 이미 답변의 절반 정도입니다. 이제 재귀를 통해 얻을 수있는 것에 대한 질문을 스스로에게 할 필요가 있습니다. Generation 함수의 return Generation(); 문이 return false;으로 변경된 경우 다시 Generation()이라고하는 메인 루프로 돌아갑니다.

물론이 변경을 한 후에는 현재 할 수있는 많은 깔끔한 업들이 많이 있습니다. 더 이상 스택을 재설정 할 필요가 없으며 생성 제한을 검사하는 if 문이 필요없고 모든 반복이 while 루프에서 수행됩니다.

그래서 두 가지 방법 : tidyup에 나는 내가있는 동안의 조건 변수를 사용하는 것이 더 읽기 찾아 일을 선호하기 때문에 hasCompleted라는 부울 변수를 도입 한 것을

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

주 루프 자체 내부.

+1

그것은 많은 의미가 있습니다. 감사! –

0

, 당신은 while 루프를 preping과 데이터 전송 더 나을 것이다 당신 .Generation 호출을 확인하고 싶습니다. false를 반환하면 데이터를 업데이트합니다. 다음과 같은 내용 :

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

이렇게하면 설명하는 오류가 발생하는 너무 심하게 중첩 된 호출을 방지 할 수 있습니다.