编译器求素数 杂项 boost::any

renzhewh 2010-08-24 05:02:27

1、编译期判断一个正数是否为素数?
由于IsPrimer_impl 特化的版本处理不好,因此目前只能接受500以下的素数,
希望高手帮我提高上限

// 辅助类
template <int value, int index>
struct IsPrimer_impl
{
enum {IsExceed = (index * index > value) ? true : false};
enum {IsDivideExact = (value % index == 0) ? true : false};

enum {result = (IsExceed ? true : (IsDivideExact ? false : IsPrimer_impl<value, index + 1>::result))};
};
// 这里的终止操作处理的不好
template <int value>
struct IsPrimer_impl<value, value>
{
enum {result = true};
};

// result为true,表示value为素数
template <int value>
struct IsPrimer
{
enum {result = IsPrimer_impl<value, 2>::result};
};

template <>
struct IsPrimer<1>
{
enum {result = false};
};


// 测试
int main()
{
// 超过499就会出现错误(VS2008)
cout << IsPrimer<2>::result << endl;
cout << IsPrimer<3>::result << endl;
cout << IsPrimer<40>::result << endl;
cout << IsPrimer<51>::result << endl;

return 0;
}

2、将赋值运算符私有化时,为什么有人将返回值类型定义为void?
这样做有什么用意吗?

3、boost库中any_cast的实现,为什么调用指针版本来完成?
// 转型,通过指针版本完成具体动作
template <class T>
T any_cast(any& operand)
{
const T* result = any_cast<T>(&operand);

if (result == NULL)
throw bad_any_cast();

return *result;
}

template <class T>
T* any_cast(any* operand)
{
return operand && (operand->type() == typeid(T)) ?
&static_cast<holder<T> *>(operand->content)->held : 0;
}
...全文
146 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
shuitu 2011-01-13
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 iambic 的回复:]

模板元还是远离吧。有人做好了给你用,你就用;没人给你做,别自己做。传统解决方案比模板元来得省事多。模板只是理论上很强大,实践上束手束脚,根本算不上成熟的技术。
[/Quote]
完全同意,模板元这种东西不实用。

我小改了一下楼主的代码,能判断更大范围的整数是否是素数。
关键点:
1.比如10整除2时停止模板推导
2.比如4*4大于10时停止模板推导
3.只考虑能否整除奇数

#include<iostream>

using namespace std;

template<bool IsDivideExact,bool IsExceed,int value,int index> struct IsPrimer_impl;

/*
如果整除,必定不是素数;停止模板推导
*/
template<bool IsExceed,int value,int index>
struct IsPrimer_impl<true,IsExceed,value,index>{
enum { result=0 };
};

/*
如果不整除,且index*index>value,必定是素数;停止模板推导
*/
template<int value,int index>
struct IsPrimer_impl<false,true,value,index>{
enum { result=1 };
};

/*
注意,index初值是3,每次递增2,即只考虑奇数。
*/
template<int value,int index>
struct IsPrimer_impl<false,false,value,index>{
enum { result=IsPrimer_impl< (0==value%index), (index*index>value),value,index+2>::result };
};

/*
value>=2
*/
template <int value>
struct IsPrimer
{
enum {result =(value==2) || IsPrimer_impl<(0==value%2),(2*2>value),value, 3>::result};
};

void main(){
cout << IsPrimer<1000*1000*1000>::result << endl;
cout << IsPrimer<2>::result << endl;
cout << IsPrimer<7>::result << endl;
cout << IsPrimer<991*991>::result << endl;
//cout << IsPrimer<997*997>::result << endl;//在VS2008下会导致错误,原因也是超出模板嵌套定义的上限
}
renzhewh 2010-08-27
  • 打赏
  • 举报
回复
非常感谢 楼上好像起的很早啊
ri_aje 2010-08-27
  • 打赏
  • 举报
回复
To 3 L

我觉得应该能够自动生成这张表,因为前几个素数是已知的,可以利用这些知识一步一步的生成下一个素数,以此类推。就好像通过数学归纳发证明定理一样,首先证明一个基本命题,然后向下一级一级的推倒,不过我现在还没有几个这样的实现。

我认为编译期的上限实际上是编译器的运行期上限强加的,因为当编译程序的时候,实际上是编译器程序的运行期。编译器需要分配各种资源,分析程序,记录变量,递归模板等等,这些都是要消耗资源的,当问题足够复杂的时候,资源迟早要消耗光的,比如说模板程序忘记写终止条件了,那么将形成一个无限深度的递归实例化。当然,实际实现的编译器并没有出现堆栈溢出崩溃的情况,一方面因为C++语言要求的实例化深度未必能够消耗掉所有资源,另一方面,编译器自身程序肯定也会保护自己,发现实例化太深的时候,会自动停止,输出错误的。

既然无法突破上限,那么在本例中,这是否意味着,要想提高所能处理数的上限,就必须设法减少处理过程中所使用的“空间”,正如楼上用素数集代替自然数集。

我认为这种理解是正确的。

模板元编程有很多长处,也有不少缺陷,我不知道所有的,也不可能在这里把我知道的都说出来。我认为模板元编程有一个很重要的特性,就是它能够在程序的编译期,加入逻辑分析的能力,从而生成高度优化的代码。这样能够写出优美简洁的程序,同时又能够获得所需的性能,前者是高层语言的特性,后者是底层语言的特性,将其结合起来,非模板莫属。
iambic 2010-08-25
  • 打赏
  • 举报
回复
模板元还是远离吧。有人做好了给你用,你就用;没人给你做,别自己做。传统解决方案比模板元来得省事多。模板只是理论上很强大,实践上束手束脚,根本算不上成熟的技术。
renzhewh 2010-08-25
  • 打赏
  • 举报
回复
多谢楼上,受教了 你很强啊 :-)

楼上在计算过程中用到素数表,使我想问:
1、如何自动生成一个这样的表呢?
2、如果没有这样一张表,可不可以在计算过程中生成呢?

还有编译期的运算类似于递归,上述存在的嵌套上限是否类似于递归中存在的上限呢?
可是递归是因为栈溢出,编译期的运算又是因为什么呢?楼上提到“机器本身的客观限制”
在这里具体是什么呢?

既然无法突破上限,那么在本例中,这是否意味着,要想提高所能处理数的上限,就必须设法减少处理过程中所使用的“空间”,正如楼上用素数集代替自然数集。

我只看过模板元编程的几个简单个入门程序,所以我很想知道模板元编程的专长是什么?(有人说,无所不能 :-) )
ri_aje 2010-08-25
  • 打赏
  • 举报
回复
现在再说第一个问题。

首先明确一点,C++并不是万能的,因为本质上是一种基于二进制的状态机,它会受到机器本身的客观限制,其中一个限制就是所谓的模板嵌套递归上限,C++标准对此并没有明确的规定,只在最后的附录中给出了一些建议,其中模板嵌套递归上限的建议深度是1024,但标准同时明确表明,编译器实现可以不以此为基准,并且这样的行为被认为是符合标准的 。

通过你陈述的问题,我猜想VS2008的默认实例化深度是512,这也是为什么你只能验证到499的原因。其实下两个小于512的素数是503和509,不过你在实例化IsPrimer和初次实例化IsPrimer_impl的时候已经消耗两层内嵌递归实例化了,因此算法推理过程中只能达到499. 我用g++-4.5.1原封不动的编译你的程序就能够达到1013,因为g++的默认深度是1024,当然VS2008和g++的行为都是符合标准的,只不过客观上g++做的更好一些。

明确了上述事实后,我认为你的程序有一个缺陷,就是在你已经判断出一个数是合数时,你的程序并没有立刻停止,而是继续递归直到进入IsPrimer_impl的特化而终止,这种做法有失效率。遗憾的是,即便修改了这一点,仍然不能有效增加你的可识别上限,原因在于你采用的算法本质上是一个一个数的试,而每次你试一个数,就消耗一层模板嵌套递归的深度,因此最多只能试512次,或1024次。必须采用更好的算法才能都有效的提高上限。

根据代数基本定理:任何大于1的整数都能够分解为一系列素数的乘积。
一种显而意见的算法是只利用素数去试验,而不用合数,因为对任意合数A,如果一它能够整除另一个合数B,那么它必然能够整除B的所有素因子,因此只需要利用素数去试验分解。这种算法的好处数,模板嵌套递归的深度n不在是你能够试验的最大数字,而是你能够试验的最大素数在所有素数升序排列中的序号,比如在我的机器上,这个数字是8161。另一个重要的事实是,对于任意合数,其质因子必然小于其平方根,我注意到在你的程序中已经应用了这一点。这一事实使得能够测试的数据有效上限达到8161*8161=66601921。

说了这么多,下面是一个上述算法的实现。程序结构分为两个文件,下面是主程序,其中的prime10000.h是前10000个素数的列表,因为本质上需要遍历素数,而非整数,所以需要这个列表。

#include <iostream>
#include <iomanip>
using namespace std;

#include "prime10000.h"

#define PRINT(N) cout << setw(20) << N << " " << primality<N>::value << endl

size_t const template_recurrsion_limit = 1024 - 1;

// Undetermined - primary algorithm
template <unsigned long long int N, unsigned long long int I = 1,
bool = false, bool = false>
struct primality
{
enum
{
factor = nth_prime<I>::value,
composite = factor*factor <= N && 0 == N%factor,
value = primality<N, (I<template_recurrsion_limit?I+1:0),
(factor*factor>N), composite>::value
};
};

// Prime number
template <unsigned long long int N, unsigned long long int I>
struct primality <N, I, true, false> { enum { value = 1 }; };

// Composite number
template <unsigned long long int N, unsigned long long int I>
struct primality <N, I, false, true> { enum { value = 0 }; };

// Prime number and composite number - logical error
template <unsigned long long int N, unsigned long long int I>
struct primality <N, I, true, true>;

// Uncovered cases
// Let n = template_recurrsion_limit defined above
// and P = the nth prime number
// and S = P * P
// and B = multipulication of the first n prime numbers
// then, N MUST be one of :
// [1] N in [2, S], N MUST be a prime number
// [2] N in (S, B], primality is undetermined
// if N is a composite number, its smallest prime factor
// MUST be great than S
// [3] N in (B,inf), impossible due to machine limit
template <unsigned long long int N, bool P, bool C>
struct primality <N, 0, P, C>
{
enum
{
prime = nth_prime<template_recurrsion_limit>::value,
value = N <= prime*prime ? 1 : 2
};
};

// Zero-divisor
template <unsigned long long int I, bool P, bool C>
struct primality <0, I, P, C> { enum { value = 0 }; };

// Units
template <unsigned long long int I, bool P, bool C>
struct primality <1, I, P, C> { enum { value = 0 }; };

int main (int, char**)
{
cout << setw(20) << "Number" << " Primality" << endl;
PRINT( 0ull);
PRINT( 1ull);
PRINT( 2ull);
PRINT( 3ull);
PRINT( 40ull);
PRINT( 51ull);
PRINT( 499ull);
PRINT( 500ull);
PRINT( 1000ull);
PRINT( 1024ull);
PRINT( 1025ull);
PRINT( 2000ull);
PRINT( 2001ull);
PRINT( 2003ull);
PRINT( 20129ull);
PRINT( 20139ull);
PRINT( 20149ull);
PRINT( 104728ull);
PRINT( 104729ull);
// Below are all prime numbers
PRINT( 16769023ull);
PRINT( 1073676287ull);
PRINT( 1125899839733759ull);
PRINT( 18014398241046527ull);
PRINT( 18014398777917439ull);
PRINT( 32361122672259149ull);
// This is the largest prime number I can find within my machine limit
// largest unsigned long long int = 18446744073709551615ull
PRINT(2305843009213693951ull);

return 0;
}

程序中PRINT是一个宏,为的是节省一次模板实例化。
由于prime10000.h有10000多行,所以不适合发在这里,我把前几行列出来,应该能看明白

template <unsigned long long int> struct nth_prime; // We never come here
template <> struct nth_prime < 1> { enum { value = 2 }; };
template <> struct nth_prime < 2> { enum { value = 3 }; };
template <> struct nth_prime < 3> { enum { value = 5 }; };
template <> struct nth_prime < 4> { enum { value = 7 }; };
template <> struct nth_prime < 5> { enum { value = 11 }; };
template <> struct nth_prime < 6> { enum { value = 13 }; };
template <> struct nth_prime < 7> { enum { value = 17 }; };
template <> struct nth_prime < 8> { enum { value = 19 }; };
template <> struct nth_prime < 9> { enum { value = 23 }; };
template <> struct nth_prime < 10> { enum { value = 29 }; };
template <> struct nth_prime < 11> { enum { value = 31 }; };
template <> struct nth_prime < 12> { enum { value = 37 }; };
template <> struct nth_prime < 13> { enum { value = 41 }; };

这张素数表是在,这里找到的http://primes.utm.edu/lists/small/10000.txt

注意,因为有了这张素数表,我们可以直接查表回答比较小的问题,但那样并不能节约任何模板嵌套深度,因为查表本身的过程仍然是递归实例化的过程。

最后一点很重要的说明,假设你个模板嵌套深度为n,而第n个素数为P,它的平方为S,B为前n个素数的连乘积,注意B其实已经超过机器能够表达的最大整数了,你需要判断的数为N。程序的逻辑将数轴分为三节,分别是
[1] [2, S],程序能够准确判断出此区间内任何数字的素数性。
[2] (S, B],程序不能够确判断出此区间内数字的素数性。素数肯定会识别为素数,问题是有些合数也会被识别为素数,这种合数的特点是他们最小的质因子大于S
[3] (B,inf),不可能出现这种情况,因为机器已经无法表示了。
ri_aje 2010-08-25
  • 打赏
  • 举报
回复
LZ第一个问题较难回答,其他两个相对容易一些,现回答2, 3,然后再说 1

2. 将赋值运算符私有化目的是为了禁止通过赋值运算符复制变量,因为当编译器遇到这样的语句时,会试图调用私有成员函数,而这是就会产生编译错误,从而强迫程序员取消赋值调用,达到类设计者的目的。因此在整个设计过程中,最重要的一点是私有化赋值运算符,确保访问控制,具体返回什么,如何实现都不重要,因为本来也不是用来调用的。你可以返回任意合法的类型,int, char, ... ,当然,最简单的莫过于返回void类型了。

3. 我不熟悉boost库,但当我看到这段程序,我的理解是。在这样一个强制类型转换过程中,由于需要访问operand的成员变量,因此对于指针和对象在语法上就有两种不同的结构,一个是operator ->,另一个是 operator .,显然,必须通过函数重载区别指针和对象,这样就使得同样的程序必须维护两个版本,而后者显然并非软件工程所倡导的实现方式,为了同时满足C++语法的限制和程序可维护性的需求,最简单的方法就是让其中一个版本调用另一个版本的实现。这样就有两种选择,其一,让对象的版本调用指针的版本,这正是boost所用的,其二,让指针的版本调用对象的版本。第二种方法不好,原因在于,当你解指针引用获取对象的时候,如果指针本身是非法的,比如 NULL,会在运行期导致程序崩溃,而这种错误并不少见,却很难调试,作为一个好的程序库,boost显然需要从库设计的角度尽可能避免这种情况;另一方面,当你提取对象地址获取其指针时,结果总是合法并且有效的,因为语言本身保证了不可能存在非法的对象,或者至少是很难存在。

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧