【算法】数学

在这里插入图片描述

上期回顾: 【算法】字符串
个人主页:GUIQU.
归属专栏:算法

在这里插入图片描述

正文

1. 最大公约数(GCD - Greatest Common Divisor)

1.1 原理

最大公约数,指两个或多个整数共有约数中最大的一个。求最大公约数有多种方法,常见的如欧几里得算法(辗转相除法),其基本原理基于以下定理:两个整数 aba > b)的最大公约数等于 ba % b 的最大公约数,即 gcd(a, b) = gcd(b, a % b),通过不断重复这个过程,直到余数为 0 时,此时的除数就是最大公约数。

1.2 代码示例(C++)

#include <iostream>
using namespace std;

// 欧几里得算法求最大公约数
int gcd(int a, int b) {
    while (b!= 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

int main() {
    int num1 = 36;
    int num2 = 48;
    int result = gcd(num1, num2);
    cout << "最大公约数为: " << result << endl;
    return 0;
}

1.3 应用场景

  • 在分数化简中,需要用最大公约数来约分,使得分数化为最简形式,比如 12/18,通过求出 1218 的最大公约数 6,可将其化简为 2/3
  • 在一些周期性问题或者分组问题中,确定合适的分组数量等情况也会用到最大公约数的概念,例如将若干个物品按照等数量分组,要使分组数量尽可能大,就可以通过求物品总数之间的最大公约数来确定。

2. 最小公倍数(LCM - Least Common Multiple)

2.1 原理

两个整数 ab 的最小公倍数等于它们的乘积除以它们的最大公约数,即 lcm(a, b) = a * b / gcd(a, b)。这个原理是基于整数的因数分解以及最大公约数和最小公倍数之间的数学关系推导而来的。

2.2 代码示例(C++)

#include <iostream>
using namespace std;

// 先利用前面的 gcd 函数求最大公约数,再求最小公倍数
int lcm(int a, int b) {
    return a * b / gcd(a, b);
}

int main() {
    int num1 = 4;
    int num2 = 6;
    int result = lcm(num1, num2);
    cout << "最小公倍数为: " << result << endl;
    return 0;
}

2.3 应用场景

  • 在安排周期性任务的同步时间时会用到,比如一个任务每隔 3 天执行一次,另一个任务每隔 4 天执行一次,要找出它们同时执行的周期,就需要求 34 的最小公倍数,即每 12 天会同时执行一次。
  • 在计算不同分母的分数加减法时,需要先通过求最小公倍数将分数化为同分母分数再进行运算。

3. 快速幂算法

3.1 原理

快速幂算法用于高效地计算 ab 次幂(a^b),尤其是当 b 很大时。它的基本思想是利用指数 b 的二进制表示,将幂运算分解为一系列的乘法运算,通过不断平方和根据 b 的二进制位决定是否乘入当前结果,从而减少乘法运算的次数,降低时间复杂度,从常规的 O(b) 时间复杂度优化到 O(log b)

例如,计算 2^1313 的二进制表示是 1101,可以写成 2^13 = 2^(8 + 4 + 1) = 2^8 * 2^4 * 2^1,通过不断对 2 进行平方操作就能快速得到相应的幂次对应的数值,然后根据二进制位情况进行乘法组合。

3.2 代码示例(C++)

#include <iostream>
using namespace std;

// 快速幂算法
int fastPower(int a, int b) {
    int result = 1;
    while (b > 0) {
        if (b & 1) {  // 判断当前二进制位是否为 1
            result *= a;
        }
        a *= a;
        b >>= 1;  // 右移一位,相当于除以 2
    }
    return result;
}

int main() {
    int base = 2;
    int exponent = 10;
    int power = fastPower(base, exponent);
    cout << "结果为: " << power << endl;
    return 0;
}

3.3 应用场景

  • 在密码学中,很多加密算法涉及到大量的幂运算,例如 RSA 加密算法等,快速幂算法可以大大提高运算效率,加快加密和解密的速度。
  • 在计算一些数学公式或者数值模拟等场景中,如果有幂次很高的运算需求,快速幂算法也能有效节省计算时间。

4. 素数判断与筛法求素数

4.1 素数判断

  • 原理
    素数(质数)是指在大于 1 的自然数中,除了 1 和它自身外,不能被其他自然数整除的数。判断一个数 n 是否为素数,最朴素的方法就是从 2 到 n - 1 依次尝试能否整除 n,如果都不能整除,则 n 是素数。但更优化的方法是只需要从 2 到 sqrt(n) 去尝试整除即可,因为如果 n 有大于 sqrt(n) 的因数,那么必然也存在小于 sqrt(n) 的对应因数,这样可以减少判断的次数。

  • 代码示例(C++)

#include <iostream>
#include <cmath>
using namespace std;

// 判断一个数是否为素数
bool isPrime(int n) {
    if (n < 2) {
        return false;
    }
    if (n == 2) {
        return true;
    }
    if (n % 2 == 0) {
        return false;
    }
    for (int i = 3; i <= sqrt(n); i += 2) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

int main() {
    int num = 17;
    if (isPrime(num)) {
        cout << num << "是素数" << endl;
    } else {
        cout << num << "不是素数" << endl;
    }
    return 0;
}

4.2 筛法求素数(以埃拉托斯特尼筛法为例)

  • 原理
    埃拉托斯特尼筛法的基本思想是先把从 2 开始的、某一范围内的数依次排列好,然后从最小的素数 2 开始,把 2 的倍数都划去(标记为非素数),接着下一个未被划去的数(即素数 3),再把 3 的倍数都划去,以此类推,不断筛选,最后剩下未被划去的数就是素数。

  • 代码示例(C++,求 1 到 n 范围内的素数)

#include <iostream>
#include <vector>
using namespace std;

// 埃拉托斯特尼筛法求素数
vector<int> sieveOfEratosthenes(int n) {
    vector<bool> isPrime(n + 1, true);
    isPrime[0] = isPrime[1] = false;
    vector<int> primes;
    for (int i = 2; i <= n; i++) {
        if (isPrime[i]) {
            primes.push_back(i);
            for (int j = i * i; j <= n; j += i) {
                isPrime[j] = false;
            }
        }
    }
    return primes;
}

int main() {
    int n = 100;
    vector<int> primeNumbers = sieveOfEratosthenes(n);
    cout << "1 到 " << n << " 范围内的素数有: ";
    for (int prime : primeNumbers) {
        cout << prime << " ";
    }
    cout << endl;
    return 0;
}

4.3 应用场景

  • 在数论相关的研究和算法中,素数是基础且重要的概念,例如在一些加密算法的密钥生成等环节依赖于素数的特性来保证安全性。
  • 在数学竞赛或者一些程序设计竞赛中,经常会遇到有关素数的问题,如判断素数、基于素数的组合或者序列等题目,需要运用上述相关算法来解决。

5. 排列组合相关算法

5.1 全排列

  • 原理
    全排列是指从 n 个不同元素中取出 n 个元素的所有排列情况。常见的生成全排列的算法有递归法、字典序法等。以递归法为例,它的基本思想是固定第一个元素,然后对后面的元素进行全排列,通过不断交换元素位置并递归调用,生成所有可能的排列组合。

  • 代码示例(C++,以递归法生成全排列为例)

#include <iostream>
#include <vector>
using namespace std;

// 交换两个元素
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

// 生成全排列的递归函数
void permute(vector<int>& nums, int start, vector<vector<int>>& result) {
    if (start == nums.size() - 1) {
        result.push_back(nums);
        return;
    }
    for (int i = start; i < nums.size(); i++) {
        swap(nums[start], nums[i]);
        permute(nums, start + 1, result);
        swap(nums[start], nums[i]);
    }
}

// 生成全排列的主函数
vector<vector<int>> permute(vector<int> nums) {
    vector<vector<int>> result;
    permute(nums, 0, result);
    return result;
}

int main() {
    vector<int> nums = {1, 2, 3};
    vector<vector<int>> permutations = permute(nums);
    cout << "全排列结果为: " << endl;
    for (vector<int> permutation : permutations) {
        for (int num : permutation) {
            cout << num << " ";
        }
        cout << endl;
    }
    return 0;
}

5.2 组合

  • 原理
    组合是指从 n 个不同元素中取出 mm <= n)个元素组成一组,不考虑元素的顺序。可以通过递归或者利用数学公式结合循环等方式来计算组合数以及生成具体的组合情况。例如,利用杨辉三角(帕斯卡三角)的性质来计算组合数,其第 i 行第 j 列(从 0 开始计数)的数值就等于 C(i, j)(表示从 i 个元素中选 j 个元素的组合数),且满足 C(i, j) = C(i - 1, j) + C(i - 1, j - 1) 的递推关系。

  • 代码示例(C++,以利用杨辉三角计算组合数为例)

#include <iostream>
#include <vector>
using namespace std;

// 利用杨辉三角计算组合数
int combination(int n, int m) {
    vector<vector<int>> pascalTriangle(n + 1, vector<int>(n + 1, 0));
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= i; j++) {
            if (j == 0 || j == i) {
                pascalTriangle[i][j] = 1;
            } else {
                pascalTriangle[i][j] = pascalTriangle[i - 1][j] + pascalTriangle[i - 1][j - 1];
            }
        }
    }
    return pascalTriangle[n][m];
}

int main() {
    int n = 5;
    int m = 3;
    int result = combination(n, m);
    cout << "组合数为: " << result << endl;
    return 0;
}

5.3 应用场景

  • 在概率统计中,计算事件发生的可能性、样本空间等情况经常会涉及排列组合的知识,例如计算抽奖、抽样等问题中的概率。
  • 在算法设计中,例如在一些搜索算法、优化算法等场景下,需要对不同元素的排列组合情况进行分析和遍历,以找到最优解或者满足特定条件的解。

6. 矩阵运算相关算法(以矩阵乘法为例)

6.1 原理

矩阵乘法要求第一个矩阵的列数等于第二个矩阵的行数,设 Am×n 的矩阵,Bn×p 的矩阵,那么它们的乘积 C = A×B 是一个 m×p 的矩阵,其中 C 中元素 c_{ij} 的计算方法是 c_{ij} = ∑(a_{ik}×b_{kj})k 从 1 到 n),也就是 A 矩阵的第 i 行元素与 B 矩阵的第 j 列元素对应相乘后相加。

6.2 代码示例(C++)

#include <iostream>
#include <vector>
using namespace std;

// 矩阵乘法函数
vector<vector<int>> matrixMultiply(vector<vector<int>>& A, vector<vector<int>>& B) {
    int m = A.size();
    int n = A[0].size();
    int p = B[0].size();
    vector<vector<int>> C(m, vector<int>(p, 0));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < p; j++) {
            for (int k = 0; k < n; k++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
    return C;
}

int main() {
    vector<vector<int>> A = {
        {1, 2},
        {3, 4}
    };
    vector<vector<int>> B = {
        {5, 6},
        {7, 8}
    };
    vector<vector<int>> result = matrixMultiply(A, B);
    cout << "矩阵乘法结果为: " << endl;
    for (vector<int> row : result) {
        for (int num : row) {
            cout << num << " ";
        }
        cout << endl;
    }
    return 0;
}

6.3 应用场景

  • 在图形学中,矩阵乘法常用于对图形进行变换,如平移、旋转、缩放等操作,通过矩阵乘法来改变图形顶点的坐标位置,实现相应的视觉效果。
  • 在机器学习、深度学习等领域,大量的数据处理和模型计算涉及矩阵运算,例如神经网络中的权重矩阵与输入数据的乘法运算等,是很多算法实现的基础数学操作。

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guiat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值