一文说尽32bit素数检测(幂模运算,米勒拉宾算法,素数检测)

视频讲解:等视频审核通过之后才能看到。

【免费】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,终止循环,判定合数返回。
	*/
}

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋培林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值