快速幂(包含取模情况下)
解决的问题:
快速得到一个a的b次方,复杂度为log2n
心得:
前提:例如a的6次方,6的二进制’110‘,右边第一位是1次方,第二位是2次方,第三位是4次方,第一位和第二位都是1,所以a的6次方 <=> a的2次方 * a的4次方
初始化res为1,通过传入的b次方,把b进行二进制操作,查看最后一位是否为1,如果是1则代表需要乘a(a一直在变,a的1、2、3、4次方等等),否则不需要。但是无论需不需要乘,每次b都会右移1位,a也需要进行平方,因为b的二进制右边第一位对应a的1次方,第二位是a的2次方,b移动了,a也要对应相乘
公用代码:
public static void main(String[] args) {
System.out.println(ksm(5, 2, 9));
}
public static int ksm(int a,int b,int mod) {
int res=1;
while(b>0) {
if((b & 1)==1) {
res=res*a%mod;
}
a=a*a%mod;
b>>=1;
}
return res;
}
例题(待更新):
矩阵快速幂
解决的问题:
与快速幂相似,也是快速得到一个矩阵的b次方
心得:
与快速幂相似,初始化ans为单位矩阵(类似于快速幂的初始化res为1)判断条件什么的,但是在乘法这一块需要用到矩阵的乘法(twoMatrixPlus方法),当b次方最后一位是1,则ans为ans矩阵与a相乘(类似于快速幂中res为res与a相乘)。但是无论是不是,b一样要右移,a一样还要平方,这个时候平方就是a矩阵与a矩阵相乘
公用代码:
package conbat.动态规划.数论;
import java.util.Arrays;
public class 快速幂与矩阵快速幂 {
static int[][] res;
static int[][] temp;
public static void main(String[] args) {
int[][] jzksm = jzksm(new int[][] { { 2, 3 }, { 2, 4 } }, 2);
for(int[] sss:jzksm) {
System.out.println(Arrays.toString(sss));
}
}
public static int[][] jzksm(int a[][],int n){
int ans[][]=new int[a.length][a[0].length];
for(int i=0;i<ans.length;i++)
for(int j=0;j<ans[0].length;j++) {
if(i==j)
ans[i][j]=1;
}//初始化为单位矩阵
while(n>0) {
if((n&1)==1)
ans=twoMatrixPlus(ans,a);
a=twoMatrixPlus(a,a);
n>>=1;
}
return ans;
}
public static int[][] twoMatrixPlus(int x [][],int y [][]) {//两个矩阵相乘,具有普适性
int ans[][]=new int[x.length][y[0].length];
for(int i=0;i<x.length;i++)
for(int j=0;j<y[0].length;j++)
for(int p=0;p<x[0].length;p++)
ans[i][j]+=x[i][p]*y[p][j];
return ans;
}
}
例题(待更新):
素数三大筛
解决的问题:
判断n是合数还是质数(素数)
心得:
有三种不同解决方法,简单方法,埃式筛,欧拉筛。
简单方法:int i=2;i*i<=x;i++,如果n/i为0则是合数,一直判断,最后没有就是质数(只看一个数的时候合适,埃式筛欧拉筛是多个数都需要的比较合适)
埃式筛:int i=2;i*i<n;i++,由2开始,一直倍数,倍数后的数就设为true,是素数,最后没有被设为true的就是合数,但是由于有一些数会被重复倍数到,例如8会被2和4进行倍数
欧拉筛:我直接就推荐视频讲解,太牛了家人们这个动画,这个算法主要就是对于埃式筛会重复设置进行了优化(if(i % prime[j] == 0) break;)欧拉筛,几行就行,一次就好_哔哩哔哩_bilibili
公用代码:
import java.beans.Visibility;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(is_easy(17));
System.out.println(is_comment(17));
System.out.println(is_good(8));
System.out.println(is_good(7));
System.out.println(is_good(9));
System.out.println(is_good(17));
System.out.println(is_good(19));
}
public static boolean is_easy(int x) { //复杂度:根号x 合数返回false 质数/素数返回true;
if(x==0||x==1) {
return false;
}
for(int i=2;i*i<=x;i++) {
if(x%i==0) {
return false;
}
}
return true;
}
public static boolean is_comment(int x) {//埃式筛 复杂度:nloglogn 得到2-N以内哪个是质数哪个是素数 维护的数组是vis[] vis[x]=true是合数
int n=10000005;
boolean[] vis=new boolean[n]; //默认false => 质数/素数
for(int i=2;i*i<n;i++) {
if(!vis[i]) {
for(int j=i*2;j<n;j+=i) {
vis[j]=true;
}
}
}
return vis[x];
}
public static int is_good(int n) {//欧拉筛 复杂度:n
int cnt = 0;
int []isPrime = new int[n+1]; // 1 为合数 0为质数
int []prime = new int[n+1];
for(int i = 2;i <=n/2;i++){
if(isPrime[i] == 0){
prime[++cnt] = i;
}
for(int j = 1;j <= cnt && prime[j] * i <= n;j++){
isPrime[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
return isPrime[n];
}
}
例题(待更新):
扩展欧几里得
解决的问题:
已知整数a,b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y,满足ax + by = gcd(a,b)
心得(待完善):
公用代码:
package conbat.动态规划.数论;
import java.util.Scanner;
public class 拓展欧几里得 {
//扩展欧几里得算法求x、y
static int x=-1;
static int y=-1;
public static void main(String[] args) {
// 已知整数a,b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y,满足ax + by = gcd(a,b)
Scanner cin = new Scanner(System.in);
while(cin.hasNext()) {
int a = cin.nextInt();
int b = cin.nextInt();
int k1 = eeaGcd(a,b);
System.out.println("最大公因子是:"+k1+",其中x="+x+",y="+y);
}
cin.close();
}
//扩展欧几里得算法
private static int eeaGcd(int a,int b) {
//最简单的情形
if(b==0) {
x=1;
y=0;
return a;//最大公约数
}else {
//一般情形
int r = eeaGcd(b,a%b);
int t = x;
x = y;
y = t-a/b*y;
return r;
}
}
}
例题(待更新):
唯一分解定理
解决的问题:
x数可以分解为哪些数相乘
心得:
x/j(2到j*j<=x)可以除几次就输出几次j,不行了就j++,最后数如果不是1则输出最后的数(例如15分为3和5,除完剩5,5不能除3了,因为j最大到3,所以最后还要输出5,例如9可以分为3,除完3还剩3,3再除完3剩1,除不了3了,也正好是1,就不需要输出了)
公用代码:
package conbat.动态规划.数论;
public class 唯一分解定理 {
public static void main(String[] args) {
idea(4);
}
public static void idea(int x) {
int i=1;
while(x>=i*i) {
i++;
while(x%i==0) {
System.out.print(i+" ");
x/=i;
}
}
if(x!=1) {
System.out.println(x);
}
}
}
例题:
最大公约数与最小公倍数
解决的问题:
得到一个数的最大公约数还有最小公倍数方法
心得:
gcd(最大公倍数)a一直%b得到t后,再把a设为原来的b,b设为t,直至b=0,lcm(最小公倍数)a*b/gcd(a,b)
例题(待更新):
公用代码:
package conbat.动态规划.数论;
public class 最大公约数 {
public static void main(String[] args) {
int gcd=gcd(15,3);
int lcm=15*3/gcd;
System.out.println(gcd);//最大公约数
System.out.println(lcm);//最小公倍数
}
public static int gcd(int a,int b) {
while(b>0) {
int t=a%b;
a=b;
b=t;
}
return a;
}
}
同余运算(包含乘法逆元)
解决的问题:
题目中数过大,要求取模的情况下
公用方法:
a/b%m =》(b%p) * (a的逆元%p) %p
(如果b与m互质,则a的逆元为a的p-2次方 b/a%p =》 b*a的逆元%p) --来自费马小定理
例题(待更新):
龟速乘法(待更新):
米勒罗宾(待更新):