求原根

#include<iostream>
#include<fstream>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 50;
const int MAXN = 10000000;
int prime[maxn]; // 模数的质因子
int e[maxn]; // 判别因子
int cnt = 0; // 模数的质因子数,也等于判别因子数
int bcode[maxn][maxn]; // 判别因子的二进制数,第一个元素记录二进数长度
int srs[MAXN]; // 简化剩余系(Simplified residual system)
int answer[MAXN];
ofstream OutFile;

int euler_phi(int n) {
	int m = (int)sqrt(n + 0.5);
	int ans = n;
	for (register int i = 2; i <= m; i++)
		if (n%i == 0)
		{//最好要先除后乘,防止结果溢出
			ans = ans / i * (i - 1);
			while (n%i == 0)n = n / i;//将n中所有因子i筛去 
							 //确保下一个i是n的质因子 
		}
	if (n > 1)ans = ans / n * (n - 1);//防止n为最后一个质因子 
	return ans;
}

int partition(int n, int* prime) //分解模因数,并求出该模下的原根判别因子
{
	int num = n;
	int cnt = 0;
	for (int i = 2; i <= sqrt(n); i++)
		if (num%i == 0)
		{
			prime[cnt++] = i;
			while (num%i == 0)
				num /= i;
		}
	for (int i = 0; i < cnt; i++) //求判别因子
		e[i] = n / prime[i];
	return cnt;
}

void genbcode(int n, int bcode[][50]) //生成判别因子的二进制数,此处注意二维数组传参
{
	for (int i = 0; i < n; i++)
	{
		int k = 0;
		int num = e[i];
		while (num)
		{
			bcode[i][++k] = num & 1;
			num = num >> 1;
		}
		bcode[i][0] = k;
	}
}

int powerMod(int e, int b, int m) //e为指数
{
	int a = 1;
	int i, k = 0, num = e;
	/*计算指数的二进制位数k.*/
	while (num)
	{
		num = num >> 1;
		++k;
	}

	for (i = 0; i < k; ++i)
	{
		// 取e的二进制的第i位,判断是否为1.
		if ((e >> i) & 1)
			a = a * b %m;
		b = b * b %m;
	}
	return a;
}

int powerMod(int* p, int b, int n, int m) // 模重复平方算法,*p存储一个数的二进制数,n为p中的元素数,b为底数,m为模
{
	int a = 1;
	for (int i = 1; i <= n; ++i)
	{
		if (p[i])
			a = a * b %m;
		b = b * b %m;
	}
	return a;
}

bool judge(int b, int m) // 判断b是否为模p的原根
{
	for (int i = 0; i < cnt; i++)
		if (powerMod(bcode[i], b, bcode[i][0], m) == 1) return false;
	return true;
}

int PR(int m) //求m的原根
{
	cnt = partition(euler_phi(m), prime);
	genbcode(cnt, bcode);
	int b = 1;
	while (!judge(++b, m));
	return b;
}

//int squarePR(int m) //求m^2的原根
//{
//	int pr = PR(m);
//	if (powerMod(m - 1, pr, m*m) == 1) return pr + m; // pr表示初始的原根,此处有定理:pr+m或pr为m^2的原根
//	return pr;
//}

void SRS(int n, int* srs) //求n的简化剩余系,存储在srs[]中
{
	for (int i = 0; i < cnt; i++)
	{
		int j = 1;
		while (prime[i] * j <= n)
		{
			srs[prime[i] * j] = 1; //删去质因数的倍数
			j++;
		}
	}
}

int findAll(int pr, int m)//找到m的所有原根
{
	int total = 0;
	SRS(euler_phi(m), srs);
	for (int i = 1; i <= euler_phi(m); i++) if (!srs[i])
	{
		int arr[maxn], k = 0, num = i;
		while (num)
		{
			arr[++k] = num & 1;
			num = num >> 1;
		}
		answer[total++] = powerMod(arr, pr, k, m*m);
	}
	sort(answer, answer + total);
	return total;
}

int main()
{
	int m, ans;
	cout << "输入模数:";
	cin >> m;
	int pr = PR(m); //求出初值
	cout << pr << "是模" << m << "的原根" << endl;
	prime[cnt++] = m;
	OutFile.open("result.txt");
	ans = findAll(pr, m);
	OutFile << "一共有" << ans << "个原根" << endl;
	for (int i = 0; i < ans; i++)
	{
		if (i % 10 == 0 && i) OutFile << endl;
		OutFile << answer[i] << '\t';
	}
	OutFile.close();
	system("pause");
}

参考

模重复平方计算法

学习笔记--简化剩余系与欧拉函数φ( )

C++中简单的文本文件输入/输出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值