找回密码
 欢迎注册
查看: 20434|回复: 21

[求助] 组合数的模运算

[复制链接]
发表于 2011-12-27 15:47:02 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?欢迎注册

×
求 C(n,m)  mod(M) 的算法。
其中n,m,M 均在int型范围内。

速度越快,内存占用越少越好,
看有没有两个都在O(n)尺度以内的算法。

问题背景:http://poj.grids.cn/practice/4036/
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-12-27 20:03:57 | 显示全部楼层
终于搞定了

平均的时间复杂度大致是 O(m),(m<n) ,
空间复杂度 O(m).
http://poj.grids.cn/practice/4036/statistics/
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-12-28 09:00:34 | 显示全部楼层
这么厉害,祝贺祝贺!
我想应该用到一些数学上的优化以及内嵌汇编吧?
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-12-28 10:42:40 | 显示全部楼层
3# gxqcn

没有什么数学方法,就是模拟了一下最基础的约分化简过程。
应该还能优化的
  1. #include <stdio.h>
  2. #define MOD 10007

  3. int gcd(int m,int n)
  4. {
  5.     int t ;
  6.     while(m!=0)t=m,m=n%m,n=t ;
  7.     return n ;
  8. }

  9. int binomialMod(int k,int n,int mod)
  10. {
  11.     int c,g,ii,jj,p[n],q[n];
  12.     /*C(k,n)= p1/q1*p2/q2*p3*q3....pn*qn */
  13.     for(ii=0;ii<n;ii++)p[ii]=k-ii ;
  14.     for(ii=n;ii>=1;ii--)
  15.     {
  16.         c=k%ii ;
  17.         g=gcd(p[c],ii);
  18.         p[c]/=g ;
  19.         q[ii-1]=ii/g ;
  20.         /*when ii<n/2, there are more reduction work */
  21.         if(q[ii-1]!=1)
  22.         {
  23.             jj=0 ;
  24.             while(q[ii-1]!=1&&c+jj<n)
  25.             {
  26.                 c=k%q[ii-1];
  27.                 g=gcd(p[c+jj],q[ii-1]);
  28.                 p[c+jj]/=g ;
  29.                 q[ii-1]/=g ;
  30.                 jj+=g ;
  31.             }
  32.         }
  33.     }
  34.    
  35.     c=1 ;
  36.     for(ii=0;ii<n;ii++)
  37.     {
  38.         c*=p[ii];
  39.         c%=mod;
  40.     }
  41.     return c ;
  42. }

  43. int powMod(int a,int pow,int mod)
  44. {
  45.     int ii=0,s=1 ;
  46.     while(++ii<=pow)s*=a,s%=mod ;
  47.     return s ;
  48. }

  49. int main()
  50. {
  51.     int a,b,k,n,m,ii,s ;
  52.     scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
  53.    
  54.     a%=MOD ;
  55.     b%=MOD ;
  56.     s=1 ;
  57.     s*=powMod(a,n,MOD);
  58.     s*=powMod(b,m,MOD);
  59.     s%=MOD ;
  60.     s*=binomialMod(k,n,MOD);
  61.     s%=MOD ;
  62.     printf("%d",s);
  63.    
  64.     return 0 ;
  65. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-12-29 08:25:58 | 显示全部楼层
上面 Line 49 中的“s*=a” 恐有溢出风险吧?
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-12-29 10:13:13 | 显示全部楼层
5# gxqcn
老大说的对。
呵呵,这题竟然被我蒙混过关了,
=========
另外,如果指数比较大的话,一个一个的乘还会很慢。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-12-29 10:14:50 | 显示全部楼层
其实现将M因子分解是不错的方法,花费时间也不大,不会超过O(sqrt(M))
然后计算M每个素因子在C(n,m)的次数,在然后计算因子乘积关于M(或其某个因子)的余数即可
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-12-29 10:18:15 | 显示全部楼层
6# wayne
上面的 binomialMod,powMod 函数仅对 MOD小于 sqrt(MAX_INT) 有效,而题目的MOD是 10007。
  1. int powMod(int a,int pow,int mod){
  2.     if (pow==0) return 1;       
  3.     a%=mod;
  4.     int s= powMod(a*a, pow/2, mod);
  5.     if (pow%2 !=0) s*=a,s%=mod;
  6.     return s;
  7. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-12-29 10:26:52 | 显示全部楼层
7# mathe
如果 M是素数,而且比较大,比如10007,该怎么考虑
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-12-29 10:30:18 | 显示全部楼层
感觉在INT范围内,计算两个INT的乘积的模,还需要单独考虑
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-5-9 08:58 , Processed in 0.070788 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表