• 模板元编程实现素数判定


    模板元编程实现素数判定

    模板元编程(英语: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;
    }

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

  • 相关阅读:
    【Vijos-P1285】佳佳的魔法药水-Dijkstra思想
    【NOIP2009提高组T3】最优贸易-双向SPFA
    【NOIP2009提高组T3】最优贸易-双向SPFA
    【Vijos-P1046】观光旅游-Floyd求最小环
    【Vijos-P1046】观光旅游-Floyd求最小环
    【Vijos-P1060】盒子-DP+组合数学
    mysql 结合keepalived测试
    set global read_only=0; 关闭只读,可以读写 set global read_only=1; 开始只读模式
    set global read_only=0; 关闭只读,可以读写 set global read_only=1; 开始只读模式
    -F, --flush-logs
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3327319.html
Copyright © 2020-2023  润新知