【问题标题】:How do I convert a doubly recursive method to a loop?如何将双递归方法转换为循环?
【发布时间】:2012-12-29 09:00:02
【问题描述】:

这是我简化的双重递归方法。它没有任何用处,但说明了所需的递归调用:

void Main()
{
    Test(2, 3, 4);
}

int n1 = 0;
int n2 = 0;

void Test(int i1, int i2, int v)
{
    if (v == 0)
    {
        (n1 + n2).Dump();
    }
    else
    {
        n1 = i1 + 10;
        n2 = i2 + 20;
        Test(n1, n2, v - 1);
        Test(n2, n1, v - 1);
    }   
}

我不知道如何将它写成一个循环来查看性能是否有所提高。

我已经更正了示例中明显的错误。

【问题讨论】:

  • 是的,使用迭代方法可以更快地完成。我的方法(在我的回答中)大约。使用 v=25 进行测试时速度提高 2 倍。

标签: c# algorithm recursion for-loop


【解决方案1】:

尝试这种迭代(无堆栈)方法:

int n1 = 0;
int n2 = 0;

void MyWay(int start1, int start2, int plus1, int plus2, int v)
{
    n1 = start2 + plus2 * v;
    n2 = start1 + plus1 * v;
    for (int i = 0, pow = 1 << v; i < pow; i++)
    {
        if ((i & 1) == 0)
        {
            int temp = n2;
            n2 = n1;
            n1 = temp;
        }
        for (int mask = i; mask > 0 && (mask & 1) == 0; mask >>= 1)
        {
            n1 += plus1;
            n2 += plus2;
        }
        Console.WriteLine(n1 + n2);
    }
}

应该被称为:

MyWay(2, 3, 10, 20, 4);

您示例中的所有常量都传递给函数,因此它是完全通用的。 此外,重要的事实是 函数完成后 n1 和 n2 的值将与递归方法完成后完全相同


关于性能:

当然,它会带来一些时间改进,但不会太多。但它会给出很大的输入参数。 此外,我的方法不会消耗额外的内存(递归和堆栈方法会消耗)。


关于性能的更新:

我已经在本地测试了您和我的方法 - 每次调用 1 000 000 次。结果如下:

递归:225.1774 毫秒

迭代:152.1194 毫秒


[更新]如果你在函数完成后不需要n1和n2,你可以缩短代码:

void MyWayTwo(int start1, int start2, int plus1, int plus2, int v)
{
    plus1 += plus2;
    int n = start1 + start2 + plus1 * v;
    for (int i = 0, pow = 1 << v; i < pow; i++)
    {
        for (int mask = i; mask > 0 && (mask & 1) == 0; mask >>= 1)
            n += plus1;
        Console.WriteLine(n);
    }
}

调用MyWayTwo(2, 3, 10, 20, 4);时的输出:

125
125
155
155
215
215
245
245
335
335
365
365
425
425
455
455

更简单的解决方案:

void MyWayTwo(int s1, int s2, int p1, int p2, int v)
{
    p1 += p2;
    s1 += s2;
    for (int i = 1 << v, p = i << 1; i < p; i++)
    {
        for (int m = i; (m & 1) == 0; m >>= 1)
            s1 += p1;
        Console.WriteLine(s1);
    }
}

【讨论】:

    【解决方案2】:

    任何可以递归完成的事情也可以使用堆栈完成。 假设您只想要您在示例中编写的功能:

    i1 和 i2 最终将被添加到全局变量 n1、n2 的总和中。您可以总结它们并将结果分配给代码开头的 n1 或 n2 以简化功能。使用堆栈,您可以执行以下操作:

    int n1 = 0;
    int n2 = 0;
    
    void Test2(int i1, int i2, int v)
    {
        Stack<int> s = new Stack<int>(new[] {v});
        n1 = i1 + i2;
    
        while (s.Any())
        {
            v = s.Pop();
            if (v == 0)
            {
                Console.Out.WriteLine(n1 + n2);
            }
            else
            {
                int tmp = n1;
                n1 = n2 + 10;
                n2 = tmp + 20;
                s.Push(v - 1);
                s.Push(v - 1);
            }
        }
    }
    

    其输出与递归代码相同:

    125
    155
    155
    215
    215
    245
    245
    335
    335
    365
    365
    425
    425
    455
    455
    

    【讨论】:

    • 太棒了!有用!只是一个问题......你是怎么想出 n1 = i1 + i2 的??
    • +1 我不确定它是否会获得任何性能提升,因为这是编译器通常实现递归调用的方式。不过不错的选择。
    • @Aditi i1 和 i2 从不单独使用,只使用它们的总和
    • 在没有堆栈的情况下也可以做到这一点。
    • 您可以使用Stack&lt;int&gt; 代替Stack(摆脱演员/装箱),然后使用while (s.Any()) 代替while (s.Count != 0)
    【解决方案3】:

    如果您只想使用最终结果,我可以给您此代码

    protected void TestIt(int i1, int i2, int v)
    {
        int tot = 0;
        for (; v > 0; v--)
            tot = tot * 2 + 30;
        Console.Out.WriteLine(tot + i1 + i2);
    }
    

    如果您需要中间结果,很遗憾,此代码无法提供给您。

    【讨论】:

      猜你喜欢
      • 2020-10-01
      • 2015-01-08
      • 2017-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多