欧拉函数-求欧拉数
重要点:
1----N中与N互制的个数
1.先求质因数
质因数公式的边界:
for(int i=2;i*i<=n;i++){
用质因数来使用公式进行求解
某个数N的欧拉函数是一个整数:表示与N互质的个数
1∼N 中与 N 互质的数的个数被称为欧拉函数,记为 ϕ(N)。
若在算数基本定理中:
N=p1^a1*p2^a2*..Pk^ak,则:
--》这个是需要使用分解质因子进行求解
ϕ(N) =N*(1-1/p1)*(1-1/p2)...*(1-1/pk)
证明:
1 2 3每个的欧拉函数
代码:
结构1-使用函数:
使用了map
1.map<Interger,Interger> map
一个存储质因子,一个存储指数。
2.遍历的时候需要注意公式的应用,p为质因子
res = x;
res = res / p*(p-1);
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Main {
//输入格式
//第一行包含整数 n。
//接下来 n 行,每行包含一个正整数 ai
//输出格式
//输出共 n 行,每行输出一个正整数 ai 的欧拉函数。
static int N = 1000000007;
public static Map euler(int n){
HashMap<Integer,Integer> map = new HashMap<>();
//分解质因子
for(int i=2;i*i<=n;i++){
while (n%i==0){
n=n/i;
map.put(i,map.getOrDefault(i,0)+1);
}
}
if(n>1) map.put(n,map.getOrDefault(n,0)+1);
return map;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while (n-->0){
int x = sc.nextInt();
long res = x;
Map<Integer,Integer> mp = euler(x);
//输出使用公式进行
for(Map.Entry<Integer,Integer> map:mp.entrySet()){
int key = map.getKey();
res =( res/key) *(key-1);
}
System.out.println(res);
}
}
}
代码2
package 数学知识;
import javax.swing.plaf.synth.SynthUI;
import java.util.Scanner;
public class 欧拉数3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = 1;
while (n-->0){
int x = 8;
long res = x;
for(int i=2;i*i<=x;i++){
if(x%i==0){
while (x%i==0){
x = x/i;
}
res = res/i*(i-1);
}
}
if(x>1) res = res/x*(x-1);
System.out.print(res);
}
}
}
线性筛-筛欧拉-欧拉数之和。
简单方法:
package 数学知识;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import static 数学知识.欧拉数.get_euler;
public class 欧拉数之和_多个 {
public static Map get_euler(int n){
HashMap<Integer,Integer> map = new HashMap<>();
for(int i=2;i<=n;i++){//
while (n%i==0){
n = n/i;
map.put(i,map.getOrDefault(i,0)+1);
}
}
if(n>1)map.put(n,map.getOrDefault(n,0)+1);
return map;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n= sc.nextInt();
while (n-->0){
int x = sc.nextInt();
long ans = 0;
for(int i=1;i<=x;i++){
long res = i;
Map<Integer,Integer> mapp= get_euler(i);
for(Map.Entry<Integer,Integer> map:mapp.entrySet()){
int p = map.getKey();
res = res/p*(p-1);
}
ans = ans +res;
}
System.out.println(ans);
}
}
}
- 线性筛法进行求数
边界是i<=n
for(int i=2;i<=n;i++){
不改动线性筛法,再进行求解
1.一个质素p的欧拉数
例如:
5:
1,2,3,4
6:
1,5
所以对于质数p欧拉数:p-1:1 2....p-1
问题:怎么使用线性筛的过程中进行呢。
1.
i%pj==0
说明pj是i的一个质因子,所以使用欧拉公式及性能累次求解时
线性筛法的公式:
for (int j = 0; primes[j] <= n / i; j ++)
{
st[primes[j] * i] = true;
if (i % primes[j] == 0)
{
break;
}
如果加入了欧拉数的选择,在使用公式的时候需要的是
for (int j = 0; primes[j] <= n / i; j ++)
{
st[primes[j] * i] = true;
if (i % primes[j] == 0)
{
//说明prime[j]是i的质因子。所以使用欧拉函数和展开,并且primes[j]*i和i的质因子相同也就是phi相同。多承phi【i】
phi[primes[j]*i] = phi[i]*primes[j];
}
又因为欧拉公式使用的质因子,不会使用幂数,所以,i和i*p所使用的质因子是一样的
所以根据公式进行化简
只有系数不一样,所以
fai(pj,i) = pj fai(i)
2.
i%pj !=0:
premes[j]一定是i*primes[j]的最小质因子,并且primes[j] 不包含在i的质因子中。
所以
phi[i]中没有primes[j]这个数。
所以
phi[primes[j]*i] =:N= primes[j]*i 并且有primes[j]这个质因子
=primes[j]*i*(1-1/p1)*...*(1-1/pk)*(1-1/primes[j])
上面这个中间含有一个phi[i]
所以
phi[primes[j]*i] = primes[j]*phi[i]*(1-1/primes[j])=phi[i] * (premes[j]-1)
for (int j = 0; primes[j] <= n / i; j ++)
{
st[primes[j] * i] = true;
if (i % primes[j] == 0)
{
phi[primes[j] * i] = phi[i] * primes[j];
break;
}
//这个地方
phi[primes[j] * i] = phi[i] * (primes[j] - 1);
}
代码:
1.要看清楚是边界是i<=n
package 数学知识;
import java.awt.*;
import java.util.Scanner;
public class 线性筛欧拉数 {
static int N = 100010;
static boolean[] st=new boolean[N];
static int[] primes = new int[N];
static int[] phi = new int[N];
public static long get_euler(int n){
int num=0;
phi[1] =1;
//&&&这个地方是n
for(int i=2;i<=n;i++){
if(st[i] == false) {
primes[num++] = i;
//初始化
phi[i] = i-1;//质数的欧拉数i-1
}
for(int j=0;i*primes[j]<=n;j++){
st[primes[j]*i]=true;
if(i%primes[j] == 0) {
phi[primes[j]*i] = primes[j]*phi[i];
break;
}
phi[primes[j]*i] = phi[i]*(primes[j]-1);
}
}
long res =0;
//欧拉数从1开始
for(int i=1;i<=n;i++){
System.out.println(phi[i]);
res = res+phi[i];
}
return res;
}
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
int n = 6;
long x = get_euler(n);
System.out.println(x);
}
}
快速幂运算
- 暴力解法:
循环进行解决
package 数学知识;
import java.util.Scanner;
public class 快速幂运算 {
//预处理快速幂,进行简化计算
//给定 n 组 ai,bi,pi,对于每组数据,求出 abiimodpi 的值。
static int mod = 1000000009;
public static void quick_mi(int a,int b,int p){
long ans =1;
for(int i=1;i<=b;i++){
ans = ans*a%p;
}
System.out.println(ans);
}
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
int n = sc.nextInt();
while (n-->0){
int a = sc.nextInt();
int b = sc .nextInt();
int p = sc.nextInt();
quick_mi(a,b,p);
}
}
}
- 二进制预处理解决
使用之前计算的数,可以进行缩短计算时间,避免重复计算。
小技巧:
当把a删掉的同时,a也要随着进制的转换变换值,跟十进制类似。
110
从右边开始,进行第一位是2^0=1
第二位是2^1=2
第三位...
代码:
注意事项:
1.涉及到乘积类的算法,一般使用的都是long性的数据结构
2.模数据的时候,一定要看清楚
3.b右移一位时,也就是b=b/2
4.b判断当前位是不是1时,也就是判断b%==1
import java.util.Scanner;
public class Main {
//给定 n 组 ai,bi,pi,对于每组数据,求出 abiimodpi 的值。
public static long quick_mi_er(long a,long b,long p){
long res =1;
//进行循环
while (b!=0){
if(((b&1)==1))res = res*a%p;//如果有1,这一位
//上面如果有一再执行,但是这个不是
//右姨一位
b = b>>1;
//更新进制值,一旦右移,代表的值就会变大
a = a*a%p;
}
return res;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while (n-->0){
long a = sc.nextLong();
long b = sc.nextLong();
long p = sc.nextLong();
long res =quick_mi_er(a,b,p);
System.out.println(res%p);
}
}
}
快速幂求逆元
注意点:
1.求逆元的时候,需要进行判断
a模 p的乘法逆元存在
要求:a和p互质。且p为质数。
则由费马定理,p_1 = a^p-2 mod p;
package 数学知识;
import java.util.Scanner;
public class 快速幂求逆元 {
//1.费马定理。a^p-1 mod p==1 条件:a与p互质,且p为质数
//使用这个进行 求解
//给定 n 组 ai,pi,其中 pi 是质数,求 ai 模 pi 的乘法逆元,若逆元不存在则输出 impossible。
//注意:请返回在 0∼p−1 之间的逆元。
public static long quick_ni(long a,long p_2,long p){//
// 快速幂
long res = 1;
while (p_2!=0){
if((p_2&1)==1) res = res*a%p;
//右移一位
p_2 = p_2>>1;
//变值
a = a*a%p;
}
return res;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while (n-->0){
long a = sc.nextLong();
long p = sc.nextLong();
if(a%p != 0){
System.out.println(quick_ni(a,p-2,p));
}else {
System.out.println("impossible");
}
}
}
}
扩展欧几里得算法
1.从欧几里得开始的。
多了两个系数。
求系数运算
x,y
ax+by=d
求x,y
代码
注意:
1.没有地址引用,在static里面定义系数
2.更新操作公式时,进行的使用是-x,y不要搞混。
int d = exgcd(b,a % b, y,x);
y -= a / b * x; //更新y,x不用变
return d;
import java.util.Scanner;
public class Main {
//因为没有变量引用,所以将变量定义在satic里面,则会简单一点
static long x;
static long y;
public static long exd_gcd(long a,long b){
if(b==0) {
x = 1;
y = 0;
return a;//这个是因为gcd本来就是一个式子,然后这个是出口,同样的在使用递归时,也不能忘记这个递归的出口。
}
//重复性操作
long d = exd_gcd(b,a%b);//同样的这个也是gcd使用的本来的样子,通过这样的方式,使用
long tmp = x;
x = y;
y = tmp-a/b*y;
return d;//最终计算祝来。
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while (n-->0){
long a = sc.nextLong();
long b = sc.nextLong();
exd_gcd(a,b);
System.out.println(x+" "+y);
}
}
}
线性同余方程
解题思路:利用拓展欧几里得算法求解
将求解ai∗xi≡bi(modmi)ai∗xi≡bi(modmi)问题转换为拓展欧几里得问题,ai∗xi≡bi(modmi)⇒ai∗xi+m∗y≡bi(modmi)ai∗xi≡bi(modmi)⇒ai∗xi+m∗y≡bi(modmi)
一.解的分析
当bi为(ai,m)的倍数时,方程有解,
因为ai∗xi为(ai,m)的倍数,m∗y也为(ai,m)的倍数,
若方程有解时,bi也必然为(ai,m)的倍数,
若方程无解,那么bi必然不是(ai,m)的倍数
拓展欧几里得算法是求解ai∗xi+m∗y≡(a,m)问题的,
若有解时,和ai∗xi≡bi相差的只是一个倍数bi/(ai,m),最后x乘上bi/(ai,m)就是最后答案。
也就是相差的
ax = b
ax + y= gcd(a,m)
右边是变成bi