第十六篇:高级算法与数据结构(下)

前言

        在实际问题中,概率与数论算法常常能带来亚线性甚至常数级的性能突破。本篇只保留核心思路和最常用的 Java 代码示例,帮助你快速上手。


一、随机化算法

1. Rolling Hash(字符串快速匹配)

  • 思路:把长度为 m 的字符串映射到一个整数(哈希值),并在滑动窗口时O(1) 更新:

    // 计算初始哈希
    long h=0, base=131, mod=1_000_000_007;
    for(int i=0;i<m;i++) h=(h*base + s.charAt(i))%mod;
    
    // 滚动更新:移出 s[i], 加入 s[i+m]
    long pow = modPow(base, m, mod); // base^m % mod
    h = ( (h*base - s.charAt(i)*pow%mod + mod) + s.charAt(i+m) )%mod;
    
  • 应用:快速子串查找、文档去重、海量日志指纹。


2. Bloom Filter(布隆过滤器)

  • 用位图+多哈希函数判断“是否可能存在”。

  • 插入和查询均为 O(k),空间远小于存储所有元素。        

BitSet bits=new BitSet(size);
int k=3; // 哈希函数个数
void add(String s){
  for(int i=0;i<k;i++){
    int pos=(hash(s,i)&0x7fffffff)%size;
    bits.set(pos);
  }
}
boolean contains(String s){
  for(int i=0;i<k;i++){
    int pos=(hash(s,i)&0x7fffffff)%size;
    if(!bits.get(pos)) return false;
  }
  return true; // 可能存在
}

二、素数测试与因式分解

1. Miller–Rabin 素性测试

  • 一种概率方法,O(k·log³n) 检测大数是否为素数。

  • 实用时对不大发现一定基就足够准确。

boolean isPrime(long n){
  if(n<2) return false;
  long d=n-1, s=0;
  while((d&1)==0){ d>>=1; s++; }
  long[] bases={2,325,9375,28178,450775,9780504,1795265022};
  for(long a:bases){
    if(a%n==0) return true;
    long x=modPow(a,d,n);
    if(x==1||x==n-1) continue;
    for(int r=1;r<s;r++){
      x=modMul(x,x,n);
      if(x==n-1) { x=-1; break; }
    }
    if(x!=n-1) return false;
  }
  return true;
}

2. Pollard’s Rho 因式分解

  • 随机化寻找非平凡因子,期望 O(n¹ᐟ⁴)

  • 与 Miller-Rabin 配合使用,先检测再分解。

long pollardRho(long n){
  if(n%2==0) return 2;
  Random rnd=new Random();
  long x=rnd.nextInt((int)n-2)+2, y=x, c=rnd.nextInt((int)n-1)+1, d=1;
  while(d==1){
    x=(modMul(x,x,n)+c)%n;
    y=(modMul(y,y,n)+c)%n;
    y=(modMul(y,y,n)+c)%n;
    d=gcd(Math.abs(x-y), n);
    if(d==n) return pollardRho(n);
  }
  return d;
}

三、快速幂与矩阵快速幂

1. 快速幂

  • 计算 $a^n\bmod m$,O(log n)

long modPow(long a,long e,long m){
  long res=1;
  while(e>0){
    if((e&1)==1) res=res*a%m;
    a=a*a%m;
    e>>=1;
  }
  return res;
}

2. 矩阵快速幂

  • 线性递推(如斐波那契)可转换矩阵幂,仍为O(log n)

long fib(long n){
  if(n<2) return n;
  long[][] M={{1,1},{1,0}}, R={{1,0},{0,1}};
  n--;
  while(n>0){
    if((n&1)==1) R=mul(R,M);
    M=mul(M,M);
    n>>=1;
  }
  return R[0][0];
}

四、中国剩余定理(CRT)与扩展欧几里得

1. 扩展欧几里得求逆元

long exgcd(long a,long b,long[] xy){
  if(b==0){ xy[0]=1; xy[1]=0; return a;}
  long d=exgcd(b,a%b,xy);
  long x=xy[1], y=xy[0] - (a/b)*xy[1];
  xy[0]=x; xy[1]=y;
  return d;
}
long modInv(long a,long m){
  long[] xy=new long[2];
  exgcd(a,m,xy);
  long inv=xy[0]%m;
  return inv<0?inv+m:inv;
}

2. CRT

long crt(int[] a,int[] n){
  int k=a.length;
  long N=1; for(int v:n) N*=v;
  long x=0;
  for(int i=0;i<k;i++){
    long Ni=N/n[i], inv=modInv(Ni,n[i]);
    x=(x + a[i]*Ni%N*inv)%N;
  }
  return (x+N)%N;
}

五、数论变换(NTT)简介

  • NTT是FFT在模运算下的对应,解决模意义下的大规模多项式乘法,无浮点误差。

  • 只需选合适 p=k·2^t+1 和本原根 $g$ 即可,复杂度 O(n\log n)。

// 省略预处理细节,核心蝶形操作示例
for(int len=1;len<n;len<<=1){
  long w=modPow(g,(p-1)/(len<<1),p);
  for(int i=0;i<n;i+=len<<1){
    long wn=1;
    for(int j=0;j<len;j++){
      long u=a[i+j], v=a[i+j+len]*wn%p;
      a[i+j]=(u+v)%p; a[i+j+len]=(u-v+p)%p;
      wn=wn*w%p;
    }
  }
}

本篇小结

  1. 滚动哈希布隆过滤器实现高效字符串与集合查询。

  2. Miller–Rabin + Pollard’s Rho 组合实现大数素性检测与因式分解。

  3. 快速幂矩阵幂将线性递推降至对数时间。

  4. CRT扩展欧几里得实现多模同余与模逆。

  5. NTT是离散卷积在模域的快速实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Stay Passion

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

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

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

打赏作者

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

抵扣说明:

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

余额充值