2012-01-27 96 views
2

能somewone幫我轉換這個方案功能:遞歸迭代 - 計劃到C

#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)])) 

進入C中的函數,並且不使用遞歸即使用迭代。

我不知道',困擾我的部分是當e很奇怪,然後遞歸調用在餘數函數中。我不知道如何在一個while循環中傳輸它?任何提示或建議:

這是我迄今爲止:因爲代碼已被寫入尾遞歸

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; 

} 

回答

4

偶數版本是直截了當這麼(powmod2 xsqrm (/ e 2) m)呼叫可以通過更換Ë反覆表達一半的e和x的平方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; 
} 

但是奇數版本沒有被遞歸寫尾。一種方法是創建一個使用累加器的輔助方法。這個輔助方法可以被遞歸地寫成尾和偶指數。然後,您可以將其轉換爲迭代。

2

由於原始方案代碼不是尾遞歸,因此您無法進行轉換。嘗試向powmod2添加額外的參數,以便在調用遞歸函數後,您不需要在奇數情況下通過餘數進行乘法運算。

爲了說明這一點,它很難loopify以下功能

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

但是很容易與積累參數loopify版本

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

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

因此,讓一個輔助函數,它的「(餘數(* x(powmod2 xsqrm(/(sub1e)2)m))m)」部分?使用迭代? – Thatdude1 2012-01-27 22:00:01

+0

不,您需要修改powmod2本身,並使用助手來啓動它,就像我的例子。 (也許還有其他的方法可以解決這個簡單的問題,但是你並不是真的在做直接翻譯) – hugomg 2012-01-27 22:25:18

1

或許,如果你要運行一些算法值來查看結果的計算方式,它可以幫助您瞭解如何轉換結果。讓我們來看看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 

在步驟1中,我們得到的結果的部分計算:它是下一步國防部7的5倍,結果在第2步,因爲它甚至結果與下一步的結果相同。在步驟3中,我們得到了類似於步驟1的結果。我們上樓的結果是通過將下一個結果乘以2(mod 7再次計算)來計算的。在終止時,我們得到了我們的結果來喂樓上:1.現在,隨着我們上升,我們只知道如何計算res:步驟3的2*1%7,步驟2的2,步驟1的2*5%7

實現它的一種方法是使用堆棧。在每個部分結果中,如果指數是奇數,我們可以將乘法因子推入堆棧,一旦我們終止,我們可以將它們全部乘以。這是轉換的天真/欺騙方法。

當您查看上述步驟時,您應該能夠看到更有效的方法。關於將所有東西都轉換成尾遞歸的其他答案也是非常好的提示。

+0

謝謝,我做了這件事,但沒有意識到奇數情況下的乘法運算 – Thatdude1 2012-01-28 05:06:42

+0

是的,我可以看到你接近解決方案的代碼。變量「i」試圖對結果進行「積累」,但稍微縮短了一點。而返回值就是你失去了一點情節的地方。 :)我希望它現在全部排序。 – vhallac 2012-01-28 09:02:40

1

最簡單的方法是推理什麼是原始函數試圖計算?這是x對電源e模塊m的值。如果您以二進制表示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 + ...

通過使用模運算符的數學屬性,即。 (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; 
} 
+0

謝謝,我想知道爲什麼如果我添加其他的情況下,我會得到超過cpu限制,我也注意到,如果我在if語句而不是奇數,我會得到超過cpu限制 – Thatdude1 2012-01-28 00:21:30

+0

你是怎麼用別的代碼編寫代碼的?你寫了'if(e%2){r =(r * x)%m; } else {x =(x * x)%m; e/= 2; }'?如果你確實寫過,那麼如果'e'很奇怪,那麼你永遠不會減少它,並進入一個無限循環。如果你想使用'else',你需要寫'if(e%2){r =(r * x)%m; x =(x * x)%m; e/= 2; } else {x =(x * x)%m; e/= 2; }'。但是,你在'if'的兩個分支的末尾有相同的代碼,最好完全刪除'else'。 – 2012-01-28 00:26:53

1

第一步將被寫入原計劃過程作爲尾遞歸。請注意,這改寫作品,因爲模運算的性質:

(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)) 

上述過程的關鍵要素是,答案在acc參數傳遞。現在我們有一個尾遞歸,之後轉換到完全迭代的解決方案是非常簡單的:

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; 
} 

可以進一步優化,比如:

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; 
}