C# 解决堆栈大小问题的方法

C# 解决堆栈大小问题的方法,c#,stack-overflow,C#,Stack Overflow,我在c#中有一个递归阶乘函数。我用它来处理biginger。当我想处理大整数时,问题就出现了,因为我的函数是递归的,所以它会导致堆栈溢出异常。现在,简单的解决方案是不使函数递归。我想知道是否有办法解决这个问题?我想在堆栈中分配更多的ram BigInteger Factorial(BigInteger n) { return n == 1 ? 1 : n * Factorial(n - 1); } 您可以创建一个具有所需堆栈大小的新线程 var tcs = new TaskComple

我在c#中有一个递归阶乘函数。我用它来处理
biginger
。当我想处理大整数时,问题就出现了,因为我的函数是递归的,所以它会导致
堆栈溢出
异常。现在,简单的解决方案是不使函数递归。我想知道是否有办法解决这个问题?我想在堆栈中分配更多的ram

BigInteger Factorial(BigInteger n)
{
    return n == 1 ? 1 : n * Factorial(n - 1);
}

您可以创建一个具有所需堆栈大小的新线程

var tcs = new TaskCompletionSource<BigInteger>();
int stackSize = 1024*1024*1024;

new Thread(() =>
    {
        tcs.SetResult(Factorial(10000));
    },stackSize)
    .Start();

var result = tcs.Task.Result;
var tcs=new TaskCompletionSource();
int stackSize=1024*1024*1024;
新线程(()=>
{
tcs.SetResult(阶乘(10000));
},堆栈大小)
.Start();
var result=tcs.Task.result;

但是正如在评论中提到的,使用迭代的方法会更好。

我理解,如果可以用c#表达递归函数,而不必担心堆栈,那就太好了。但不幸的是,这是不可能直接实现的,无论堆栈有多大,总会出现堆栈空间不足的情况。此外,你的表现可能会非常糟糕。如果你有一个像这样的尾部递归函数,你可以做一些事情,这可以让你用原始的递归方式来表达你的函数,而不需要付出巨大的代价

不幸的是,c#不直接支持尾部递归调用,但是可以使用所谓的“蹦床”结构来解决问题

参见示例:和

来自上一篇博客的以下代码将允许您作为尾部递归函数执行阶乘,而不会出现堆栈问题

public static class TailRecursion
{
    public static T Execute<T>(Func<RecursionResult<T>> func)
    {
        do
        {
            var recursionResult = func();
            if (recursionResult.IsFinalResult)
                return recursionResult.Result;
            func = recursionResult.NextStep;
        } while (true);
    }

    public static RecursionResult<T> Return<T>(T result)
    {
        return new RecursionResult<T>(true, result, null);
    }

    public static RecursionResult<T> Next<T>(Func<RecursionResult<T>> nextStep)
    {
        return new RecursionResult<T>(false, default(T), nextStep);
    }

}

public class RecursionResult<T>
{
    private readonly bool _isFinalResult;
    private readonly T _result;
    private readonly Func<RecursionResult<T>> _nextStep;
    internal RecursionResult(bool isFinalResult, T result, Func<RecursionResult<T>> nextStep)
    {
        _isFinalResult = isFinalResult;
        _result = result;
        _nextStep = nextStep;
    }

    public bool IsFinalResult { get { return _isFinalResult; } }
    public T Result { get { return _result; } }
    public Func<RecursionResult<T>> NextStep { get { return _nextStep; } }
}

class Program
{
    static void Main(string[] args)
    {
        BigInteger result = TailRecursion.Execute(() => Factorial(50000, 1));
    }

    static RecursionResult<BigInteger> Factorial(int n, BigInteger product)
    {
        if (n < 2)
            return TailRecursion.Return(product);
        return TailRecursion.Next(() => Factorial(n - 1, n * product));
    }
}
公共静态类递归
{
公共静态T执行(Func Func)
{
做
{
var recursionResult=func();
if(recursionResult.IsFinalResult)
返回recursionResult.Result;
func=recursionResult.NextStep;
}虽然(正确);
}
公共静态递归结果返回(T结果)
{
返回新的RecursionResult(true、result、null);
}
公共静态递归结果下一步(Func nextStep)
{
返回新的递归结果(false,默认值(T),下一步);
}
}
公共类递归结果
{
私有只读bool_isFinalResult;
私有只读T_结果;
私有只读功能下一步;
内部递归结果(bool是finalresult,T result,Func nextStep)
{
_isFinalResult=isFinalResult;
_结果=结果;
_下一步=下一步;
}
公共bool IsFinalResult{get{return\u IsFinalResult;}}
公共T结果{get{return_Result;}}
public Func NextStep{get{return{u NextStep;}}
}
班级计划
{
静态void Main(字符串[]参数)
{
biginger result=TailRecursion.Execute(()=>Factorial(50000,1));
}
静态递归结果阶乘(int n,BigInteger乘积)
{
if(n<2)
return TailRecursion.return(产品);
返回TailRecursion.Next(()=>Factorial(n-1,n*product));
}
}

计算阶乘不需要堆栈;既不是调用堆栈也不是堆栈。为什么坚持要有一个?工具不对。它将阶乘从O(1)空间转化为O(n)空间。您需要对尾部调用进行优化,以使用递归在O(1)中实现它,而C#没有提供递归。如果您将代码转换为尾部调用形式,则迭代解决方案已经完成一半。递归算法仅适用于复杂度不超过O(log(n))或深度受合理数字限制的情况。两者都不适用。这不是一个真正需要解决的问题,Calc.exe也完成了这项工作。