视频讲解:等视频审核通过之后才能看到。
【免费】32bit素数检测视频讲解.zip资源-CSDN文库
快速幂模运算
快速幂模运算代码
以下所有代码的视频讲解,抖音号:SPL19750815
unsigned int PowerMod_U32(unsigned int x, unsigned int N, \
unsigned int m);
/*
⨉教学函数。
计算x^(N)模m的非负余数。不是先把x^(N)算出来,再取余数,早就溢出了,也不是
做N次循环。代码包里有一篇数论算法,第一个算法就是逐次平方法幂模运算。
*/
unsigned int PowerMod_U32(unsigned int x, unsigned int N, unsigned int m)
{
if (m == 0)
{
std::cout << "除数为零,程序强行终止!\n";
exit(0);
}
if (m == 1)
return 0;
/*
模m不能为0。
模m是1,余数必定为0。
*/
unsigned int k;
unsigned long Max;
unsigned int MaskCode = 1;
unsigned long long X = x;
unsigned long long Remainder = 1;
/*
如果N = 0,对任意x,x^N = 1,模m是结果1。 注意m = 1被前面的if语句事先
滤除了,任何数模1结果是0。如果N较小,直接使用循环法更快。
*/
if (N == 0)
Remainder = 1;
else if (N <= 12)
{
for (k = 0; k < N; k++)
{
Remainder *= X;//Remainder初始值为1,每次乘以X,取余,循环N次。
Remainder %= m;
/*
unsigned int的乘法需要用 unsigned long long来处理中间结果才不
会溢出,然后把乘法的结果求余数,余数必定小于m, 而m不会超出32
bit,无论循环多少次都不会溢出。 如果Remainder被设置为unsigned
int类型,那么Remainder *= X只有低32bit乘法结果,高32bit被编译
器丢弃了。
*/
}
}
else
{
_BitScanReverse(&Max, N);
/*
_BitScanReverse这个函数取得N的二进制里最高有效位的次数,基于下标0
计算,这是cpu硬件完成。
MaskCode初始化为1,每次循环左移一次, MaskCode的二进制只有一个1,
位与运算&用来测试N的二进制对应位是否为1。
示例:N = 18 =(10010)二进制,计算出Max = 4。
k if测试 每次循环后result(基于mod m) X(基于mod m)
0 假 1 X^2
1 真 X^2 X^4
2 假 X^2 X^8
3 假 X^2 X^16
4 真 X^18 X^32
注意到没,在这个例子里,明明计算到X^16(mod m)就够用了,可是平白无
故地多计算了一个X^32。其次最后一次循环的if测试必定成立, 多执行了
这次测试。最后,MaskCode <<= 1和k++,怎么都感觉都有一个是多余的。
*/
for (k = 0; k <= Max; k++)
{
if (N & MaskCode)
{
Remainder *= X;
Remainder %= m;
}
MaskCode <<= 1;
X *= X;
X %= m;
}
}
return (unsigned int)Remainder;
//余数必定小于模m,而m又必定小于2^32,所以强制转换不会丢失数据。
}
改进后的快速幂模运算代码
void PowerMod_U32(unsigned int x, unsigned int N, \
unsigned int m, unsigned int& R);
/*
★★★★★实用函数。
优化版逐次平方法快速幂模运算。
*/
/*
下面这个幂模函数改正了上一个存在的各种不足,提速15%。
*/
void PowerMod_U32(unsigned int x, unsigned int N, \
unsigned int m, unsigned int& R)
{
if (m <= 1)
{
if (m == 0)
{
std::cout << "除数为零,程序强行终止!\n";
exit(0);
}
else
{
R = 0;
return;
}
}
if (N <= 1)
{
if (N == 0)
{
R = 1;
return;
}
else
{
R = x % m;
return;
}
}
unsigned int k;
unsigned long Max;
unsigned long long Product;
R = 1;
if (N <= 12)
{
for (k = 0; k < N; k++)
{
Product = (unsigned long long)R * x;
_udiv64(Product, m, &R);
}
}
else
{
_BitScanReverse(&Max, N);
Max = (1 << (Max - 1));
for (k = 1; k <= Max; k <<= 1)
{
if (N & k)
{
Product = (unsigned long long)R * x;
_udiv64(Product, m, &R);
}
Product = (unsigned long long)x * x;
_udiv64(Product, m, &x);
}
Product = (unsigned long long)R * x;
_udiv64(Product, m, &R);
/*
这段代码和上一段代码有何不同?
1.循环变量k兼做位与测试,省去了一条MaskCode <<= 1的操作。
2.在上一段代码中Remainder是64bit变量,求模运算符%是64bit除以64bit
的运算;在这段代码中R是32bit变量,_udiv64是64bit除以32bit的运算,
速度稍快。
3.在上一段代码中,最后一次循环if测试必定为真, 里面的运算必定要执
行。但是if下面的两条语句被多余计算了(因为没有再下一次循环了)。
*/
}
}
米勒拉宾算法
米勒拉宾算法代码
bool StdMillerRabin_U32(unsigned int Witness, unsigned int Composite);
/*
★★★★★实用函数。
32bit米勒拉宾测试。
用一个证据Witness来判断Composite是否为合数的米勒拉宾合数测试算法, 要求证
据Witness >= 2。
米勒拉宾合数测试算法参考机械工业出版社[数论概论]第90页, 作者约瑟夫H.西尔
弗曼。算法,看代码包里的文档。
①如果Witness证明了 Composite是合数,返回 true,100%确信断言。
②如果Witness证明不了Composite是合数,返回false,这里有25%的误判。 什么意
思呢?返回true,一定是合数;返回fasle, 只是说明当前使用的这个证据Witness
证明不了Composite是合数。这个算法是证明合数的,不是证明素数的。
例如172947529 = 307 * 631 * 919是合数,Witness = 17, 这个算法返回false,
仅仅说明17证明不了172947529是合数。现在把Witness换成23,立即返回true。 17
和23做出了截然不同的判断,该相信哪一个呢?返回true的那个。如果判定一个数P
到底是素数还是合数,该怎么做呢?用一些不同的Witness来测试,如果有一个返回
true,那确信是合数,如果都返回false,那么P成为素数的可能性非常之高, 这个
就是概率检测。错判的可能性是0.25^N,N是证据个数。
*/
bool StdMillerRabin_U32(unsigned int Witness, unsigned int Composite)
{
if (Witness < 2)
{
std::cout << "Witness必须大于等于2!\n";
exit(0);
}
if (Composite <= 2)
{
if (Composite == 2)
return false;
else
return true;
}
if ((Composite & 1) == 0)
return true;
/*
进行规则检查。
1.Witness必须大于等于2。
2.Composite = 0、1视同合数(素数从2开始定义), 2是唯一偶素数,直接判
定返回。如果Composite是偶数,直接判定返回。
*/
if (Composite == Witness)
return false;//无法断言
else
{
if (Composite % Witness == 0)
return true;
}
/*
上面的if-else代码需要好好理解,这是烧脑所在。
1.如果Composite和Witness相等(第一个if部分),这是不允许的。 米勒拉宾
算法的起始条件要求这两个数不能相同。在这种情况下,直接返回false,表示
无法断言Composite是合数。米勒拉宾算法是一种概率型的算法,在这种情况下
需要更换另外一些不同的证据数来重新检测。
2.如果Composite和Witness不相等(else部分), 并且Witness整除Composite
(这意味着Composite严格大,因为它们不相同的else假设), 此时Composite
必定是合数,因为Composite有一个大于等于2的因子并且这个因子不是本身。
3.如果Composite和Witness不相等(else部分), 又假设else里面的if语句不
成立,即Witness不整除Composite,那正是这个代码正常的流程会向下继续。
*/
/*
如果没有这一组if-else代码会发生什么问题呢?假定要检测61,而证据数也是
61,在接下来的一系列运算中求得的余数都是零, 满足米勒拉宾算法的结论性
条件,会给出61是合数的荒谬结论, 这个错误的根源就在于证据数和被检测数
相同。再如,用证据数7来检测49,显然7整除49, 49可以直接被判定为合数而
无需继续。
*/
unsigned int C_1 = Composite - 1;
unsigned long k;
unsigned int q = C_1;
unsigned long n;
unsigned int Remainder;
_BitScanForward(&k, q);
q >>= k;
/*
如果Composite是2或者2的倍数,前面那些语句直接滤除了,不可能进到这里。
所以Composite是奇数,从而C_1是偶数,接下来把C_1写成 (2^k)*q的形式。比
如Composite = 21,C_1 = 20 = (2^2)*5,k = 2,q = 5。
_BitScanForward是从右面开始查找q的二进制里第一个1的下标, 基于下标0计
算,这是硬件来完成。右移运算是除以2的k次幂。
*/
PowerMod_U32(Witness, q, Composite, Remainder);
if ((Remainder == 1) || (Remainder == C_1))
return false;//无法断言
for (n = 1; n < k; n++)
{
_udiv64((unsigned long long)Remainder * Remainder, \
Composite, &Remainder);
if (Remainder == C_1)
return false;//无法断言
}
return true;
}
多证据数米勒拉宾算法代码
bool ExtMillerRabin_U32(unsigned int* pWitness, unsigned int Length, \
unsigned int Composite);
/*
★实用函数。
32bit米勒拉宾测试。
使用一组数pWitness来判定Composite是否为合数的米勒拉宾测试算法,Length指定
证据数个数。
返回值:
Composite为合数:true;
Composite为素数:false(基于概率)。
*/
bool ExtMillerRabin_U32(unsigned int* pWitness, unsigned int Length, \
unsigned int Composite)
{
bool isComposite = false;
/*
如果任何一次循环,返回isComposite为true,那么Composite必定合数, 如果
都返回false,那么Composite以高概率成为素数。
*/
for (unsigned int k = 0; k < Length; k++)
{
isComposite = StdMillerRabin_U32(pWitness[k], Composite);
if (isComposite)
break;
}
return isComposite;
}
基于米勒拉宾算法的基本素数检测代码
bool StdPrimeQ_U32(unsigned int P, bool PrintNT = false);
/*
⨉教学函数。
2^32以内100%判定P是否为素数,标准速度0.5us以内。
返回值:
P为素数:true。
P为合数:false。
*/
bool StdPrimeQ_U32(unsigned int P, bool PrintNT)
{
bool isPrime = true;
if (StdMillerRabin_U32(2, P))
{
isPrime = false;
goto RetrueHere;
}
if (StdMillerRabin_U32(7, P))
{
isPrime = false;
goto RetrueHere;
}
if (StdMillerRabin_U32(61, P))
isPrime = false;
RetrueHere:
if (PrintNT)
{
if (isPrime)
std::cout << P << "是素数。\n";
else
std::cout << P << "是合数。\n";
}
return isPrime;
}
优化后的算法,去掉函数调用,全部写进一个函数代码里,提高15%速度。
bool ExtPrimeQ_U32(unsigned int P, bool PrintNT = false);
/*
⨉教学函数。
2^32以内100%判定P是否为素数,增强速度0.5us以内。
返回值:
P为素数:true。
P为合数:false。
*/
bool ExtPrimeQ_U32(unsigned int P, bool PrintNT)
{
unsigned int Witness = 2;
unsigned int Remainder = 1;
unsigned long long Product;
unsigned int P_1 = P - 1;
unsigned long k;
unsigned int q = P_1;
unsigned long Max;
unsigned int t;
unsigned int Counter = 0;
bool isPrime = true;
if (P <= 7)
{
switch (P)
{
case 0:
case 1:
case 4:
case 6:
isPrime = false;
goto RetrueHere;
case 2:
case 3:
case 5:
case 7:
goto RetrueHere;
}
}
if (P == 61)
goto RetrueHere;
if ((P & 1) == 0)
{
isPrime = false;
goto RetrueHere;
}
_BitScanForward(&k, q);
q >>= k;
NextRound:
if (q <= 12)
{
for (t = 0; t < q; t++)
{
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
}
else
{
_BitScanReverse(&Max, q);
Max = (1 << (Max - 1));
for (t = 1; t <= Max; t <<= 1)
{
if (q & t)
{
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
Product = (unsigned long long)Witness * Witness;
_udiv64(Product, P, &Witness);
}
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
if ((Remainder == 1) || (Remainder == P_1))
{
switch (Counter)
{
case 0:
Counter = 1;
Witness = 7;
Remainder = 1;
goto NextRound;
case 1:
Counter = 2;
Witness = 61;
Remainder = 1;
goto NextRound;
default:
goto RetrueHere;
}
}
for (t = 1; t < k; t++)
{
Product = (unsigned long long)Remainder * Remainder;
_udiv64(Product, P, &Remainder);
if (Remainder == P_1)
{
switch (Counter)
{
case 0:
Counter = 1;
Witness = 7;
Remainder = 1;
goto NextRound;
case 1:
Counter = 2;
Witness = 61;
Remainder = 1;
goto NextRound;
default:
goto RetrueHere;
}
}
}
isPrime = false;
RetrueHere:
if (PrintNT)
{
if (isPrime)
std::cout << P << "是素数。\n";
else
std::cout << P << "是合数。\n";
}
return isPrime;
}
再次优化,我自己创意的奇招妙想。
bool FlashPrimeQ_U32(unsigned int P);
/*
★★★★★实用函数。
2^32以内100%判定P是否为素数,优化速度0.4us以内。
*/
bool FlashPrimeQ_U32(unsigned int P)
{
const static unsigned int Witness7Power[11] = {
1, 7, 49, 343, 2401,
16807, 117649, 823543, 5764801, 40353607,
282475249
};
const static unsigned int Witness61Power[5] = {
1, 61, 3721, 226981, 13845841
};
if (P <= 7)
{
switch (P)
{
case 0:
case 1:
case 4:
case 6:
return false;
case 2:
case 3:
case 5:
case 7:
return true;
}
}
if (P == 61)
return true;
if ((P & 1) == 0)
return false;
if (P % 3 == 0)
return false;
if (P % 5 == 0)
return false;
if (P % 7 == 0)
return false;
/*
学者们找出了2、7和61这三个证据数,可以100%判定32bit范围内的每一个数是
否素数。
能否让算法更快速一些呢?算法的核心就是幂模运算,例如求2^q mod m,先从
q里抽出31行不行?q = u * 31 + v,2^q = (2^31)^u * 2^v mod m,2^31是已
知,这样原先的循环从q降到u,减少了5次。不是减少31次,更不是降低31倍!
循环次数是q的最高bit的下标值,31近似是5次。原先的循环最多是31次,减少
5次,至少提速16%。7和61类似,但是这两个数只能减少3次和2次循环。
*/
unsigned int Witness = 2147483648 % P;
unsigned int Remainder = 1;
unsigned long long Product;
unsigned int P_1 = P - 1;
unsigned long k;
unsigned int q = P_1;
unsigned long Max;
unsigned int t;
unsigned int u;
unsigned int v;
_BitScanForward(&k, q);
q >>= k;
u = _udiv64(q, 31, &v);
if (u)
{
if (u <= 12)
{
for (t = 0; t < u; t++)
{
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
}
else
{
_BitScanReverse(&Max, u);
Max = (1 << (Max - 1));
for (t = 1; t <= Max; t <<= 1)
{
if (u & t)
{
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
Product = (unsigned long long)Witness * Witness;
_udiv64(Product, P, &Witness);
}
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
}
Product = ((unsigned long long)Remainder << v);
_udiv64(Product, P, &Remainder);
if ((Remainder == 1) || (Remainder == P_1))
goto SecondRound;
for (t = 1; t < k; t++)
{
Product = (unsigned long long)Remainder * Remainder;
_udiv64(Product, P, &Remainder);
if (Remainder == P_1)
goto SecondRound;
}
return false;
SecondRound:
Witness = 1977326743 % P;
Remainder = 1;
u = _udiv64(q, 11, &v);
if (u)
{
if (u <= 12)
{
for (t = 0; t < u; t++)
{
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
}
else
{
_BitScanReverse(&Max, u);
Max = (1 << (Max - 1));
for (t = 1; t <= Max; t <<= 1)
{
if (u & t)
{
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
Product = (unsigned long long)Witness * Witness;
_udiv64(Product, P, &Witness);
}
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
}
Product = (unsigned long long)Remainder * Witness7Power[v];
_udiv64(Product, P, &Remainder);
if ((Remainder == 1) || (Remainder == P_1))
goto ThirdRound;
for (t = 1; t < k; t++)
{
Product = (unsigned long long)Remainder * Remainder;
_udiv64(Product, P, &Remainder);
if (Remainder == P_1)
goto ThirdRound;
}
return false;
ThirdRound:
Witness = 844596301 % P;
Remainder = 1;
u = _udiv64(q, 5, &v);
if (u)
{
if (u <= 12)
{
for (t = 0; t < u; t++)
{
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
}
else
{
_BitScanReverse(&Max, u);
Max = (1 << (Max - 1));
for (t = 1; t <= Max; t <<= 1)
{
if (u & t)
{
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
Product = (unsigned long long)Witness * Witness;
_udiv64(Product, P, &Witness);
}
Product = (unsigned long long)Remainder * Witness;
_udiv64(Product, P, &Remainder);
}
}
Product = (unsigned long long)Remainder * Witness61Power[v];
_udiv64(Product, P, &Remainder);
if ((Remainder == 1) || (Remainder == P_1))
return true;
for (t = 1; t < k; t++)
{
Product = (unsigned long long)Remainder * Remainder;
_udiv64(Product, P, &Remainder);
if (Remainder == P_1)
return true;
}
return false;
}
创建文本格式素数表
void CreateTxtPrimeTable(unsigned int Start = 1, \
unsigned int End = 10000, const char* pFileName = NULL);
/*
★★★★★实用函数。
在当前目录下生成文本格式素数表。可以指定文件位置和文件名,必须符合c++语法
规则,一定要加上.txt这个后缀名,可以不传递参数pFileName,使用默认文件名。
要求End - Start <= 99999999。
*/
void CreateTxtPrimeTable(unsigned int Start, unsigned int End, \
const char* pFileName)
{
unsigned int Min = Start < End ? Start : End;
unsigned int Max = Start < End ? End : Start;
if ((Max - Min) > 99999999)
Max = Min + 99999999;
std::cout << "正在生成文件,计算中......\n";
unsigned long long tk = GetTickCount64();
fstream PrimeTable;
if (pFileName)
PrimeTable.open(pFileName, ios::out);
else
PrimeTable.open("文本格式素数表.txt", ios::out);
unsigned int Prime;
unsigned int Counter = 0;
unsigned int M;
unsigned int S;
for (Prime = Min; Prime <= Max; Prime++)
{
if (FlashPrimeQ_U32(Prime))
{
if (Prime < 100000)
PrimeTable << Prime << " ";
else
PrimeTable << Prime << " ";
Counter++;
}
if (Counter == 10)
{
PrimeTable << "\n";
Counter = 0;
}
/*
Prime < 100000打印两个TAB键,否则打印一个,每10个换行。
*/
}
PrimeTable.close();
tk = GetTickCount64() - tk;
tk /= 1000;
tk++;
M = _udiv64(tk, 60, &S);
std::cout << "文件生成完毕!\n";
std::cout << "大约用时" << M << "分" << S << "秒。\n";
}
创建2^32以内素数表
void CreatePrimeTableUnder4294967296();
/*
◯该函数自动调用。
创建2^32以内素数表,约需要5分钟,约需要800M硬盘空间!不可以修改文件名,不
可以改变文件位置!这个表是给机器用的!
*/
void CreatePrimeTableUnder4294967296()
{
char c;
char str[256];
std::cout << "即将生成“4294967296以内素数表.bin”文件!\
约需要5分钟,约需要800M硬盘空间,请耐心等待!\n";
std::cout << "按下键盘Y键(不区分大小写)和回车键继续,\
其它键和回车键跳过生成素数表!\n>>>>>>等待按键......\n";
scanf_s("%c", &c, 1);
gets_s(str, 256);
if ((c != 'y') && (c != 'Y'))
{
std::cout << "已跳过!没有素数表某些函数将无法运行!\
若其后需要此素数表,您可以手动调用CreatePrimeTableUnder4294967296()函数!\n";
return;
}
std::cout << "正在生成文件,计算中......\n";
unsigned long long tk = GetTickCount64();
fstream PrimeTable("4294967296以内素数表.bin", ios::out | ios::binary);
unsigned int k;
unsigned int M;
unsigned int S;
unsigned int Counter = 0;
/*
使用2,3,5,7筛法,会丢掉201之前的46个素数,手工写进去。4294967291是
2^32以内最大素数,算一下循环到20452224就可以了,会丢掉最后两个, 手工
写进去。不能循环到20452225,否则最后一轮循环溢出32bit,会重复写几个小
素数。
*/
unsigned int First46Primes[46] = {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199
};
PrimeTable.write((char*)&First46Primes[0], 184);
for (k = 1; k <= 20452224; k++)
{
M = 210 * k;
S = M + 1; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 11; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 13; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 17; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 19; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 23; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 29; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 31; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 37; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 41; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 43; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 47; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 53; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 59; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 61; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 67; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 71; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 73; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 79; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 83; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 89; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 97; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 101; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 103; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 107; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 109; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 113; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 121; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 127; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 131; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 137; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 139; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 143; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 149; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 151; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 157; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 163; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 167; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 169; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 173; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 179; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 181; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 187; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 191; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 193; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 197; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 199; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
S = M + 209; if (StdPrimeQ_U32(S)) PrimeTable.write((char*)&S, 4);
if (k % 204522 == 0)
{
Counter++;
if (Counter < 10)
printf_s(" %I32u%%完成 ", Counter);
else if (Counter < 100)
printf_s(" %I32u%%完成 ", Counter);
else
printf_s("%I32u%%完成 ", Counter);
if (Counter % 10 == 0)
printf_s("\n");
}
}
S = 4294967279;
PrimeTable.write((char*)&S, 4);
S = 4294967291;
PrimeTable.write((char*)&S, 4);
PrimeTable.close();
tk = GetTickCount64() - tk;
tk /= 1000;
tk++;
M = _udiv64(tk, 60, &S);
std::cout << "“4294967296以内素数表.bin”文件生成完毕!\n";
std::cout << "大约用时" << M << "分" << S << "秒。\n";
}
简单测试:
int main()
{
unsigned int P = 142881523;
if (FlashPrimeQ_U32(P))
std::cout << "是素数" << endl;
else
std::cout << "是合数" << endl;
}
最后附上古老的试除法和筛法代码。
bool DemoPrimeQ_U32(unsigned int P);
/*
⨉教学函数。
使用最原始的试除法测试P是否为素数。
返回值:
P为素数:true。
P为合数:false。
*/
bool DemoPrimeQ_U32(unsigned int P)
{
/*
if ((P == 0) || (P == 1))
return false;
bool isPrime = true;
unsigned int k;
unsigned int LIMIT = (unsigned int)sqrt(P);
for (k = 2; k <= LIMIT; k++)
{
if (P % k == 0)
{
isPrime = false;
break;
}
}
return isPrime;
*/
/*
以上这些注释掉的代码是基本算法,先读懂它。 下面的代码使用素数2和3两个
筛子,把速度加快约3倍。
*/
switch (P)
{
case 0:
case 1:
case 4:
return false;
case 2:
case 3:
case 5:
return true;
default:
break;
}
/*
用2和3两个筛子,那就是模6步进。先筛2和3,如果整除,那就是合数,如果不
整除,那么其后的含有因子2和3的数都没有必要进行测试。6*n、6*n + 1、
6*n + 2、6*n + 3、6*n + 4、6*n + 5,n从0开始循环,包括了所有数, 因为
只有6*n + 1和6*n + 5不含有因子2和3。但是n不能从0开始循环, 否则遇到的
第一个数就是6*0 + 1 = 1,1整除任何数,程序会失败。 n必须从1开始循环,
这就会漏掉素数5,所以先把5也筛掉。
*/
if (P % 2 == 0)
return false;
if (P % 3 == 0)
return false;
if (P % 5 == 0)
return false;
/*
如果P就是2,3或者5,前面的switch语句直接判定结果返回。
*/
bool isPrime = true;
/*
先假定是素数,一旦测试为合数,则置false。
*/
unsigned int LIMIT = (unsigned int)sqrt(P) / 6;
unsigned int k;
unsigned int n;
/*
下边的代码,一看就懂,但是要思考以下几个问题。
1.循环里的最大的数6*LIMIT + 5确保大于等于根号P的向下取整吗? 因为至少
要循环到根号P的向下取整这个数才行。
2.循环里的最大的数6*LIMIT + 5确保严格小于P吗?因为万一某个6*k + 1或者
6*k + 5恰好等于P,if语句成立判定为合数,可这是错的。 自己整除自己不能
作为依据,不能因为11整除11判定11是合数吧?所以必须确保6*LIMIT + 5严格
小于P。
3.如果P小于等于35,那么LIMIT = 0, 不满足循环的初始条件循环不会执行,
此时这个代码正确吗?
*/
for (k = 1; k <= LIMIT; k++)
{
n = 6 * k;
if (P % (n + 1) == 0)
{
isPrime = false;
break;
}
if (P % (n + 5) == 0)
{
isPrime = false;
break;
}
}
return isPrime;
/*
6是怎样被判定为合数的?第一个if (6 % 2 == 0)就判定了。
7是怎样被判定为素数的?试除法试除到根号7的向下取整就可以, 也就是说测
试到2就行,第一个if (7 % 2 == 0)不成立,按理说就可以结束了。 但是程序
是继续运行,先假定了isPrime为true(素数),计算出LIMIT = 0,for循环不
满足初始条件,跳过,没有代码改变过isPrime的初始假定,7被判定为素数。
31是怎样被判定为素数的?同理,试除到根号31的向下取整5就可以,前面三个
if刚好筛到5,下面的for循环又不满足初始条件,跳过,31被判定为素数。
3613是怎样被判定为素数的?2,3,5都不是因子,LIMIT = 10,k从1开始循环
到10,测试以下这些数:7,11,13,17,19,23,25,29,31,35,37,41,
43,47,49,53,55,59,61,65,全部不满足, 65大于根号3613的向下取整
值60,3613被判定为素数。
4087怎样被判定为合数的?2,3,5都不是因子,LIMIT = 10,k从1开始循环到
10,测试以下这些数:7,11,13,17,19,23,25,29,31,35,37,41,
43,47,49,53,55,59,61,65,当测试到61时满足条件,isPrime被改写为
false,终止循环,判定合数返回。
*/
}