定义
在数论,特别是整除理论中,原根是一个很重要的概念。
对于两个正整数 , 由欧拉定理可知,存在正整数 , 比如说欧拉函数 ,即小于等于 的正整数中与 互素的正整数的个数,使得 。
由此,在 时,定义 对模 的指数 为使 成立的最小的正整数 。由前知 一定小于等于 ,若 ,则称 是模 的原根。
例子
设 ,则 等于6。
- 设
,由于 ,而 ,所以 2 不是模 7 的一个原根。 - 设
,由于 , , , , , ,因此有 ,所以 3 是模 7 的一个原根。
性质
- 可以证明,如果正整数
和正整数 d 满足 ,则 整除 d。因此 整除 。在例子中,当 时,我们仅需要验证 3 的 2、3 次方模 7 的余数即可,如果其中有一个是1,则3就不是原根。
- 记
,则 模 m 两两不同余。因此当 是模{\displaystyle m} 的原根时, 构成模 m 的简化剩余系。
- 模
有原根的充要条件是 ,其中 是奇素数, 是任意正整数。
- 对正整数
,如果 a 是模 m 的原根,那么 a 是整数模m乘法群(即加法群 Z/mZ 的可逆元,也就是所有与 m 互素的正整数构成的等价类构成的乘法群)Zm×的一个生成元。由于Zm×有 个元素,而它的生成元的个数就是它的可逆元个数,即 个,因此当模 有原根时,它有 个原根。
一些数的原根列表
m | 模m的原根(有*号的数没有原根,此时是有最大模m周期的数) | 周期 ( A002322) | 1 | 0 | 1 | 2 | 1 | 1 | 3 | 2 | 2 | 4 | 3 | 2 | 5 | 2, 3, 4 | 4 | 6 | 5 | 2 | 7 | 3, 5 | 6 | 8* | 3, 5, 7 | 2 | 9 | 2, 5 | 6 | 10 | 3, 7 | 4 | 11 | 2, 6, 7, 8 | 10 | 12* | 5, 7, 11 | 2 | 13 | 2, 6, 7, 11 | 12 | 14 | 3, 5 | 6 | 15* | 2, 7, 8, 13 | 4 | 16* | 3, 5, 11, 13 | 4 | 17 | 3, 5, 6, 7, 10, 11, 12, 14 | 16 | 18 | 5, 11 | 6 | 19 | 2, 3, 10, 13, 14, 15 | 18 | 20* | 3, 7, 13, 17 | 4 | 21* | 2, 5, 10, 11, 17, 19 | 6 | 22 | 7, 13, 17, 19 | 10 | 23 | 5, 7, 10, 11, 14, 15, 17, 19, 20, 21 | 22 | 24* | 5, 7, 11, 13, 17, 19, 23 | 2 | 25 | 2, 3, 8, 12, 13, 17, 22, 23 | 20 | 26 | 7, 11, 15, 19 | 12 | 27 | 2, 5, 11, 14, 20, 23 | 18 | 28* | 3, 5, 11, 17, 19, 23 | 6 | 29 | 2, 3, 8, 10, 11, 14, 15, 18, 19, 21, 26, 27 | 28 | 30* | 7, 13, 17, 23 | 4 | 31 | 3, 11, 12, 13, 17, 21, 22, 24 | 30 | 32* | 3, 5, 11, 13, 19, 21, 27, 29 | 8 | 33* | 2, 5, 7, 8, 13, 14, 17, 19, 20, 26, 28, 29 | 10 | 34 | 3, 5, 7, 11, 23, 27, 29, 31 | 16 | 35* | 2, 3, 12, 17, 18, 23, 32, 33 | 12 | 36* | 5, 7, 11, 23, 29, 31 | 6 |
除了直接运算以外,至今还没有一个办法可以找到模特定m时的原根,但假如已知模m有一个原根,则可找出它其他的原根。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int Maxn = 1e6+10;
const int N = 1e8+10;
int prime[Maxn];//存储素数
int sprime[Maxn];//存储P-1的素因子
bool check[Maxn];
bitset<Maxn>pri;//结果只有0和1,判断是否为素数
int k;//记录Maxn以内的素数个数
int cnt;//记录素因子的个数
void is_prime(){
pri.set();//将所有的二进制数都标为1
for(int i=2; i<Maxn; i++) {
if(pri[i]) {
prime[k++]=i;
for(int j=i+i; j<Maxn; j+=i)
pri[j]=0;
}
}
}
void Divide(int n)//将n分解为素因子
{
cnt=0;
int t=(int)sqrt(1.0*n);
for(int i=0; prime[i]<=t; i++) {
if(n%prime[i]==0) {
sprime[cnt++]=prime[i];
while(n%prime[i]==0)//因为有可能有多个peime[i]
n/=prime[i];
}
}
if(n>1)
sprime[cnt++]=n;//可能只有自己一个素因子
}
LL modexp(LL a,LL b,int mod)//快速幂取余
{
LL res=1;
while(b>0) {
a=a%mod;
if(b&1)
res=res*a%mod;
b=b>>1;
a=a*a%mod;
}
return res;
}
int main()
{
int p;
Is_prime();
while(~scanf("%d",&p)) {
Divide(p-1);
for(int g=2; g<p; g++) {
int flag=1;
for(int i=0; i<cnt; i++) {
int t=(p-1)/sprime[i];
if(modexp(g,t,p)==1) {
flag=0;
break;
}
}
if(flag) {
int root=g;
printf("%d\n",root);
break;//去掉break的话是求所有的原根,加上break是求最小的原根、
}
}
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
typedef long long ll;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
ll n;
ll get(ll n){
ll ans = n;
for(int i=2;i*i<=n;i++){
if(n %i == 0){
ans = ans / i * (i - 1);
while(n %i == 0)
n /= i;
}
}
if(n > 1) ans = ans / n * (n - 1);
return ans ;
}
int main(){
while(scanf("%lld",&n)!=EOF){
printf("%lld\n",get(get(n)));
}
return 0;
}
|