将递归转换为循环

时间:2014-07-01 16:30:37

标签: c# recursion

下面的C#代码计算一组点的最小封闭圆,但由于它的递归实现而导致堆栈溢出异常。

enter image description here

它是从a paper中提取的,它将算法指定为递归。

该算法尾递归,并且它不能轻易转换为循环结构。

递归函数是

public static Circle MiniDiskImpl(IReadOnlyList<Point2D> points, List<Point2D> boundary)
{
    if (!points.Any() || boundary.Count == 3)
    {
        if (boundary.Count == 0)
            return null;

        if (boundary.Count == 1)
            return null;

        if (boundary.Count == 2)
        {
            var radius = boundary[0].DistanceTo(boundary[1])/2;
            var center = (boundary[0] + boundary[1])*0.5;
            return new Circle(Plane.XY, center, radius);
        }

        return new Circle(Plane.XY,boundary[0],boundary[1],boundary[2]);
    }

    var p = points[0];
    var Q = points.GetRange(1,points.Count - 1);
    var D = MiniDiskImpl(Q, boundary);

    if (D==null || D.Center.DistanceTo(p) >= D.Radius)
    {
        D = MiniDiskImpl(Q, boundary.Concat(p).ToList());
    }

    return D;
}

用户调用此函数。

public static Circle MiniDisk(List<Point2D> points)
{
    points = points.Slice(0); // Clone the list so we can manipulate in place
    points.Shuffle(); // sorted points cause poor algorithm performance
    return MiniDiskImpl(points, new List<Point2D>());
}

问题是,使算法非递归所需的转换是什么?

2 个答案:

答案 0 :(得分:0)

当你使用递归时,你使用调用堆栈来执行某些操作,你总是可以通过将自己的对象放到实际的堆栈中将递归算法转换为它的迭代版本,请参阅wiki上的DFS示例:{{3} }

答案 1 :(得分:0)

我对以下内容感到惊讶。

/// <summary>
/// Find the smallest enclosing circle. See reference
/// http://www.inf.ethz.ch/personal/emo/PublFiles/SmallEnclDisk_LNCS555_91.pdf
/// "Smallest enclosing disks (balls and ellipsoids) EMO WELZL
/// </summary>
/// <returns></returns>
public static class Circles 
{

    private static Return<Circle> Left
        (List<Point2D> points, List<Point2D> boundary)
    {

        if ( !points.Any() || boundary.Count == 3 )
        {
            if ( boundary.Count <= 1 )
            {
                return Return.Value<Circle>(null);
            }

            if ( boundary.Count == 2 )
            {
                var radius = boundary[0].DistanceTo(boundary[1]) / 2;
                var center = ( boundary[0] + boundary[1] ) * 0.5;
                return Return.Value(new Circle(Plane.XY, center, radius));
            }

            return Return.Value(new Circle(Plane.XY, boundary[0], boundary[1], boundary[2]));
        }

        var p = points[0];
        var q = points.GetRange(1, points.Count - 1);

        return from circle in Return.Call(() => Left(q, boundary))
               from result in 
                   (circle == null || circle.Center.DistanceTo(p) >= circle.Radius)
                           ? Return.Call(() => Left(q, boundary.Concat(p).ToList()))
                           : Return.Value(circle)
               select result;
    }

    public static Circle MiniDisk( List<Point2D> points )
    {
        points = points.Slice(0);
        points.Shuffle();
        return TrampolineRecursion.Do(()=>LeftLeft(points, new List<Point2D>()));
    }

}

使用蹦床monad的以下实现,我只是疯了。

namespace Weingartner.Numerics.Recursion
{
    public class Return<T>
    {
        #region return value mode
        public bool HasReturnValue;
        public T ReturnValue;
        #endregion

        #region call and continue mode
        public Func<Return<T>> Call;
        public Func<T, Return<T>> Continue;
        #endregion

        /// <summary>
        /// To avoid GC pressure we use a thread static variable
        /// to handle the return value from functions.
        /// </summary>
        [ThreadStatic] private static Return<T> _ReturnRegister;

        public static Return<T> R()
        {
            if (_ReturnRegister == null)
            {
                _ReturnRegister = new Return<T>();
            }
            return _ReturnRegister;
        }

        public Return<T> Bind(Func<T, Return<T>> fn)
        {
            if (HasReturnValue)
                return Return.Call(() => Return.Value(ReturnValue), fn);

            if(Continue!=null)
                return Return.Call(Call, t => Continue(t).SelectMany(fn));

            return Return.Call(Call, fn);
        }

        public Return<T> SelectMany(Func<T, Return<T>> fn)
        {
            return Bind(fn);
        }
        public Return<T> SelectMany( Func<T, Return<T>> f, Func<T, T, T> g)
        {
            return Bind(x => f(x).Bind(y => Return.Value(g(x, y))));
        }

        public Return<T> Select(Func<T, T> fn)
        {
            if (HasReturnValue)
                return Return.Value(fn(ReturnValue));

            if(Continue!=null)
                return Return.Call(Call, t => Continue(t).Select(fn));

            return Return.Call(Call, t => Continue(t).Select(fn));
        }

        public T Run()
        {

            var stack = new Stack<Func<T,Return<T>>>();
            if(Continue!=null)
                stack.Push(Continue);
            stack.Push(r => Call());

            var @return = Return.Value(default(T));

            while ( stack.Count > 0 )
            {
                @return = stack.Peek()(@return.ReturnValue);
                stack.Pop();
                if (!@return.HasReturnValue)
                {
                    if(@return.Continue!=null)
                        stack.Push(@return.Continue);
                    var t = @return;
                    stack.Push(r => t.Call());
                }
            }

            return ReturnValue = @return.ReturnValue;
        }
    }

    public class Return
    {
        public static Return<T> Value<T>( T t )
        {
            var r = Return<T>.R();
            r.HasReturnValue = true;
            r.ReturnValue = t;
            return r;
        }

        public static Return<T> Call<T>
            (Func<Return<T>> call, Func<T, Return<T>> @continue = null)
        {
            var r = Return<T>.R();
            r.HasReturnValue = false;
            r.Call = call;
            r.Continue = @continue;
            return r;
        }

    }

    public static class TrampolineRecursion
    {
        public static T Do<T>(Func<Return<T>> call)
        {
            return Return.Call(call).Run();
        }
    }
}