常见算法-数学技巧
数学算法:编程中的数学魔法
当算法遇上数学,简洁而优雅
引言
在算法世界里,数学不仅仅是公式和定理,更是解决问题的强大工具。很多看似复杂的问题,只要找到背后的数学规律,往往能用寥寥几行代码优雅地解决。
本文涵盖:
- 质数与因数分解
- 最大公约数与最小公倍数
- 快速幂与模运算
- 数学规律与找规律问题
- 组合数学基础
一、质数相关
1.1 判断质数
质数:大于1的自然数中,只能被1和自己整除的数
方法1:试除法(基础)
1 | bool isPrime(int n) { |
优化点:
- 只需检查到√n
- 跳过偶数,只检查奇数
时间复杂度:O(√n)
为什么只需要检查到√n?
1 | 假设 n = a × b |
1.2 埃拉托斯特尼筛法(Sieve of Eratosthenes)
LeetCode 204:统计所有小于非负整数 n 的质数的数量
问题分析
1 | 输入:n = 10 |
算法思路:筛法
1 | 从2开始,标记2的所有倍数为合数 |
可视化过程
1 | n = 20 |
代码实现
1 | int countPrimes(int n) { |
时间复杂度:O(n log log n)
空间复杂度:O(n)
优化技巧
1 | // 优化1:从i*i开始标记 |
1.3 分解质因数
将一个正整数分解成质数的乘积
问题示例
1 | 输入:12 |
代码实现
1 | vector<int> primeFactorization(int n) { |
示例执行:
1 | n = 60 |
二、最大公约数(GCD)与最小公倍数(LCM)
2.1 最大公约数(GCD)
两个或多个整数共有约数中最大的一个
欧几里得算法(辗转相除法)
数学原理:
1 | gcd(a, b) = gcd(b, a % b) |
可视化:
1 | gcd(48, 18) |
代码实现
1 | // 递归版本 |
时间复杂度:O(log min(a, b))
2.2 最小公倍数(LCM)
两个或多个整数公有的倍数中最小的一个
数学关系
1 | lcm(a, b) = (a × b) / gcd(a, b) |
代码实现
1 | long long lcm(int a, int b) { |
注意:要防止整数溢出!
2.3 应用:简化分数
1 | struct Fraction { |
示例:
1 | 12/18 简化: |
三、快速幂与模运算
3.1 快速幂(LeetCode 50)
计算 x^n,要求时间复杂度 O(log n)
暴力方法
1 | // O(n) |
问题:n很大时太慢
快速幂(分治)
核心思想:
1 | x^8 = (x^4)^2 = ((x^2)^2)^2 |
示例:
1 | 计算 2^10: |
代码实现(递归版)
1 | double myPow(double x, long long n) { |
代码实现(迭代版,推荐)
1 | double myPow(double x, long long n) { |
时间复杂度:O(log n)
3.2 模运算下的快速幂
LeetCode 1969:超级次方
模运算性质
1 | (a × b) % p = ((a % p) × (b % p)) % p |
代码实现
1 | const int MOD = 1e9 + 7; |
应用:计算大数的幂次,防止溢出
四、数学规律问题
4.1 阶乘末尾零的个数(LeetCode 172)
给定一个整数 n,返回 n! 结果尾数中零的数量
分析
末尾的0来自于 2 × 5,因子2的数量总是比5多,所以只需要数5的个数。
示例:
1 | 5! = 120 → 1个0(一个5) |
数5的个数
1 | n / 5:有多少个5的倍数 |
代码实现
1 | int trailingZeroes(int n) { |
示例:
1 | n = 25 |
时间复杂度:O(log n)
4.2 数字1的个数(LeetCode 233)
给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的次数
问题示例
1 | 输入:n = 13 |
数位DP思路
按位统计:个位、十位、百位…各有多少个1
1 | 对于 n = 234,统计十位上1的个数: |
代码实现
1 | int countDigitOne(int n) { |
4.3 丑数(LeetCode 264)
丑数是只包含质因数 2, 3, 5 的正整数
问题
1 | 输入:n = 10 |
动态规划(三指针)
思路:
1 | 每个丑数都是之前的丑数 × 2、× 3 或 × 5 得到的 |
代码实现
1 | int nthUglyNumber(int n) { |
过程演示:
1 | 初始:dp[0] = 1 |
五、组合数学
5.1 帕斯卡三角形(LeetCode 118)
杨辉三角/帕斯卡三角形
可视化
1 | 1 |
规律:
1 | 每个数 = 左上方的数 + 右上方的数 |
代码实现
1 | vector<vector<int>> generate(int numRows) { |
5.2 组合数计算
计算 C(n, k) = n! / (k! × (n-k)!)
方法1:递推公式
1 | int combination(int n, int k) { |
方法2:直接计算(防溢出)
1 | long long combination(int n, int k) { |
六、其他数学技巧
6.1 判断完全平方数
1 | bool isPerfectSquare(int num) { |
6.2 整数反转(LeetCode 7)
1 | int reverse(int x) { |
6.3 回文数(LeetCode 9)
1 | bool isPalindrome(int x) { |
总结
核心要点
- 质数判断:试除法 O(√n),埃氏筛法 O(n log log n)
- GCD:欧几里得算法 O(log min(a,b))
- 快速幂:分治思想,O(log n)
- 找规律:观察数学性质,降低复杂度
常用模板
1 | // 判断质数 |
推荐题目
入门级:
进阶级:
高级:
推荐阅读:
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Smarter's blog!
评论

