跳转至

数学算法完全详解

重要性:⭐⭐⭐⭐ 难度:⭐⭐⭐ 学习时间:2-3天 前置知识:基础数学、位运算


📚 目录

  1. 数论基础
  2. 快速幂
  3. 组合数学
  4. 位运算技巧
  5. LeetCode题目详解

数论基础

质数相关

质数判断

Python
def is_prime(n):
    """
    判断n是否为质数
    时间:O(√n)
    """
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False

    i = 3
    while i * i <= n:
        if n % i == 0:
            return False
        i += 2

    return True

埃氏筛

Python
def sieve_of_eratosthenes(n):
    """
    埃氏筛:找出2~n的所有质数
    时间:O(n log log n)
    空间:O(n)
    """
    is_prime = [True] * (n + 1)
    is_prime[0] = is_prime[1] = False

    for i in range(2, int(n**0.5) + 1):
        if is_prime[i]:
            # 标记i的所有倍数为合数
            for j in range(i * i, n + 1, i):
                is_prime[j] = False

    return is_prime

# 测试
primes = sieve_of_eratosthenes(30)
print([i for i in range(2, 31) if primes[i]])
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

线性筛(欧拉筛)

Python
def linear_sieve(n):
    """
    线性筛:每个合数只被最小质因子筛去
    时间:O(n)
    空间:O(n)
    """
    is_prime = [True] * (n + 1)
    primes = []
    is_prime[0] = is_prime[1] = False

    for i in range(2, n + 1):
        if is_prime[i]:
            primes.append(i)

        for p in primes:
            if i * p > n:
                break
            is_prime[i * p] = False
            if i % p == 0:  # p是i的最小质因子
                break

    return primes

# C++版本
"""
#include <bits/stdc++.h>
using namespace std;

vector<int> linearSieve(int n) {
    vector<bool> isPrime(n + 1, true);
    vector<int> primes;
    isPrime[0] = isPrime[1] = false;

    for (int i = 2; i <= n; i++) {
        if (isPrime[i]) {
            primes.push_back(i);
        }

        for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) {
            isPrime[i * primes[j]] = false;
            if (i % primes[j] == 0) {
                break;
            }
        }
    }

    return primes;
}
"""

GCD和LCM

Python
def gcd(a, b):
    """
    最大公约数(辗转相除法)
    时间:O(log min(a, b))
    """
    return a if b == 0 else gcd(b, a % b)

def lcm(a, b):
    """
    最小公倍数
    """
    return a // gcd(a, b) * b

# C++版本
"""
int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

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

快速幂

原理

Text Only
计算 a^b mod m:

将b表示为二进制:b = b_k * 2^k + ... + b_1 * 2 + b_0

a^b = a^(b_k * 2^k) * ... * a^(b_1 * 2) * a^b_0

例如:a^13 = a^8 * a^4 * a^1(13 = 1101_2)

Python实现

Python
def fast_pow(base, exp, mod):
    """
    快速幂
    时间:O(log exp)
    空间:O(1)
    """
    result = 1
    base %= mod

    while exp > 0:
        if exp & 1:  # 如果exp是奇数
            result = result * base % mod
        base = base * base % mod
        exp >>= 1

    return result

# 测试
print(fast_pow(2, 10, 1000))  # 24
print(fast_pow(3, 100, 1000000007))  # 大数取模

C++实现(竞赛风格)

C++
#include <bits/stdc++.h>  // 引入头文件
using namespace std;

/**
 * 快速幂
 * 时间:O(log exp)
 * 空间:O(1)
 */
long long fastPow(long long base, long long exp, long long mod) {
    long long result = 1;
    base %= mod;

    while (exp > 0) {
        if (exp & 1) {
            result = result * base % mod;
        }
        base = base * base % mod;
        exp >>= 1;
    }

    return result;
}

int main() {
    cout << fastPow(2, 10, 1000) << endl;  // 24
    cout << fastPow(3, 100, 1000000007) << endl;
    return 0;
}

组合数学

组合数计算

Python
class Combination:
    """
    组合数计算(预处理)
    时间:预处理O(n),查询O(1)
    空间:O(n)
    """
    def __init__(self, n, mod=10**9 + 7):
        self.mod = mod
        self.fact = [1] * (n + 1)
        self.inv_fact = [1] * (n + 1)

        # 阶乘
        for i in range(1, n + 1):
            self.fact[i] = self.fact[i - 1] * i % mod

        # 逆元(费马小定理)
        self.inv_fact[n] = self._pow(self.fact[n], mod - 2)
        for i in range(n - 1, -1, -1):
            self.inv_fact[i] = self.inv_fact[i + 1] * (i + 1) % mod

    def _pow(self, a, b):
        """快速幂"""
        res = 1
        while b > 0:
            if b & 1:
                res = res * a % self.mod
            a = a * a % self.mod
            b >>= 1
        return res

    def C(self, n, k):
        """组合数C(n, k)"""
        if k < 0 or k > n:
            return 0
        return self.fact[n] * self.inv_fact[k] % self.mod * self.inv_fact[n - k] % self.mod

    def P(self, n, k):
        """排列数P(n, k)"""
        if k < 0 or k > n:
            return 0
        return self.fact[n] * self.inv_fact[n - k] % self.mod

# C++版本
"""
#include <bits/stdc++.h>
using namespace std;

class Combination {
private:
    vector<long long> fact;
    vector<long long> invFact;
    long long mod;

    long long powMod(long long a, long long b) {
        long long res = 1;
        a %= mod;
        while (b > 0) {
            if (b & 1) res = res * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return res;
    }

public:
    Combination(int n, long long m = 1e9 + 7) {
        mod = m;
        fact.resize(n + 1);
        invFact.resize(n + 1);

        fact[0] = 1;
        for (int i = 1; i <= n; i++) {
            fact[i] = fact[i - 1] * i % mod;
        }

        invFact[n] = powMod(fact[n], mod - 2);
        for (int i = n - 1; i >= 0; i--) {
            invFact[i] = invFact[i + 1] * (i + 1) % mod;
        }
    }

    long long C(int n, int k) {
        if (k < 0 || k > n) return 0;
        return fact[n] * invFact[k] % mod * invFact[n - k] % mod;
    }

    long long P(int n, int k) {
        if (k < 0 || k > n) return 0;
        return fact[n] * invFact[n - k] % mod;
    }
};
"""

卡特兰数

Python
def catalan(n):
    """
    卡特兰数
    C(n) = C(2n, n) / (n + 1)
    应用场景:括号匹配、二叉树形态、出栈顺序
    """
    comb = Combination(2 * n)
    return comb.C(2 * n, n) * comb._pow(n + 1, comb.mod - 2) % comb.mod

位运算技巧

常用技巧

Python
# 1. 判断奇偶
is_odd = x & 1  # 1为奇数,0为偶数

# 2. 交换两个数
a ^= b
b ^= a
a ^= b

# 3. 取最低位的1
lowbit = x & (-x)

# 4. 清除最低位的1
x &= x - 1

# 5. 判断是否为2的幂
is_power_of_2 = (x & (x - 1)) == 0

# 6. 计算1的个数(汉明重量)
def count_bits(x):
    count = 0
    while x:
        count += 1
        x &= x - 1
    return count

# 7. 获取第i位
bit_i = (x >> i) & 1

# 8. 设置第i位为1
x |= (1 << i)

# 9. 设置第i位为0
x &= ~(1 << i)

# 10. 切换第i位
x ^= (1 << i)

LeetCode题目详解

题目1:Pow(x, n)

题目链接LeetCode 50

Python
class Solution:
    def myPow(self, x: float, n: int) -> float:
        """
        快速幂实现
        时间:O(log n)
        空间:O(1)
        """
        if n < 0:
            x = 1 / x
            n = -n

        result = 1
        while n > 0:
            if n & 1:
                result *= x
            x *= x
            n >>= 1

        return result

题目2:计数质数

题目链接LeetCode 204

Python
class Solution:
    def countPrimes(self, n: int) -> int:
        """
        埃氏筛
        时间:O(n log log n)
        空间:O(n)
        """
        if n <= 2:
            return 0

        is_prime = [True] * n
        is_prime[0] = is_prime[1] = False

        for i in range(2, int(n**0.5) + 1):
            if is_prime[i]:
                for j in range(i * i, n, i):
                    is_prime[j] = False

        return sum(is_prime)

题目3:超级次方

题目链接LeetCode 372

Python
class Solution:
    def superPow(self, a: int, b: list[int]) -> int:
        """
        超级次方:计算 a^b mod 1337
        其中b是一个数组,表示一个大数

        利用:a^b = a^(b//10 * 10 + b%10) = (a^(b//10))^10 * a^(b%10)
        """
        MOD = 1337

        def pow_mod(base, exp):
            """快速幂"""
            result = 1
            base %= MOD
            while exp > 0:
                if exp & 1:
                    result = result * base % MOD
                base = base * base % MOD
                exp >>= 1
            return result

        result = 1
        for digit in b:
            # result = result^10 * a^digit
            result = pow_mod(result, 10) * pow_mod(a, digit) % MOD

        return result

📝 总结

关键要点

质数相关: - 埃氏筛:O(n log log n) - 线性筛:O(n)

快速幂: - 时间:O(log n) - 应用:大数幂运算、矩阵快速幂

组合数学: - 预处理阶乘和逆元 - 查询O(1)

位运算: - lowbit:x & (-x) - 清除最低位1:x & (x - 1)

下一步

继续学习: - 动态规划 - 更多DP技巧


📚 扩展阅读