【算法】初等数论

news/2025/2/23 17:10:32

初等数论

取余,遵循尽可能让商向0靠近的原则,结果的正负和左操作数相同

取模,遵循尽可能让商向负无穷靠近的原则,结果的正负和右操作数相同

7/(-3)=-2.3,产生了两个商-2和-3,取余语言中取-2,导致余数为1;取模语言中取-3,导致余数为-2

java%是取余

1、暴力幂

思想:直接将a连续乘以b

时间复杂度:O(n)

空间复杂度:O(1)

java">    // 求 a^b
    public long pow(int a, int b){
        long ans = 1;
        for (int i = 0; i < b; i++) {
            ans *= a;
        }
        return ans;
    }

2、快速幂

思想:利用幂的2进制形式来加速运算。

时间复杂度:O(log₂N)

空间复杂度:O(1)

java">    // 求 a^b
    public long pow(int a, int b){
        long ans = 1;
        // 不断取幂的二进制形式中的最后一位并且将b不断右移(将b最后一位抛弃),直到幂最后变为0
        while(b > 0){
            // 当前幂的最后一位为1,表明需要将该结果添加到最终的结果中(由于是幂中的+,实际上操作为乘法)
            if((b & 1) == 1){
                ans *= a;
            }
            // 将底数变为原底数的二次方
            a *= a;
            // 抛弃幂二进制形式的最后一位
            b >>= 1;
        }
        return ans;
    }

例子:

3 5 = 3 101 = 3 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 0 = 3 1 ∗ 2 3 ∗ 3 0 ∗ 2 2 ∗ 3 1 ∗ 2 0 3^{5}=3^{101}=3^{1*2^{3}+0*2^{2}+1*2^{0}}=3^{1*2^{3}}*3^{0*2^{2}}*3^{1*2^{0}} 35=3101=3123+022+120=312330223120

3、Math类

java">// 求 a^b
// java.lang.Math
// double pow(double a, double b)
Math.pow(a, b)

可以支持浮点数的幂

补充

结果%c

原理:多个数的积%c,等于下列操作和的结果

  • 每个乘项%c
  • 最终积%c
java">    // 求 a^b%c
    public long pow(int a, int b, int c){
        long ans = 1;
        // 不断取幂的二进制形式中的最后一位并且将b不断右移(将b最后一位抛弃),直到幂最后变为0
        while(b > 0){
            // 当前幂的最后一位为1,表明需要将该结果添加到最终的结果中(由于是幂中的+,实际上操作为乘法)
            if((b & 1) == 1){
                ans = (ans*a)%c;
            }
            // 将底数变为原底数的二次方
            a = (a*a)%c;
            // 抛弃幂二进制形式的最后一位
            b >>= 1;
        }
        return ans%c;
    }

对数

1、Math类

java">//java.lang.Math
double log(double a) //以e为底
double log10(double a) //以10为底

Math.log(n);
Math.log10(n);

可以求浮点数的对数

2、朴素

java">    public static int logN(int base, int n) {
        int power = 0;
        while (n / base > 0) {
            n = n / base;
            power++;
        }
        return power;
    }

	//log2
    public int log2(int n) {
        int power = 0;
        while ((n = n >> 1) > 0) {
            power++;
        }
        return power;
    }

矩阵

1、单位矩阵

单位矩阵的对角线上的元素全为1,其他的元素全为0

java">	public int[][] unit(int n){
        int[][] res=new int[n][n];
        for(int i=0;i<n;i++){
            res[i][i]=1;
        }
        return res;
    }

2、乘法

java">	public int[][] multiplyMatrix(int x1[][],int x2[][]){
        //第一个矩阵的列必须等于第二个矩阵的行
        if(x1[0].length!=x2.length){
            return;
        }
        int lineLength=x1.length;   //第一个矩阵的行
        int listLength=x2[0].length;//第二个矩阵的列
        int[][] multiply=new int[lineLength][listLength];//相乘的结果矩阵
        for(int i=0;i<lineLength;i++){
            for(int j=0;j<listLength;j++){
                for(int k=0;k<x1[0].length;k++){
                    multiply[i][j]+=x1[i][k]*x2[k][j];
                }
            }
        }
        return multiply;
    }

矩阵%c等于矩阵上的每一个元素都%c

3、快速幂

类似于整数的快速幂,不同的是1的表示(矩阵中为单位矩阵),以及乘法的定义

java">    // 求 a^b
    public int[][] pow(int[][] A, int b){
        int[][] ans = unit(A.length);
        // 不断取幂的二进制形式中的最后一位并且将b不断右移(将b最后一位抛弃),直到幂最后变为0
        while(b > 0){
            // 当前幂的最后一位为1,表明需要将该结果添加到最终的结果中(由于是幂中的+,实际上操作为乘法)
            if((b & 1) == 1){
                ans = multiplyMatrix(ans,a);
            }
            // 将底数变为原底数的二次方
            a = multiplyMatrix(a,a);
            // 抛弃幂二进制形式的最后一位
            b >>= 1;
        }
        return ans;
    }

素数(质数)

质数:是指在大于1的整数中,除了1和它本身以外不再有其他因数的自然数。

合数:是指在大于1的整数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。

1既不属于质数也不属于合数。最小的合数是4,最小的质数是2

1、朴素

java">boolean isPrime(int n){
	for(int i=2;i*i<=n;i++){
		if(n%i==0){
			return false;
		}
	}
	return true;
}

2、埃氏筛法

java">	//埃氏筛选法
    public void eratosthenes(int n) {
        boolean[] isPrime = new boolean[n+1];//false代表是素数,true代表的是合数
        for (int i = 0; i <= n; i++) {
            if(i<2){
                isPrime[i]=true;
                continue;
            }
            //如果是素数
            if (!isPrime[i]) {
                //将该素数的所有倍数都标记为合数
                for (int j = 2 * i; j < n; j += i) {
                    isPrime[j] = true;
                }
            }
        }
    }

埃拉托斯特尼筛法(简称埃氏筛或爱氏筛):要得到自然数n以内的全部素数,必须把不大于 根号n 的所有素数的倍数剔除,剩下的就是素数。

时间复杂度:O(nloglogn)

不足之处:6 在 indexI = 2 时被标记,而在 indexI = 3 时,又被标记了一次。存在重复标记,有优化空间

3、欧拉筛

欧拉筛是对埃氏筛的改进,避免重筛,提高效率

java">	//欧拉筛选法
    public void euler(int n) {
        //建立一个bool类型的数组,以下标为要判断的数字 以该下标的值为素数的标志
		//若i是素数 则 isPrime[i]=false
        boolean[] isPrime = new boolean[n+1];
        isPrime[0] = isPrime[1] = true;//数字0和1都不是素数,所以赋true
 
        int[] Prime = new int[n+1];//存放素数的数组
        int t = 0;
        Prime[t++] = 2;//把2放进素数表
        for (int i = 2; i <= n; i++) {
            if (!isPrime[i])//若当前数是素数
                Prime[t++] = i;//则存入素数数组
            // 每一个数都去乘以当前素数表里面已有的数,如果 indexI 是合数,且 indexI % primeList[indexJ] == 0,那么它只能乘以第一个素数 2
            for (int j = 0; j < t && Prime[j] * i <= n; j++) {
                isPrime[i * Prime[j]] = true;
                // 保证了每个合数只会被它的最小素因子筛掉,避免重筛,使得程序更有效率
                if (i % Prime[j] == 0)
                    break;
            }
        }
    }

欧拉筛法:保证每个合数只会被它的最小质因数筛掉,时间复杂度降低到O(n)。

每一个数都去乘以当前素数表里面 小于等于最小素因子的数

最大公因数(gcd)

最大公约数(Greatest Common Divisor)

1、辗转相除法(欧几里得)

思想:两个正整数a和b(a > b),它们的最大公约数gcd等于a除以b的余数r和b之间的最大公约数。辗转相除法的算法流程可以如下:

  1. 计算a与b的余数r。
  2. 如果r为0,则返回gcd = b。否则,转到步骤3。
  3. 使用b的值更新a的值,使用余数r更新b的值,转到步骤1。
int gcd(int x, int y) {
	return x == 0 ? y : gcd(y % x, x);
}

int gcd(int a, int b){
    if (b == 0)
        return a;
    else
        return gcd(b, a%b);
}

int gcd(int a, int b){
    int temp;
    while(b!=0){
        temp=a%b;
        a=b;
        b=temp;
    }
    return a;
}

根本无需判断a和b的大小,当a值小于b值时,算法的下一次递归调用就能够将a和b的值交换过来

2、更相减损术

思想:两个正整数a和b(a > b),它们的最大公约数等于a-b的差值c和较小数b的最大公约数。依次递归下去,直到两个数相等。这相等两个数的值就是所求最大公约数。

java">int gcd(int x, int y) {
	if (x==y)return x;
	else if (x>y)return gcd(x-y,y);
	else return gcd(y-x,x);
}

更相减损法和辗转相除法的思想较为接近,不同的是辗转相除法迭代更快,而更相减损法迭代慢。但后者使用的是减法,前者使用的是求余,前者损耗较低。在两数相差较大时避免使用更相减损法,而在大数是避免使用辗转相除法。

最小公倍数(lcm)

1、加穷举法

将大数依次乘N(N为从1开始的自然数),对得到的数判断其是否整除小数。

2、乘穷举法

将大数依次加1,对得到的数判断其是否可整除两数。

3、最大公因数法(最优)

l c m ( a , b ) = ∣ a ⋅ b ∣ g c d ( a , b ) lcm(a,b)=\frac{∣a⋅b∣}{gcd(a,b)} lcm(a,b)=gcd(a,b)ab

java">int lcm(int a, int b) {
    return a * b / gcd(a, b);
}

http://www.niftyadmin.cn/n/5863611.html

相关文章

数据库之MySQL——事务(一)

1、MySQL之事务的四大特性(ACID)&#xff1f; 原子性(atomicity)&#xff1a;一个事务必须视为一个不可分割的最小工作单元&#xff0c;整个事务中的所有操作要么全部提交成功&#xff0c;要么全部失败回滚&#xff0c;对于一个事务来说&#xff0c;不可能只执行其中的一部分操…

论文略读:Uncovering Hidden Representations in Language Models

202502 arxiv 说一下主要结论吧 对于下游任务&#xff0c;语言模型的中间层在所有架构和任务中始终优于最后一层 这挑战了使用最后一层表示的传统观点。 不同的架构表现出不同的信息压缩模式。自回归模型在中间层存在瓶颈&#xff0c;而双向模型则保持更均匀的趋势 BERT通过双…

CSS垂直居中终极方案:告别复杂计算,拥抱现代布局

CSS垂直居中终极方案&#xff1a;告别复杂计算&#xff0c;拥抱现代布局 &#x1f4cc; 前言&#xff1a;为什么垂直居中如此重要&#xff1f;一、2024年最推荐的3种方案1. Flexbox布局&#xff08;首推方案&#xff09;2. Grid布局&#xff08;未来趋势&#xff09;3. Transfo…

【核心算法篇二十】《DeepSeek符号回归:让AI化身「数学神探」破解数据背后的宇宙公式》

“宇宙最不可理解之处,就是它居然可以被理解。”——爱因斯坦 如果让AI来续写这句话,或许会是:"数据最迷人的地方,在于它总能用数学公式讲出故事。"今天我们要聊的DeepSeek符号回归技术,就是教AI从杂乱数据中自动发现精妙数学规律的「黑魔法」。全程高能预警,建…

OpenCV(6):图像边缘检测

图像边缘检测是计算机视觉和图像处理中的一项基本任务&#xff0c;它用于识别图像中亮度变化明显的区域&#xff0c;这些区域通常对应于物体的边界。是 OpenCV 中常用的边缘检测函数及其说明: 函数算法说明适用场景cv2.Canny()Canny 边缘检测多阶段算法&#xff0c;检测效果较…

机器学习实战(7):聚类算法——发现数据中的隐藏模式

第7集&#xff1a;聚类算法——发现数据中的隐藏模式 在机器学习中&#xff0c;聚类&#xff08;Clustering&#xff09; 是一种无监督学习方法&#xff0c;用于发现数据中的隐藏模式或分组。与分类任务不同&#xff0c;聚类不需要标签&#xff0c;而是根据数据的相似性将其划…

MATLAB学习之旅:数据建模与仿真应用

在MATLAB的学习之旅中&#xff0c;我们已经积累了丰富的基础知识和实用的编程技巧。从前面的学习中&#xff0c;我们对MATLAB的基础操作、数据处理、统计分析等方面都有了深入的了解。如今&#xff0c;我们将迈向一个充满创造力和实用性的阶段——数据建模与仿真应用。这部分内…

Oracle 10g数据库资源下载分享

简介&#xff1a;Oracle 10g是Oracle公司推出的一款数据库管理系统版本&#xff0c;本教程将向读者详细说明如何在不同的操作系统平台上安装、配置Oracle 10g&#xff0c;包括系统需求、安装步骤、数据库启动关闭、用户和表空间管理、网络配置及性能调优。同时&#xff0c;还介…