简体   繁体   English

递归到迭代 - 到C的方案

[英]Recursion to Iteration - Scheme to C

Can somewone help me convert this scheme function: 有人可以帮我转换这个方案功能:

#lang racket
(define (powmod2 x e m)    
  (define xsqrm (remainder (* x x) m))    
  (cond [(= e 0) 1]    
        [(even? e) (powmod2 xsqrm (/ e 2) m)]    
        [(odd? e) (remainder (* x (powmod2 xsqrm (/ (sub1 e) 2) m)) m)]))

Into a function in C, and don't use recursion ie use iteration. 进入C中的函数,不使用递归即使用迭代。

I'm out of ideas', the part that is bothering me is when e is odd and then the recursive call is in the remainder function. 我没有想法',困扰我的部分是当e是奇数然后递归调用在余数函数中。 I dont know how to transfer that in a while loop? 我不知道如何在while循环中传输它? any tips or suggestions: 任何提示或建议:

This is what i have so far: 这是我到目前为止:

int powmod2(int x, int e, int m) {
    int i = x;
    int xsqrm = ((x * x) % m);
    while (e != 0){
        if (e%2 == 0) {
            x = xsqrm;
            e = (e/2);
            xsqrm = ((x * x) % m);
        }
        else {
            i = x; 
            x = xsqrm;
            e = (e - 1)/2;
            xsqrm = ((x * x) % m); 

        }
    }
    e = 1;
    return (i*e)%m;

}

The even version is straightforward because the code has been written tail recursively so the call to (powmod2 xsqrm (/ e 2) m) can be expressed iteratively by replacing e with half of e and x with its square modulo m: 偶数版本是直截了当的,因为代码已经递归写入,所以对(powmod2 xsqrm (/ e 2) m)的调用可以通过用e和x的一半替换e以其方模m来迭代表示:

int powmod2(int x, int e, int m) { /* version for when e is a power of 2 */
  while ((e /= 2) != 0)
    x = (x * x) % m;
  return x;
}

However the odd version has not been written tail recursively. 然而奇数版本没有递归写入尾部。 One approach is to create a helper method that uses an accumulator. 一种方法是创建一个使用累加器的辅助方法。 This helper method can then be written tail recursively for both even and odd exponent. 然后可以针对偶数和奇数指数递归写入该辅助方法。 You can then transform that into an iteration. 然后,您可以将其转换为迭代。

You are having trouble doing the conversion because the original scheme code is not tail recursive. 您在进行转换时遇到问题,因为原始方案代码不是尾递归的。 Try to add extra parameters to powmod2 so that you do not need to do the multiplication by remainder in the odd case after calling the recursive function. 尝试向powmod2添加额外的参数,这样在调用递归函数后,您不需要在奇数情况下乘以余数。

To illustrate, its hard to loopify the following function 为了说明,它很难循环以下功能

int fac(n){
    if(n == 0) {
        return 1;
    }else{
        return n * fac(n-1)
    }
}

But it is easy to loopify the version with an accumulation parameter 但是使用累积参数来循环化版本很容易

int fac(n, res){
    if(n == 0) {
        return res;
    }else{
        return fac(n-1, res*n)
    }
}

int real_fac(n){ return fac(n, 1); }

Perhaps if you were to run the algorithm with some values to see how the result is calculated, it can help you figure out how to convert it. 也许如果您使用某些值运行算法来查看结果的计算方式,它可以帮助您找出如何转换它。 Let's see a single run for x=5 , e=5 and m=7 : 让我们看一下x=5e=5m=7的单次运行:

1. x=5, e=5, m=7
   xsqrm=4
   e:odd => res = 5*...%7
2. x=4, e=2, m=7
   xsqrm=2
   e:even => res = ...%7
3. x=2, e=1, m=7
   xsqrm=4
   e:odd => res = 2*...%7
4. x=4, e=0, m=7
   e==0 => res = 1

res = 5*2%7=3

At step 1, we get a partial calculation for the result: it is 5 times the result of next step mod 7. At step 2, since it is even the result is the same as the result of the next step. 在步骤1,我们得到结果的部分计算:它是下一步模7的结果的5倍。在步骤2,因为它是偶数,结果与下一步的结果相同。 At step 3, we've got something similar to step 1. The result we'll feed upstairs is calculated by multiplying next result by 2 (mod 7 again). 在第3步,我们得到类似于第1步的结果。我们将上楼的结果通过将下一个结果乘以2(mod 7再次)来计算。 And at termination, we've got our result to feed upstairs: 1. Now, as we go up, we just know how to calculate res: 2*1%7 for step 3, 2 for step 2, and 2*5%7 for step 1. 在终止时,我们将结果送到楼上:1。现在,当我们上升时,我们只知道如何计算res:步骤3为2*1%7 ,步骤22*5%7为步骤1。

One way to implement it is to use a stack. 实现它的一种方法是使用堆栈。 At every partial result, if the exponent is odd, we can push the multiplication factor to the stack, and once we terminate, we can just multiply them all. 在每个部分结果中,如果指数是奇数,我们可以将乘法因子推到堆栈,一旦我们终止,我们就可以将它们全部相乘。 This is the naive/cheating method for conversion. 这是转换的天真/欺骗方法。

There is a more efficient way that you should be able to see when you look at the steps above. 当您查看上述步骤时,您应该能够看到更有效的方法。 Also other answers about converting everything to tail recursive is a very good hint. 关于将所有内容转换为尾递归的其他答案也是一个非常好的提示。

The easiest way is to reason what is the original function trying to compute? 最简单的方法是推断尝试计算的原始函数是什么? This is the value of x to the power e module m . 这是功率e模块mx值。 If you express e in binary, you can get e = e0 * 1 + e1 * 2 + e2 * 4 + e3 * 8 + ... , where en is either 0 or 1. And x^n = x * e0 + x ^ 2 * e1 + x ^ 4 * e2 + x ^ 8 * e3 + ... . 如果用二进制表示e ,你可以得到e = e0 * 1 + e1 * 2 + e2 * 4 + e3 * 8 + ... ,其中en是0或1.并且x^n = x * e0 + x ^ 2 * e1 + x ^ 4 * e2 + x ^ 8 * e3 + ...

By using the mathematical properties of the modulo operator, ie. 通过使用模运算符的数学属性,即。 (a + b) % m = ((a % m) + (b % m)) % m and (a * b) % m = ((a % m) * (b % m)) % m , we can then rewrite the function as: (a + b) % m = ((a % m) + (b % m)) % m(a * b) % m = ((a % m) * (b % m)) % m ,我们可以然后将函数重写为:

int powmod2(int x, int e, int m) {
  // This correspond to (= e 0)
  int r = 1;
  while (e != 0) {
    if (e % 2) {
      // This correspond to (odd? e)
      r = (r * x) % m;
    }
    // This correspond to the recursive call
    // that is done whatever the parity of e.
    x = (x * x) % m;
    e /= 2;
  }
  return r;
}

The first step would be writing the original Scheme procedure as a tail recursion. 第一步是将原始Scheme过程写为尾递归。 Notice that this rewrite works because of the properties of modular arithmetic: 请注意,由于模块化算法的属性,此重写可以正常工作:

(define (powmod2 x e m)
  (define (iter x e acc)
    (let ((xsqrm (remainder (* x x) m)))
      (cond ((zero? e) acc)
            ((even? e) (iter xsqrm (/ e 2) acc))
            (else (iter xsqrm (/ (sub1 e) 2) (remainder (* x acc) m))))))
  (iter x e 1))

The key element of the above procedure is that the answer is passed in the acc parameter. 上述过程的关键要素是在acc参数中传递答案。 Now we have a tail recursion, after that the conversion to a fully iterative solution is pretty straightforward: 现在我们有一个尾递归,之后转换为完全迭代的解决方案非常简单:

int powmod2(int x, int e, int m) {
    int acc = 1;
    int xsqrm = 0;
    while (e != 0) {
        xsqrm = (x * x) % m;
        if (e % 2 == 0) {
            x = xsqrm;
            e = e / 2;
        }
        else {
            acc = (x * acc) % m;
            x = xsqrm;
            e = (e - 1) / 2;
        }
    }
    return acc;
}

It can be optimized further, like this: 它可以进一步优化,如下所示:

int powmod2(int x, int e, int m) {
    int acc = 1;
    while (e) {
        if (e & 1) {
            e--;
            acc = (x * acc) % m;
        }
        x = (x * x) % m;
        e >>= 1;
    }
    return acc;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM