模板元编程实现素数判定

模板元编程实现素数判定

模板元编程(英语:Template metaprogramming;缩写:TMP)是一种元编程技术,不夸张的说,这项技术开启了一种新的C++编程方式。编译器使用模板产生暂时性的源码,然后再和剩下的源码混合并编译。这些模板的输出包括编译时期常数、数据结构以及完整的函数。如此利用模板可以被想成编译期的运行。本文介绍了利用模板元编程技术实现在编译期判断一个整数是否为素数的算法。输入为一个大于0的整数,输出为1表示该整数为素数,为0表示为合数。本文的主要目的是用实例说明模板元编程实现算术和逻辑运算的一般设计和编写方法。模板元编程的概念和基本介绍参见维基百科:http://zh.wikipedia.org/wiki/模板超編程

我们使用最基本的素数判断算法,伪代码如下:

?
1
2
3
4
5
6
7
function IsPrime(n)
if  n == 1 then return  false
if  n == 2 then return  true
for  each m from 2
     if  m * m > n then return  true
     if  n mod m = 0 then return  false
     m := m + 1

这显然是一个复杂度为O(sqrt(n))的算法,主要的逻辑为循环。模板元编程是以递归的逻辑形式来实现循环算法,因此必须先明确两件事:有几个变量参与,以及循环的终止条件是什么。这个算法显然有2个变量参与运算:一个是n,另一个是m。我们令m从2开始递增,直到达到循环终止条件。在模板元编程中,由于模板参数推导的优先级是以特化程度排列的,因此终止条件和特殊值的处理使用偏特化(也叫部分特化)实现。关于模板参数推导和偏特化的概念和语法这里不做赘述,请参阅C++书籍或搜索网页资料。那么根据以上分析,我们可以先写出一个框架如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
template <uint n, uint m> struct  TEST{ enum {
     r = TEST<n, nextM>::r //nextM为下一个M,暂不实现。在这里用递归结构代替了循环
};};
template <uint n> struct  ISPRIME{ enum {
     r = TEST<n, 2>::r //从2开始,依次判断每一个可能的m取值,判断代码暂未实现。
};};
template <> struct  ISPRIME<1>{ enum { //对于算法不能计算的特殊值1,判断为0
     r = 0
};};
template <> struct  ISPRIME<2>{ enum { //对于算法不能计算的特殊值2,判断为1
     r = 1
};};

循环的终止条件是:m的平方大于n或可以整除n。当满足终止条件时,向模板参数传递一个特殊值,并在偏特化中处理这个值,那么递归逻辑就终止了。而判断是否满足终止条件,则需进行逻辑和算术运算。基于以上分析,框架代码改写如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <uint n, uint m> struct  TEST{ enum {
        // if (n % m == 0) n = 0;
        // if (m * m > n) m = 0; else ++m;
     r = TEST<n, m>::r //上面两行代码不能写在此处,仅说明逻辑。实际的语法下文再做介绍
};};
template <uint m> struct  TEST<0, m>{ enum { //n为0的情况
     r = 0 //即在非特化的模板代码中,n可以被m整除,因此n被赋值为0,故为合数
};};
template <uint n> struct  TEST<n, 0>{ enum { //m为0的情况
     r = 1 //即在非特化的模板代码中,m * m > n,因此n不能被任何比它小的数整除,故为素数
};};
template <uint n> struct  ISPRIME{ enum {
     r = TEST<n, 2>::r //从2开始,依次判断每一个可能的m取值,判断代码暂未实现。
};};
template <> struct  ISPRIME<1>{ enum { //对于算法不能计算的特殊值1,判断为0
     r = 0
};};
template <> struct  ISPRIME<2>{ enum { //对于算法不能计算的特殊值2,判断为1
     r = 1
};};

 最后只要用模板的参数推导实现取模的算术运算和上面框架中的两个逻辑判断即可,完整代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
typedef  unsigned int  uint;
 
template <uint n, uint m> struct  NEXTN{ enum {
     r = ((n % m != 0) * n) //相当于:if (n % m == 0) return 0; else return n;
};};
template <uint n, uint m> struct  NEXTM{ enum {
     r = (m * m <= n ? (m + 1) : 0) //相当于:if (m * m > n) return 0; else return m + 1;
};};
template <uint n, uint m> struct  TEST{ enum {
     r = TEST<NEXTN<n, m>::r, NEXTM<n, m>::r>::r //将终止条件融入模板参数变量
};};
template <uint m> struct  TEST<0, m>{ enum { //对n为0的偏特化
     r = 0
};};
template <uint n> struct  TEST<n, 0>{ enum { //对m为0的偏特化
     r = 1
};};
template <uint n> struct  ISPRIME{ enum { //对TEST模板进行封装,使用户无需输入模板参数变量m
     r = TEST<n, 2>::r
};};
template <> struct  ISPRIME<1>{ enum { //对n为1的偏特化
     r = 0
};};
template <> struct  ISPRIME<2>{ enum { //对n为2的偏特化
     r = 1
};};
 
int  main() {
     int  primes[] = {
         ISPRIME<1>::r, ISPRIME<2>::r, ISPRIME<3>::r, ISPRIME<4>::r,
         ISPRIME<5>::r, ISPRIME<6>::r, ISPRIME<7>::r, ISPRIME<8>::r,
         ISPRIME<9>::r, ISPRIME<10>::r, ISPRIME<11>::r, ISPRIME<12>::r,
         ISPRIME<13>::r, ISPRIME<14>::r, ISPRIME<15>::r, ISPRIME<16>::r,
     };
     for  ( int  i = 0; i < sizeof (primes) / sizeof (primes[0]); ++i)
         std::cout << i + 1 << (primes[i] ? " YES"  : " NO" ) <<std::endl;
     return  0;
}

 如果您有更简洁的写法,请回复告知。谢谢!

你可能感兴趣的:(元编程)