找回密码
 欢迎注册
查看: 40999|回复: 13

[求助] 求因子个数的问题

[复制链接]
发表于 2011-11-9 11:55:41 | 显示全部楼层 |阅读模式

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

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

×
有这样一个问题:

求一个形如这样的数:$\frac{n(n+1)}{2}$ 的因子个数。

比如 1+2+3  = 6 = 3*(3+1)/2

这个数的因子是: 1,2,3,6总共4个

对于一个一般的形如上面所说的数,如果才能快速计算出其因子的个数呢?

我的计算方法没任何技巧,比较慢:
  1. for(i=1;;i++)
  2. {
  3.     k = 0;
  4.     for(j=1;j<=i*(i+1)/2;j++)
  5.     {
  6.         if((i*(i+1)/2)%j==0)
  7.         {
  8.             k++;
  9.         }
  10.         if(k>=500)
  11.         {
  12.             cout << "the number is : " << i*(i+1)/2;
  13.             break;
  14.         }
  15.     }
  16. }
复制代码
我是计算因子个数超过500个的时候,这个数是多少。这个程序对于比较小的数还可以,对于稍微大一点的数,就算不出来了。我是对于<i*(i+1)/2 这样形式的数,一个一个去判断,看看取模是否为零,然后确定其是否为其约数。不过效率貌似太低。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 13:23:49 | 显示全部楼层
32位系统,整数的最大值是max_uint=2^32-1=4294967295
若 n*(n+1)/2<max_uint,则n的最大值是92681,n的平方根的最大值304.

对此题目,可使用下面的步骤来做
1. 先用筛法求出304以内的所有素数。
2. 然后对m=n*(n+1)/2 分解素因数。
3. 假定m=p1^e1 * p2^e2 * ... * pn^e5,则这个m的所有因数个数是(e1+1)*(e2+1)*... *(en+1)

by the way, 此题目的复杂度是相当的低。

楼下将给出代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 15:27:15 | 显示全部楼层
代码完成,可在1秒内检查完所有的数。最多有1024个因子
第一个含有1024个因子的数是73359, 73359*(73359+1)/2 有 1024 个因子
  1. // URL: http://bbs.emath.ac.cn/redirect.php?tid=3786&goto=lastpost#lastpost

  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <math.h>
  5. #include <memory.h>

  6. typedef unsigned long DWORD;
  7. typedef unsigned char BYTE;


  8. // 一定范围内质数的个数
  9. //                           10                            4
  10. //                          100                           25
  11. //                        1,000                          168
  12. //                       10,000                        1,229
  13. //                      100,000                        9,592
  14. //                    1,000,000                       78,498
  15. //                   10,000,000                      664,579
  16. //                  100,000,000                    5,761,455
  17. //                1,000,000,000                   50,847,534
  18. //               10,000,000,000                  455,052,511
  19. //              100,000,000,000                4,118,054,813
  20. //            1,000,000,000,000               37,607,912,018
  21. //           10,000,000,000,000              346,065,536,839
  22. //          100,000,000,000,000            3,204,941,750,802
  23. //        1,000,000,000,000,000           29,844,570,422,669
  24. //       10,000,000,000,000,000          279,238,341,033,925
  25. //      100,000,000,000,000,000        2,623,557,157,654,233
  26. //    1,000,000,000,000,000,000       24,739,954,287,740,860
  27. //  估计x 范围内的质数的个数, 欧拉公式 pi(x)=x /( ln(x));

  28. // 经过测试,
  29. // 在1-1百万之间,相邻2个质数的最大差为114,这两个质数分别为492113,492227
  30. // 在1-1千万之间,相邻2个质数的最大差为154,这两个质数分别为4652353,4652507
  31. // 在1-3千万之间,相邻2个质数的最大差为210,这两个质数分别为20831323,20831533
  32. // 在1-1亿之间,  相邻2个质数的最大差为220,这两个质数分别为47326693,47326913


  33. #define MAX_PRIME 302

  34. struct FAC_ARR_ST
  35. {
  36.         int fc;
  37.         struct
  38.         {
  39.                 DWORD p;
  40.                 int   e;
  41.         }arr[10]; //2^32以内的数最大9个素因子
  42. };


  43. struct FAC_ARR_ST g_facTab;

  44. void clear_facTab()
  45. {
  46.         g_facTab.fc=0;
  47.         memset(g_facTab.arr,0,sizeof(g_facTab.arr));
  48. }

  49. void add_to_facTab(DWORD p)
  50. {
  51.         int i;
  52.         int bfind=0;
  53.         for (i=0;i<g_facTab.fc;i++)
  54.         {
  55.                 if ( g_facTab.arr[ i ].p==p)
  56.                 {
  57.                         g_facTab.arr[ i ].e++;
  58.                         bfind=1;
  59.                         break;
  60.                 }
  61.         }
  62.        
  63.         if ( !bfind )
  64.         {
  65.                 g_facTab.arr[g_facTab.fc].p=p;
  66.                 g_facTab.arr[g_facTab.fc].e=1;
  67.                 g_facTab.fc++;
  68.         }
  69. }

  70. //**********************************************
  71. // n 不能大于L2 cache,否则效能很低
  72. DWORD Sift32(DWORD n,DWORD *primeTab)
  73. // 对0到n 之间(不包括n)的数进行筛选,返回质数的个数
  74. //**********************************************
  75. {
  76.         BYTE *pBuff= new BYTE[n];
  77.         if (pBuff==NULL)
  78.                 return 0;

  79.         DWORD i,sqrt_n,p;
  80.         DWORD pn;
  81.         //----------------------------
  82.         sqrt_n=(DWORD)(sqrt(n)+1);
  83.        
  84.         while (sqrt_n * sqrt_n >=n)
  85.                 sqrt_n--;
  86.        
  87.         memset(pBuff,1,n*sizeof(BYTE));
  88.         *pBuff=0;
  89.         *(pBuff+1)=0; //1不是质数
  90.         *(pBuff+2)=1; //2是质数

  91.         for ( i=4;i<n;i+=2) //筛去2的倍数,不包括2
  92.                 *(pBuff+i)=0;
  93.        
  94.         for (i=3;i<=sqrt_n;) //i 是质数
  95.         {
  96.                 for (p=i*i;p<n; p+=2*i)
  97.                         *(pBuff+p)=0; //筛去i 的奇数倍
  98.                 i+=2;
  99.                 while ( *(pBuff+i)==0 )
  100.                         i++; //前进到下一个质数
  101.         }
  102.         pn=0; //素数个数
  103.        
  104.         if ( primeTab==NULL)
  105.         {
  106.                 for (i=2;i<n;i++)
  107.                 {
  108.                         if (pBuff[ i ])
  109.                                 pn++;
  110.                 }
  111.         }
  112.         else
  113.         {
  114.                 for (i=2;i<n;i++)
  115.                 {
  116.                         if (pBuff[ i ])
  117.                         {
  118.                                 primeTab[pn++]=i;
  119.                         }
  120.                 }
  121.         }
  122.         delete[] pBuff;
  123.         return pn;
  124. }


  125. void check_all(DWORD max_n)
  126. {
  127.         DWORD *primeTab=NULL;
  128.         DWORD primeCount;
  129.         DWORD i,j,k;
  130.         int   f_c;                //总的因子数
  131.         int max_fc;

  132.         //创建一个素数表
  133.         primeCount=Sift32(MAX_PRIME,NULL);  //得到MAX_PRIME以内的素数个数
  134.         primeTab=(DWORD *) malloc(sizeof(DWORD)* (primeCount+1));
  135.         Sift32(MAX_PRIME,primeTab);
  136.         primeTab[primeCount]=2147483647; //设置一个栅栏

  137.         max_fc=0;
  138.         //-----------------------------------------
  139.         for (i=2;i<=max_n;i++)
  140.         {
  141.                 DWORD na[2];
  142.                 if (i & 1) //i是奇数
  143.                 {        na[0]=i; na[1]=(i+1)/2;}
  144.                 else
  145.                 {        na[0]=i/2; na[1]=i+1;        }
  146.                
  147.                
  148.                 clear_facTab(); // n*(n+1)/2的素因子数表初始化为空

  149.                 for (j=0;j<2;j++)
  150.                 {
  151.                         DWORD n=na[j];
  152.                         DWORD r;
  153.                         // primeTab[primeCount]=2147483647,保证循环能够终止
  154.                         r=n;
  155.                         for (k=0; primeTab[k] * primeTab[k] <= n && r>1; )
  156.                         {
  157.                                 DWORD prime=primeTab[k];
  158.                                 if ( n % prime==0)
  159.                                 {
  160.                                         add_to_facTab(primeTab[k]);  //增加素因子到素因子表 g_facTab
  161.                                         r/=primeTab[k];
  162.                                 }
  163.                                 k++;
  164.                         }
  165.                        
  166.                         if (r>1)
  167.                                 add_to_facTab(r);

  168.                 }
  169.                
  170.                 f_c=1;
  171.                 for (j=0;j<g_facTab.fc;j++)
  172.                         f_c *= (g_facTab.arr[j].e +1);
  173.                
  174.                 //printf("%u*(%u+1)/2 with %u factor\n",i,i,f_c);
  175.                 if (f_c >max_fc)
  176.                 {
  177.                         printf("%u*(%u+1)/2 with %u factor\n",i,i,f_c);
  178.                         max_fc=f_c;
  179.                 }
  180.         }


  181.         free(primeTab);
  182. }


  183. int main(int argc, char* argv[])
  184. {
  185.         check_all(92681);
  186.         return 0;
  187. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 15:43:01 | 显示全部楼层
有才
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-11-9 16:46:18 | 显示全部楼层
首先感谢liangbch,里面提到的算法很有用:

m=p1^e1 * p2^e2 * ... * pn^e5,则这个m的所有因数个数是(e1+1)*(e2+1)*... *(en+1)


我用这个也算了下,的确速度很快。我是对每个数,统计下其素因子个数,然后按照公式算。
  1. #include <iostream>
  2. using namespace std;
  3. void main()
  4. {
  5.         int a[200],n,i,j,k,ans,answer,l,end;
  6.         for(n=0;n<200;n++)
  7.         {
  8.                 a[n] = 0;
  9.         }
  10.         for(i=1;;i++)
  11.         {
  12.                 k = 0;
  13.                 answer = i*(i+1)/2;
  14.                 ans = i*(i+1)/2;


  15.                 for(j=2;j<=ans;j++)
  16.                 {
  17.                         if(ans%j == 0)
  18.                         {
  19.                                 while(ans%j==0)
  20.                                 {
  21.                                         a[k]++;
  22.                                         ans = ans / j;
  23.                                 }
  24.                                 k++;
  25.                         }
  26.                 }
  27.                 end = 1;
  28.                 for(l=0;l<k;l++)
  29.                 {
  30.                         end = (a[l]+1)*end;
  31.                 }
  32.                 if(end >= 500)
  33.                 {
  34.                         cout << "end is :"<< end << endl;
  35.                         cout << "answer is : " << answer << endl;
  36.                         break;
  37.                 }
  38.                 for(n=0;n<200;n++)
  39.                 {
  40.                         a[n] = 0;
  41.                 }
  42.         }
  43. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 17:24:59 | 显示全部楼层
你这个求m的素因子的代码还是有待改进,在最坏情况下,求m的所有素因子的复杂度是sqrt(m)/ln(sqrt(m)),不必从2逐一试除到m
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 17:51:33 | 显示全部楼层
好厉害。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 20:13:21 | 显示全部楼层
看了一下楼主的代码,并将int 型变量改为unsigned long后,发现楼主的代码也只能检查到65535,当i>65535, 5楼第12行代码会发生整数溢出。
answer = i*(i+1)/2;

我的代码可检查到 92681。

  对楼主的代码做了如下修改,并增加了时间花费检测,发现,当检测到65535时,我的程序用时为21.6毫秒,楼主的程序为2081毫秒。
1.修改变量定义,将int型改为unsigned long,增加数据范围。
2.屏蔽5楼的第36-38行。
36.                       // cout << "end is :"<< end << endl;
37.                        //cout << "answer is : " << answer << endl;
38.                       // break;

3. 将 “for(i=1;;i++)” 改为 “for(i=1;i<65536;i++)”

我的代码有少许修改,
1. 将3楼第210行改为“check_all(65535);“
2.在3楼183行后增加 "if ( primeTab[k]>r) break",如果不修改此处,用时为22.1毫秒
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 20:23:18 | 显示全部楼层
3# 代码价值信息量很大!
需要花点时间好好咀嚼。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-9 20:27:23 | 显示全部楼层
楼上过奖了。关于筛法求素数和分解质因数,楼上亦可称为专家,对楼上来说,我的代码很很稀松平常,没什么值得学习的,不过对于初学者,应该具有参考价值。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-4-28 08:50 , Processed in 0.050225 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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