你如何最好地将递归函数转换为迭代函数?

时间:2014-10-31 19:28:29

标签: java recursion

这个问题基于我在compsci课上的测试。特别是,我正在努力转换这个功能:

public static void foo(int number)  {
    if (number > 0) {
        foo(number / 2);
        System.out.print(number % 2);
    }
}

我需要将此函数转换为非递归函数,但我正在努力解决它,因为System.out.print(number % 2)在递归调用之后发生。

6 个答案:

答案 0 :(得分:5)

您可以随时模拟堆栈,但在很多情况下,您可以将其转换为完全无堆栈的解决方案。 (我不是100%肯定,但我认为只有primitive recursive functions才能实现无堆栈转换。我看不出像Ackermann function这样的东西可以在没有某种堆栈的情况下计算出来。)< / p>

无论如何,对于实践中的大多数情况(以及课堂中的所有情况),都可以找到一种方法。在这里我们可以使用反击技巧:

public static void foo(int number)  {
    for ( int divisor = 1; divisor <= number; divisor *= 2) {
      System.out.print( (number / divisor) % 2 );
    }
}

<强>更新 转换这样的简单函数最简单实用的方法是运行它,在每次迭代后写下输出,然后完全忘记递归,忘记原始代码,单独查看输出并问自己:这是什么代码做什么?然后尝试编写产生相同输出的迭代代码。这项技术在uni上很有用。但它并不总是在现实生活中起作用。 :)

答案 1 :(得分:4)

关于这个问题的另一个观点,因为你已经有了很多答案。

解决递归问题的全覆盖方法是使用堆栈。但是,如果您确切知道要解决的问题,可以找到替代解决方案。或者可能不完全是另类 - 只是一个更紧凑的。

在这个例子中,对于大于零的整数,该函数为您提供参数的二进制表示。

您可能想要使用:

   public static void foo(int number) {

       if ( number > 0 ) {
           System.out.print( Integer.toBinaryString(number) );
       }

   }

但是,当然,这可能只会让你在测试中获得大胆的分数。

所以这里是对Java实际执行此操作的改编:

public static void foo(int number) {

    if (number > 0) {
        char[] chars = new char[32];
        int charPos = 32;
        char[] digits = { '0', '1' };

        do {
            chars[--charPos] = digits[number % 2];
            number /= 2;
        } while (number > 0);

        System.out.print(new String(chars, charPos, 32 - charPos));
    }
}
事实上,它是使用堆栈,但不是非常复杂的堆栈。堆栈只是一个字符数组,你从头开始填充,然后开始。您可以使用此类数组而不是Collection,因为已知事实int包含的位数不超过32位。所以你永远不会用完数组的边界。说实话,因为你只使用正数,它甚至可以是一个31个字符的数组。

因此,在每个回合中,您将当前数字放在数组中的当前空位置,并将索引向左移动。最后,使用String的构造函数将收集的所有字符转换为字符串,方便地,可以使用字符数组的指定部分。

Java使用的实际运算符有点不同:

        do {
            chars[charPos--] = digits[number & 1];
            number >>>= 1;
        } while (number != 0);

因为移位和掩蔽是比divison更有效的操作。如果您使用此版本,则与使用Integer.toBinaryString()一样高效。

答案 2 :(得分:3)

您可以使用Deque来跟踪需要打印的内容

public static void foo(int number)  {
    Deque<Integer> stack = new ArrayDeque<Integer>();
    while (number > 0) {
        stack.push(number);
        number = number/2;
    }
    //iterate over the stack...
    while(!stack.isEmpty()){ 
        Integer myInt = stack.pop();
         //your code here
    }
}

答案 3 :(得分:3)

也许是prepending到一个字符串?

public static void foo(int number) {
    String r = "";
    while(number > 0) {
        r = (number%2)+r;
        number = number/2;  
    }
    System.out.print(r);
}

答案 4 :(得分:2)

使用堆栈模拟递归的最佳方法,并使用push和pop,因为这就是递归的工作原理:

public static void foo2(int number)
{ 
    Stack st = new Stack();
   for(int i=number;i>0;i=i/2)
   {
       st.push(i%2);
   }
   while(!st.empty())
   {
       System.out.print(st.pop());
   }
}

答案 5 :(得分:1)

recursion-elimination的通用方法是使用Stack复制编译器performs recursion的方式。此方法可用于将任何递归程序转换为非递归程序。

我们使用Stack的方式是存储与每个递归调用相对应的不同stack frames。每个堆栈帧都需要以某种方式在代码执行期间跟踪其位置。我们区分这些堆栈帧(即主要执行步骤)越好,我们的解决方案就越简单。

对于递归函数,每个堆栈帧可以分为两个主要执行部分:


A)检查是否number is > 0并致电foo作为参数传递number / 2

B)打印number % 2


或在代码中:

// all marked A are a single "step"
public static void foo(int number)  {
    if (number > 0) {                   //   A  
        foo(number / 2);                //   A 
        System.out.print(number % 2);   //   B
    }
}

因此,让我们创建一个StackFrame类来复制它:

static class StackFrame {
    int number;
    char nep;   // Next Execution Position ('A' or 'B')
    StackFrame(int number, char nep) {
        this.number = number;
        this.nep = nep;
    }
}

nep变量用于存储每个StackFrame的程序执行中的下一个位置。

该计划将处理执行部分AB

A)程序使用Stack StackFrames并在每次复制递归调用时按StackFramenumber > 0条件StackFrames } 是真的。 (这将是递归中的基本情况

B)一旦达到这个条件(基本情况),我们就可以像编译器一样开始弹出堆栈的number % 2并打印我们想要的值(public static void iterative(int number) { Stack<StackFrame> stack = new Stack<StackFrame>(); stack.push(new StackFrame(number, 'A')); while(!stack.isEmpty()) { // run until we have stack frames StackFrame top = stack.peek(); switch(top.nep) { // determine next execution step case 'A': top.nep = 'B'; // set next execution step if(top.number / 2 > 0) { // check base case and push current stack frame if base case is true stack.push(new StackFrame(top.number / 2, 'A')); } break; case 'B': System.out.print(top.number % 2); stack.pop(); // end current stack frame break; } } } )。

以下是这种方法的外观:

number = 211

Here is a full sample program

Recursive output: 11010011 Iterative output: 11010011 的示例程序输出:

{{1}}

以下是一些用于递归消除的great visual examples