本文详细介绍Lucas定理和费马小定理的数学原理及其在算法竞赛中的应用,并提供Java代码实现和实际竞赛例题分析。
 
一、数学原理介绍 1.1 费马小定理(Fermat’s Little Theorem) 费马小定理是数论中的一个重要定理,由法国数学家皮埃尔·德·费马提出。
定理内容 :如果p是质数,a是整数且不是p的倍数,则有:
等价形式:对于任意整数a和质数p,有:
证明思路 :
考虑集合S = {1, 2, …, p-1} 
将S中每个元素乘以a(模p),得到集合T = {a·1 mod p, a·2 mod p, …, a·(p-1) mod p} 
可以证明T中的元素互不相同且都不为0 
因此T是S的一个排列 
两个集合的元素乘积相等:(a·1)·(a·2)·…·(a·(p-1)) ≡ 1·2·…·(p-1) (mod p) 
整理得:a^(p-1)·(p-1)! ≡ (p-1)! (mod p) 
由于(p-1)!与p互质,可以消去,得到a^(p-1) ≡ 1 (mod p) 
 
1.2 Lucas定理(Lucas’ Theorem) Lucas定理用于在模质数p的情况下计算组合数。
定理内容 :对于非负整数n和m,以及质数p,有:
1 C(n, m) ≡ C(n0, m0) × C(n1, m1) × ... × C(nk, mk) (mod p) 
其中,n = n0 + n1·p + n2·p² + … + nk·p^k,m = m0 + m1·p + m2·p² + … + mk·p^k,是n和m的p进制表示。
简化理解 :
将n和m表示为p进制 
分别计算每一位上的组合数 
将这些组合数相乘,结果即为C(n,m) mod p 
 
二、竞赛中的应用场景 2.1 费马小定理的应用 
模运算下的除法 :
在模p下,a/b可以表示为a·b^(-1) mod p 
利用费马小定理,b^(-1) ≡ b^(p-2) (mod p) 
因此a/b ≡ a·b^(p-2) (mod p) 
 
快速幂取模 :
计算a^b mod p时,可以利用费马小定理降低指数 
当b很大时,可以用b mod (p-1)替代b 
 
逆元计算 :
在模p下,a的乘法逆元是a^(p-2) 
用于解决除法取模问题 
 
 
2.2 Lucas定理的应用 
大组合数取模 :
当n和m非常大时,直接计算C(n,m)会溢出 
使用Lucas定理可以将大组合数分解为小组合数的乘积 
 
组合数取模质数幂 :
数位DP问题 :
 
三、Java代码实现 3.1 费马小定理实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public  class  FermatTheorem  {         public  static  long  fastPow (long  a, long  b, long  mod)  {         long  result  =  1 ;         a %= mod;         while  (b > 0 ) {             if  ((b & 1 ) == 1 ) {                 result = (result * a) % mod;             }             a = (a * a) % mod;             b >>= 1 ;         }         return  result;     }               public  static  long  modInverse (long  a, long  mod)  {         if  (gcd(a, mod) != 1 ) {             throw  new  ArithmeticException ("Modular inverse does not exist" );         }         return  fastPow(a, mod - 2 , mod);     }               private  static  long  gcd (long  a, long  b)  {         return  b == 0  ? a : gcd(b, a % b);     }               public  static  long  modDivide (long  a, long  b, long  mod)  {         return  (a * modInverse(b, mod)) % mod;     } } 
3.2 Lucas定理实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  class  LucasTheorem  {         public  static  long  combination (long  n, long  m, long  mod)  {         if  (m > n) return  0 ;         if  (m > n - m) m = n - m;                  long  numerator  =  1 ;         long  denominator  =  1 ;                  for  (int  i  =  0 ; i < m; i++) {             numerator = (numerator * (n - i)) % mod;             denominator = (denominator * (i + 1 )) % mod;         }                  return  FermatTheorem.modDivide(numerator, denominator, mod);     }               public  static  long  lucas (long  n, long  m, long  p)  {         if  (m == 0 ) return  1 ;         return  (combination(n % p, m % p, p) *                  lucas(n / p, m / p, p)) % p;     } } 
3.3 扩展Lucas定理(处理质数幂) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 public  class  ExtendedLucas  {         private  static  long [] exgcd(long  a, long  b) {         if  (b == 0 ) {             return  new  long []{1 , 0 , a};         }         long [] result = exgcd(b, a % b);         long  x  =  result[0 ];         long  y  =  result[1 ];         result[0 ] = y;         result[1 ] = x - (a / b) * y;         return  result;     }               private  static  long  modInverse (long  a, long  mod)  {         long [] result = exgcd(a, mod);         return  (result[0 ] % mod + mod) % mod;     }               private  static  long  factorialPrimePower (long  n, long  p)  {         long  count  =  0 ;         while  (n > 0 ) {             n /= p;             count += n;         }         return  count;     }               private  static  long  factorialModPrimePower (long  n, long  p, long  pk)  {         long  result  =  1 ;         for  (long  i  =  1 ; i <= n; i++) {             if  (i % p != 0 ) {                 result = (result * i) % pk;             }         }                           if  (n >= pk) {             result = (result * factorialModPrimePower(n / pk, p, pk)) % pk;         }                  return  result;     }               public  static  long  combinationModPrimePower (long  n, long  m, long  p, long  k)  {         long  pk  =  (long ) Math.pow(p, k);                           long  pn  =  factorialPrimePower(n, p);         long  pm  =  factorialPrimePower(m, p);         long  pnm  =  factorialPrimePower(n - m, p);                           if  (pn > pm + pnm) {             return  0 ;         }                           long  numerator  =  factorialModPrimePower(n, p, pk);         long  denominator  =  (factorialModPrimePower(m, p, pk) *                             factorialModPrimePower(n - m, p, pk)) % pk;                  return  (numerator * modInverse(denominator, pk)) % pk;     }               public  static  long  chineseRemainderTheorem (long [] remainders, long [] moduli)  {         long  product  =  1 ;         for  (long  mod : moduli) {             product *= mod;         }                  long  result  =  0 ;         for  (int  i  =  0 ; i < remainders.length; i++) {             long  m  =  product / moduli[i];             long  inverse  =  modInverse(m, moduli[i]);             result = (result + remainders[i] * m * inverse) % product;         }                  return  result;     }               public  static  long  extendedLucas (long  n, long  m, long  p, long  k)  {                  return  combinationModPrimePower(n, m, p, k);     } } 
四、竞赛例题分析 例题1:大组合数取模(基础应用) 题目描述 :
分析 :
直接计算C(n,m)会溢出 
使用Lucas定理可以解决 
 
Java实现 :
1 2 3 4 5 6 7 8 9 10 11 12 import  java.util.Scanner;public  class  LargeCombinatorialNumber  {    public  static  void  main (String[] args)  {         Scanner  scanner  =  new  Scanner (System.in);         long  n  =  scanner.nextLong();         long  m  =  scanner.nextLong();         long  p  =  scanner.nextLong();                  System.out.println(LucasTheorem.lucas(n, m, p));     } } 
例题2:模运算下的除法(费马小定理应用) 题目描述 :
分析 :
需要计算乘积的模逆元 
使用费马小定理计算每个数的逆元,然后相乘 
 
Java实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  java.util.Scanner;public  class  ModularInverseProduct  {    public  static  void  main (String[] args)  {         Scanner  scanner  =  new  Scanner (System.in);         int  n  =  scanner.nextInt();         long  p  =  scanner.nextLong();                  long  result  =  1 ;         for  (int  i  =  0 ; i < n; i++) {             long  a  =  scanner.nextLong();                          long  inverse  =  FermatTheorem.modInverse(a, p);             result = (result * inverse) % p;         }                  System.out.println(result);     } } 
例题3:组合数取模质数幂(扩展Lucas定理应用) 题目描述 :
分析 :
基本Lucas定理只适用于模质数的情况 
需要使用扩展Lucas定理处理模质数幂的情况 
 
Java实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 import  java.util.Scanner;public  class  CombinationModPrimePower  {    public  static  void  main (String[] args)  {         Scanner  scanner  =  new  Scanner (System.in);         long  n  =  scanner.nextLong();         long  m  =  scanner.nextLong();         long  p  =  scanner.nextLong();         long  k  =  scanner.nextLong();                  System.out.println(ExtendedLucas.extendedLucas(n, m, p, k));     } } 
例题4:快速幂与费马小定理结合(优化大指数) 题目描述 :
分析 :
直接使用快速幂计算a^b mod p,当b非常大时效率低下 
根据费马小定理,a^(p-1) ≡ 1 (mod p) 
因此a^b ≡ a^(b mod (p-1)) (mod p),当b ≥ p-1时 
特别地,当b < p-1时,直接计算a^b mod p 
 
Java实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import  java.util.Scanner;public  class  LargeExponentiation  {    public  static  void  main (String[] args)  {         Scanner  scanner  =  new  Scanner (System.in);         long  a  =  scanner.nextLong();         long  b  =  scanner.nextLong();         long  p  =  scanner.nextLong();                           if  (p == 1 ) {             System.out.println(0 );             return ;         }                           if  (a % p == 0 ) {             System.out.println(0 );             return ;         }                           if  (b >= p - 1 ) {             b = b % (p - 1 );                          if  (b == 0 ) {                 b = p - 1 ;             }         }                  System.out.println(FermatTheorem.fastPow(a, b, p));     } } 
例题5:组合数学问题(Lucas定理实际应用) 题目描述 :
分析 :
这是一个经典的组合数学问题,等价于将n个球放入m个盒子,每个盒子至少放一个球 
答案为C(n-1, m-1),即从n-1个位置中选择m-1个隔板的方法数 
当n和m很大时,需要使用Lucas定理计算 
 
Java实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  java.util.Scanner;public  class  BallsAndBoxes  {    public  static  void  main (String[] args)  {         Scanner  scanner  =  new  Scanner (System.in);         long  n  =  scanner.nextLong();         long  m  =  scanner.nextLong();         long  mod  =  1000000007 ;                            if  (m > n) {             System.out.println(0 );              return ;         }                                    System.out.println(LucasTheorem.combination(n - 1 , m - 1 , mod));     } } 
五、总结与拓展 5.1 两个定理的联系 费马小定理和Lucas定理在竞赛中经常结合使用:
费马小定理用于计算模质数情况下的乘法逆元 
Lucas定理利用这些逆元计算大组合数取模 
 
5.2 实际应用技巧 
预处理优化 :
对于多次查询的情况,可以预处理阶乘及其逆元 
时间复杂度从O(n)降至O(1)查询 
 
模数分解 :
当模数不是质数时,可以分解为质数幂的乘积 
使用中国剩余定理合并结果 
 
数值溢出处理 :
使用long类型或BigInteger处理大数 
中间计算过程及时取模 
 
 
5.3 相关拓展 
Wilson定理 :对于质数p,有(p-1)! ≡ -1 (mod p)
欧拉定理 :费马小定理的推广,若gcd(a,n)=1,则a^φ(n) ≡ 1 (mod n),其中φ(n)为欧拉函数
扩展Lucas定理 :处理模数为质数幂或合数的情况
 
在算法竞赛中,熟练掌握这些数论工具可以有效解决各种组合数学和模运算问题,是提高竞赛水平的重要一环。