数论 - 分解质因数+欧拉函数
一、分解质因数
由 算 术 基 本 定 理 , 任 何 一 个 大 于 1 的 自 然 数 N , 如 果 N 不 为 质 数 , 那 么 N 可 以 唯 一 分 解 成 有 限 个 质 数 的 乘 积 N = P 1 a 1 P 2 a 2 P 3 a 3 . . . . . . P n a n , 这 里 P 1 < P 2 < P 3 . . . . . . < P n 均 为 质 数 , 其 中 指 数 a i 是 正 整 数 。 现 给 定 一 个 正 整 数 n , 可 以 以 小 于 O ( n ) 的 时 间 复 杂 度 得 到 其 所 有 的 质 因 子 及 每 个 质 因 子 对 应 的 幂 。 即 得 到 P 1 , P 2 , . . . , P n 和 a 1 , a 2 , . . . , a n 。 同 质 数 判 定 的 方 法 类 似 , 需 要 另 外 注 意 两 点 : ① 、 依 然 是 i 从 2 开 始 枚 举 每 个 质 因 子 , 每 次 枚 举 到 一 个 质 因 子 就 从 n 中 把 i 除 尽 , 这 样 可 以 保 证 合 数 因 子 被 除 掉 。 ② 、 对 任 意 的 n , 至 多 仅 有 一 个 大 于 n 的 质 因 子 。 因 为 显 然 假 设 大 于 n 的 质 因 子 数 量 大 于 等 于 2 , 那 么 这 两 个 质 因 子 乘 积 必 然 大 于 n 。 有 了 上 面 这 个 性 质 , 我 们 每 次 枚 举 质 因 子 i , 只 需 要 循 环 到 n i 次 即 可 , 若 最 后 n > 1 , 说 明 存 在 一 个 比 n 大 的 质 因 子 , 就 是 此 时 的 “ n ” 。 由 于 每 次 枚 举 一 个 i , 会 把 n 中 的 i 因 子 除 尽 , 总 的 时 间 复 杂 度 是 介 于 O ( l o g 2 n ) − O ( n ) 之 间 的 。 由算术基本定理,任何一个大于1的自然数 N,如果N不为质数,\\那么N可以唯一分解成有限个质数的乘积N=P_1^{a_1}P_2^{a_2}P_3^{a_3}......P_n^{a_n},\\这里P_1<P_2<P_3......<P_n均为质数,其中指数a_i是正整数。\\ \ \\现给定一个正整数n,可以以小于O(\sqrt{n})的时间复杂度得到其所有的质因子及每个质因子对应的幂。\\即得到P_1,P_2,...,P_n和a_1,a_2,...,a_n。\\ \ \\同质数判定的方法类似,需要另外注意两点:\\① 、依然是i从2开始枚举每个质因子,每次枚举到一个质因子就从n中把i除尽,这样可以保证合数因子被除掉。\\ \ \\②、对任意的n,至多仅有一个大于\sqrt{n}的质因子。\\因为显然假设大于\sqrt{n}的质因子数量大于等于2,那么这两个质因子乘积必然大于n。\\ \ \\有了上面这个性质,我们每次枚举质因子i,只需要循环到\frac{n}{i}次即可,\\若最后n>1,说明存在一个比\sqrt{n}大的质因子,就是此时的“n”。\\由于每次枚举一个i,会把n中的i因子除尽,总的时间复杂度是介于O(log_2n)-O(\sqrt{n})之间的。 由算术基本定理,任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积N=P1a1P2a2P3a3......Pnan,这里P1<P2<P3......<Pn均为质数,其中指数ai是正整数。 现给定一个正整数n,可以以小于O(n)的时间复杂度得到其所有的质因子及每个质因子对应的幂。即得到P1,P2,...,Pn和a1,a2,...,an。 同质数判定的方法类似,需要另外注意两点:①、依然是i从2开始枚举每个质因子,每次枚举到一个质因子就从n中把i除尽,这样可以保证合数因子被除掉。 ②、对任意的n,至多仅有一个大于n的质因子。因为显然假设大于n的质因子数量大于等于2,那么这两个质因子乘积必然大于n。 有了上面这个性质,我们每次枚举质因子i,只需要循环到in次即可,若最后n>1,说明存在一个比n大的质因子,就是此时的“n”。由于每次枚举一个i,会把n中的i因子除尽,总的时间复杂度是介于O(log2n)−O(n)之间的。
例题:
给定n个正整数ai,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。
输入格式
第一行包含整数n。
接下来n行,每行包含一个正整数ai。
输出格式
对于每个正整数ai,按照从小到大的顺序输出其分解质因数后,每个质因数的底数和指数,每个底数和指数占一行。
每个正整数的质因数全部输出完毕后,输出一个空行。
数据范围
1≤n≤100,
1≤ai≤2∗109
输入样例:
2
6
8
输出样例:
2 1
3 1
2 3
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,x;
void divide(int x)
{
for(int i=2;i<=x/i;i++)
if(x%i==0)
{
int cnt=0;
while(x%i==0)
{
x/=i;
cnt++;
}
cout<<i<<" "<<cnt<<endl;
}
if(x>1) cout<<x<<" "<<1<<endl;
cout<<endl;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x;
divide(x);
}
return 0;
}
二、欧拉函数
对 于 正 整 数 n , 用 欧 拉 函 数 能 够 求 得 [ 1 , n − 1 ] 中 与 n 互 质 的 数 的 个 数 。 对 正 整 数 n = p 1 a 1 p 2 a 2 . . . p n a n , 欧 拉 函 数 ϕ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p n ) , 其 中 , p 1 , p 2 , . . . , p n 是 n 的 质 因 子 。 原 理 : 直 白 地 讲 , p i 是 n 的 任 意 质 因 子 , 设 [ 1 , n ] 中 , 是 p i 的 倍 数 的 数 ( p i , 2 p i , . . . ) 的 个 数 是 a i , 那 么 [ 1 , n − 1 ] 中 与 n 互 质 的 数 的 个 数 = n − ∑ a i , 而 a i = n p i , 借 助 容 斥 原 理 得 到 的 式 子 即 ϕ ( n ) 乘 积 展 开 的 式 子 。 求 正 整 数 n 的 欧 拉 函 数 , 我 们 可 以 利 用 分 解 质 因 数 的 方 法 求 得 n 的 所 有 质 因 子 , 再 代 入 公 式 即 可 。 时 间 复 杂 度 仍 然 是 小 于 O ( n ) 的 。 对于正整数n,用欧拉函数能够求得[1,n-1]中与n互质的数的个数。\\ \ \\对正整数n=p_1^{a_1}p_2^{a_2}...p_n^{a_n},欧拉函数\phi(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_n}),其中,p_1,p_2,...,p_n是n的质因子。\\ \ \\原理:直白地讲,p_i是n的任意质因子,设[1,n]中,是p_i的倍数的数(p_i,2p_i,...)的个数是a_i,\\ \ \\那么[1,n-1]中与n互质的数的个数=n-\sum a_i,而a_i=\frac{n}{p_i},借助容斥原理得到的式子即\phi(n)乘积展开的式子。\\ \ \\求正整数n的欧拉函数,我们可以利用分解质因数的方法求得n的所有质因子,再代入公式即可。\\时间复杂度仍然是小于O(\sqrt{n})的。 对于正整数n,用欧拉函数能够求得[1,n−1]中与n互质的数的个数。 对正整数n=p1a1p2a2...pnan,欧拉函数ϕ(n)=n(1−p11)(1−p21)...(1−pn1),其中,p1,p2,...,pn是n的质因子。 原理:直白地讲,pi是n的任意质因子,设[1,n]中,是pi的倍数的数(pi,2pi,...)的个数是ai, 那么[1,n−1]中与n互质的数的个数=n−∑ai,而ai=pin,借助容斥原理得到的式子即ϕ(n)乘积展开的式子。 求正整数n的欧拉函数,我们可以利用分解质因数的方法求得n的所有质因子,再代入公式即可。时间复杂度仍然是小于O(n)的。
例题:
给定n个正整数ai,请你求出每个数的欧拉函数。
输入格式
第一行包含整数n。
接下来n行,每行包含一个正整数ai。
输出格式
输出共n行,每行输出一个正整数ai的欧拉函数。
数据范围
1≤n≤100,
1≤ai≤2∗109
输入样例:
3
3
6
8
输出样例:
2
2
4
注意:
为 了 避 免 计 算 过 程 中 小 数 的 出 现 , 将 n ( 1 − 1 p i ) 转 化 为 n / p i × ( p i − 1 ) 。 为了避免计算过程中小数的出现,将n(1-\frac{1}{p_i})转化为n/p_i×(p_i-1)。 为了避免计算过程中小数的出现,将n(1−pi1)转化为n/pi×(pi−1)。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,a;
int main()
{
cin>>n;
while(n--)
{
cin>>a;
int ans=a;
for(int i=2;i<=a/i;i++)
{
if(a%i==0)
{
ans=ans/i*(i-1);
while(a%i==0) a/=i;
}
}
if(a>1) ans=ans/a*(a-1);
cout<<ans<<endl;
}
return 0;
}
三、模板: Relatives POJ - 2407
题意:
给 定 正 整 数 n , 输 出 [ 1 , n − 1 ] 内 , 与 n 互 质 的 数 的 个 数 。 0 为 输 入 结 尾 。 给定正整数n,输出[1,n-1]内,与n互质的数的个数。0为输入结尾。 给定正整数n,输出[1,n−1]内,与n互质的数的个数。0为输入结尾。
Sample Input:
7
12
0
Sample Output:
6
4
数据范围:
n < = 1 0 9 , T i m e l i m i t : 1000 m s , M e m o r y l i m i t : 65536 k B n<=10^9,\\Time\ limit:1000 ms,Memory\ limit:65536 kB n<=109,Time limit:1000ms,Memory limit:65536kB
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
int euler(int n)
{
int res=n;
for(int i=2;i<=n/i;i++)
{
if(n%i==0)
{
res=res/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) res=res/n*(n-1);
return res;
}
int main()
{
while(~scanf("%d",&n),n)
printf("%d\n",euler(n));
return 0;
}