Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

c# - Runtime exception, recursion too deep

I converted the pseudo-code here into C#, and have it recursively repeat 10,000 times. But I get a C# runtime error, StackOverflow Exception after 9217 times. How can I prevent this?

EDIT If it helps anybody, here's the code:

    private double CalculatePi(int maxRecursion)
    {
        return 2 * CalculatePi(maxRecursion, 1);
    }

    private double CalculatePi(int maxRecursion, int i)
    {
        if (i >= maxRecursion)
            return 1;
        return 1 + i / (2.0 * i + 1) * CalculatePi(maxRecursion, i + 1);
    }

    double pi = CalculatePi(10000); // 10,000 recursions

EDIT2 So everybody seems to agree that i need to convert this to iterative... can anybody give some code? I can't seem to write any iterative code that works...

EDIT Thanks to Paul Rieck for this answer, which I tested, and it works:

    private static double CalculatePi(int maxRecursion)
    {
        double result = 1;
        for (int i = maxRecursion; i >= 1; i-- )
        {
            result = 1 + i / (2.0 * i + 1) * result;
        }
        return result * 2;
    }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

So everybody seems to agree that i need to convert this to iterative... can anybody give some code? I can't seem to write any iterative code that works...

Are you asking for a fish, or asking to be taught how to catch fish? If the point of this exercise is to learn how to convert recursive code to iterative code then you're not well served by just getting the answer.

To convert recursive code to iterative code there are lots of possible ways to do it. The easiest way in this case is to simply work out the pattern. What does the code do? It computes:

(1 + 1 / (2.0 * 1 + 1)) * 
(1 + 2 / (2.0 * 2 + 1)) * 
(1 + 3 / (2.0 * 3 + 1)) * 
(1 + 4 / (2.0 * 4 + 1)) * 
(1 + 5 / (2.0 * 5 + 1)) * 
(1 + 6 / (2.0 * 6 + 1)) * 
(1 + 7 / (2.0 * 7 + 1)) * 
...
(1 + 9999/ (2.0 * 9999+ 1)) *
1

Now can you write a loop that computes that? Sure.

double product = 1.0;
for (int i = 9999; i >= 0; --i)
    product *= (1 + i / (2.0 * i + 1));

That's the easiest way to do it. But there are lots of ways to solve this problem.

You could use an aggregator. Consider the "total" operation; that's an example of an aggregator. You have a sequence of things and you keep a running total of them, accumulating the result in an accumulator. The standard query operators have an aggregation operation supplied for you:

double seed = 1.0;
Enumerable.Range(0, 10000).Aggregate(seed, 
    (product, i) => product * (1 + i / (2.0 * i + 1))

Or, you could keep your algorithm recursive but eliminate the stack overflow by:

  • building your own stack structure on the heap
  • defining a virtual machine, writing your program in the virtual machine language, and then implementing the virtual machine to keep its stack on the heap.
  • rewriting your program in Continuation Passing Style; every step along the way then returns a continuation to the caller, which invokes the next continuation; the stack never gets deep

Explaining those techniques takes too long for one answer. I have a six-part blog series on how to use those techniques to turn recursive programs into programs that don't consume much stack; start reading it here:

http://blogs.msdn.com/b/ericlippert/archive/2005/07/25/recursion-part-zero.aspx


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...