用模板静态计算出最大公因子 (几何画板开发笔记 一)

问题提出:
   在开发 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
};

 

你可能感兴趣的:(算法,模板,数学)