用模板靜態計算出最大公因子 (幾何畫板開發筆記 一)

問題提出:
   在開發 C++ 版幾何圖形系統中, 須要求出兩個數字的最大公因子, 算法用 C 語言可寫出爲: 算法

int gcd (int x, int y) {
   if (y == 0) return x;
   else if (x > y) return gcd (y, x % y);
   else /* x <= y */ return gcd (x, y % y);
} 數組

可是爲定義一個靜態的數組如 char buf[size], 這裏 size 是兩個編譯期已知的常量的最大公因子
的時候, 就沒法用函數來計算了, 由於函數要到運行期才能計算出結果.  而咱們須要在編譯期就知道
兩個數的最大公因子. 函數

所以, 咱們必須有什麼辦法根據已知的兩個常量, 靜態的計算出它們的最大公因子.
例如用宏的形式寫爲 #define GCD(x, y)  ...某求出gcd的方法...
在 C 語言中, 是沒有辦法作到求 gcd 的靜態寫法的. 用 C++ 的模板, 才能作到. 測試

爲了實現求 gcd, 須要先作一些準備工做. 包括求兩個值的較大者, 較小者, 以及兩個數的模.

#define min(x, y)     ((x) < (y) ? (x) : (y))   // 求二者中較小者.
#define max(x, y)    ((x) > (y) ? (x) : (y))    // 求二者中較大者.

求二者的餘 mod 須要用模板, 由於對 0 求除會致使錯誤, 在常量表達式中不能使用. 翻譯

template <int X, int Y> struct mod {
   enum { value = X % Y };
}; 遞歸

解決除 0 的問題, 用模板的偏特化版本: 開發

template <int X> struct mod <X, 0> {   // X % 0
   enum { value = 0 };
}; 編譯器

template <int Y> struct mod <0, Y> {  // 0 % Y
   enum { value = 0 };
}; 編譯

template <> struct mod<0, 0> {  // 0 % 0
   enum { value = 0 };
}; 模板

簡單測試 mod:
enum {
   mod1 = mod<3, 4>::value,   // ==> 3, 使用通常版本的 mod<>
   mod2 = mod<4, 3>::value,   // ==> 1, 通常版本
   mod3 = mod<0, 4>::value,   // ==> 0, 偏特化 x=0 的版本.
   mod4 = mod<3, 0>::value,   // ==> 0, 偏特化 y=0 的版本.
   mod5 = mod<0, 0>::value,   // ==> 0, 特化 x=0,y=0 的版本.
};

而後將 gcd 算法翻譯爲模板:

template <int X, int Y> struct gcd {
   enum { amin = min (X, Y),   // X, Y 中較小的一個.
              amax = max (X, Y),  // X, Y 中較大的一個.
              amod = mod<amax, amin>::value,    // X % Y 若是 X > Y
              value = (Y == 0) ? X
                          : gcd <amin, amod>::value    // 遞歸求小者 和 模 的 gcd.
   };
};

再提供兩個特化的版本:

template <> struct gcd <1, 1> { enum { value = 1 }; };
template <> struct gcd <0, 0> { enum { value = 0 }; };
(也許還能提供更多的特化版本).

測試 gcd:

enum {
    gcd1 = gcd<4, 3>::value,     // ==> 1
    gcd2 = gcd<4, 6>::value,     // ==> 2
    gcd3 = gcd<20, 16>::value,  // ==> 4
};

如今咱們能夠定義一個靜態數組, 用以下方式了:

#define  X   4     // X, Y 根據編譯器參數可能有所不一樣. 例如 X 定義爲 sizeof (int)
#define  Y   6
static const int buf_size = gcd<4, 6>::value;   // ==> 2
static int buf [buf_size];

 

最後基於 gcd 以求出最小公倍數 lcm:
#define lcm(X, Y)  ((X)/gcd<(X), (Y)>::value * (Y))

enum {    lcm1 = lcm(4, 6),    // ==> 12    lcm2 = lcm(3, 7),    // ==> 21 };

相關文章
相關標籤/搜索