原根相关

原根,定义为,对一给定模数p(p是奇质数),若有一g,满足g1−p−1g^{1-p-1}g1p1两两不同,则称g为p的原根。

不会证明的结论QAQ:
奇质数p一定存在原根,而且通过打表我们可以发现最小的原根都很小。

task1:如何判断一个数x是否为p的一个原根?

费马小定理:xp−1=1(p为质数)x^{p-1}=1(p为质数)xp1=1(p)

假设x1−p−1x^{1-p-1}x1p1不是互不相同,设有一最小的y使xy=1x^y=1xy=1,则必有y&lt;p−1y&lt;p-1y<p1

证明非常简单:
假设一个数iii(i∗x) mod p(i*x)~mod~p(ix) mod p连有向边,我们从i=1,沿着边走,由于在走了p-1步后回到了1,每个点只有一条出边,所以我们可以确定1走p-1步一定是重复经过一个长度为y(y|p-1)的环(p-1)/y次,所以如果不是互不相同,那么肯定可以在y(y<p-1)步时找到一个1。

这样看上去我们需要枚举p-1的所有约数来判断,好麻烦啊。

p=∏piqip=\prod p_i^{q_i}p=piqi,因为y∣p−1,y&lt;p−1y|p-1,y&lt;p-1yp1,y<p1,所以一定有一p/pip/p_ip/pi是y的倍数。

又因为xy∗y′=(xy)y′=1y′=1x^{y*y&#x27;}=(x^{y})^{y&#x27;}=1^{y&#x27;}=1xyy=(xy)y=1y=1,所以只需要判断有没有xp/pi=1x^{p/p_i}=1xp/pi=1就行了。

复杂度可以看作O(log22p)O(log_{2}^2p)O(log22p)

task2:原根的个数=φ(p-1)

假设我们已经找到了最小的原根g,我们知道g1−p−1g_{1-p-1}g1p1是互不相同的。

所以若有其他原根g′≠gg&#x27;≠gg̸=g,不妨用gx=g′g^x=g&#x27;gx=g的形式来表示

这样有什么好处呢?

g′1−p−1g&#x27;^{1-p-1}g1p1互不相同,相当于(gx)1−p−1=gx∗(1−p−1)(g^{x})^{1-p-1}=g^{x*(1-p-1)}(gx)1p1=gx(1p1)互不相同,即x∗(1−p−1)(mod (p−1))x*(1-p-1)(mod~(p-1))x(1p1)(mod (p1))互不相同,那显然gcd(x,p−1)=1gcd(x,p-1)=1gcd(x,p1)=1,那么x的个数即为φ(p−1)φ(p-1)φ(p1)

所有的原根都可以用gx(gcd(x,p−1)=1)g_{x}(gcd(x,p-1)=1)gx(gcd(x,p1)=1)的形式表示,特殊地,当x=1时,就是g自己。

实际运用:找NTT模数和对应原根

我们首先要清楚NTT模数是什么样的:
p是NTT模数,则p是质数,可以分解为p=2x∗y+1p=2^x*y+1p=2xy+1,且x比较大,一般x>=21

不难想到枚举x,y,然后判断p=2x∗y+1p=2^x*y+1p=2xy+1是否为质数,然后再从小到大枚举数,判断是否为原根,找到即退即可。

示例:

#include<cstdio>
#define pp printf
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

int gcd(int x, int y) {
	return !y ? x : gcd(y, x % y);
}

ll ksm(ll x, ll y, const ll mo) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

int p;

int u[20], v[20], u0;

void fen(int x) {
	u0 = 0;
	for(int i = 2; i * i <= x; i ++) if(x % i == 0) {
		u[++ u0] = i; v[u0] = 0;
		while(x % i == 0) x /= i, v[u0] ++;
	}
	if(x > 1) u[++ u0] = x, v[u0] = 1;
}

int phi(int x) {
	fen(x);
	fo(i, 1, u0) x = x / u[i] * (u[i] - 1);
	return x;
}

int p2, gp, g;

int main() {
	fo(i, 21, 30) fo(j, 1, 1024) {
		ll s = (1ll << i) * j + 1;
		if(s >= (1ll << 31) || s < 9e8) continue;
		p = s;
		p2 = phi(p);
		if(p2 != p - 1) continue;
		gp = phi(p2);
		g = -1;
		fo(i, 2, p - 1) if(gcd(i, p) == 1) {
			int bz = 1;
			fo(j, 1, u0) if(ksm(i, p2 / u[j], p) == 1) {
				bz = 0; break;
			}
			if(bz) { g = i; break;}
		}
		pp("%d %d %d\n", p, g, gp);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值