找回密码
 欢迎注册
查看: 16568|回复: 6

[讨论] 一道和角谷静夫猜想相关的问题

[复制链接]
发表于 2011-11-30 00:04:34 | 显示全部楼层 |阅读模式

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

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

×
最近在看EulerProject上的问题: 有一道问题是这样的: 角谷静夫猜想是说:对于一个任意的自然数,总可以通过以下步骤变为1: $f(n) = \frac{n}{2}.$ 如果 n 是偶数 $f(n) = 3 \times n + 1.$ 如果n 是奇数 对于不同的数,需要的步骤肯定不同,问题是1000000以内的数,哪个数需要的步骤最多? 我的想法比较粗糙:就是对每个数去统计.然后选取一个最长的。 对于每个数,到底需要多少次才可以到达1应该是未知的。假设统计这么多所需要的平均次数是T,那么就做了 1000000*T次循环,然后还有(2*T+1)*1000000次判断,我的代码如下;
  1. int main()
  2. {
  3. //open file to output
  4. ofstream outfile("G:/math/ans.txt");
  5. long ans = 0;
  6. long index;
  7. long a,b;
  8. for(long i = 2; i < 1000000; )
  9. {
  10. a = i;
  11. index = 0;
  12. while(true)
  13. {
  14. if(a == 1)
  15. {
  16. break;
  17. }
  18. if(a % 2 == 0)
  19. {
  20. a = a/2;
  21. }
  22. else
  23. {
  24. a = a*3 + 1;
  25. }
  26. index++;
  27. }
  28. if(index > ans)
  29. {
  30. ans = index;
  31. b = i;
  32. }
  33. i+=2;
  34. }
  35. if(ans < 500000)
  36. {
  37. ans = ans * 2;
  38. b++;
  39. }
  40. outfile << "这个数是:" << ans << endl;
  41. outfile << "需要的次数:" << b << endl;
  42. }
复制代码
不过貌似我的电脑根本跑不出来这个结果。是不是我的计算方法太愚蠢了。(我考虑偶数可以不用判断了。如果判断出来最大的数是<500000的奇数,则考虑这个数*2的那个偶数,就是需要步骤最多的。)我跑了很久结果也出不来
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-30 07:36:57 | 显示全部楼层
我发现楼主的代码有一个bug。楼主显然没有考虑到整数溢出问题,在执行下面的语句时,可能会发生整数溢出而导致结果错误。例如,当n=159487,在计算到第60步时,a*3+1的值就会超过32bit,这时,如果变量类型为unsigned long, 在32bit编译器下编译,就会出现错误。我注意到楼主的代码中,数据类型为long,那么溢出的几率会更高
a = a*3 + 1;
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-30 07:45:57 | 显示全部楼层
  我没有仔细调试楼主的程序。我自己写了一个程序,在PM1.7上运行,计算 100万/1000万/1亿之内的数需要的步时, 需要375/6250/93265毫秒。  几点说明: 1.在计算过程中,我的程序使用unsigned __int64(VC编译器) 型变量,避免溢出。 2.我的程序作了一点儿优化,事先计算出1-65536之间的数所需的步骤,在计算大于65536的数所需的步骤时,一旦发现计算过程中,a的值<=65536,那么剩余的步骤可直接查表。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-11-30 07:48:39 | 显示全部楼层
本楼给出所有源代码
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <windows.h>
  4. typedef unsigned __int64 UINT64;
  5. typedef unsigned long DWORD;
  6. typedef unsigned short WORD;
  7. WORD g_steps_tab[65536];
  8. int steps(DWORD n)
  9. {
  10. UINT64 x=n;
  11. int r=0;
  12. while ( x >1)
  13. {
  14. if ( x & 1) //x 是奇数
  15. {
  16. if ( x>=6148914691236517205I64)
  17. {
  18. printf("ERROR,n=%u,integer overflow, can not continue\n",n);
  19. return 0;
  20. }
  21. x=x*2+x+1;
  22. r++;
  23. }
  24. else
  25. {
  26. x=x /2;
  27. r++;
  28. }
  29. }
  30. return r;
  31. }
  32. //在计算过程中,当n的值小于65536,直接查表
  33. //tab[i] 表示 i+1 转化为1需要的步数
  34. int steps_pro(DWORD n,WORD tab[])
  35. {
  36. UINT64 x=n;
  37. int r=0;
  38. while ( x >1)
  39. {
  40. if ( x <=65536)
  41. {
  42. return r+tab[x-1];
  43. }
  44. else if ( x & 1) //x 是奇数
  45. {
  46. if ( x>=6148914691236517205I64)
  47. {
  48. printf("ERROR,n=%u,integer overflow, can not continue\n",n);
  49. return 0;
  50. }
  51. x=x*2+x+1;
  52. r++;
  53. }
  54. else
  55. {
  56. x=x /2;
  57. r++;
  58. }
  59. }
  60. return r;
  61. }
  62. void test2(DWORD to)
  63. {
  64. DWORD i,max_n;
  65. int r,max_r;
  66. max_r=0;
  67. DWORD t;
  68. t=GetTickCount();
  69. for (i=1;i<=65536;i++)
  70. {
  71. r=steps(i);
  72. g_steps_tab[i-1]=r;
  73. }
  74. max_r=0;
  75. for (i=1;i<=to;i++)
  76. {
  77. r=steps_pro(i,g_steps_tab);
  78. if (r>max_r)
  79. {
  80. max_r=r;
  81. max_n=i;
  82. }
  83. }
  84. t=GetTickCount()-t;
  85. printf("Calc up to %u, take %d ms\n",to,t);
  86. printf("steps[%d]=%d\n",max_n,max_r);
  87. }
  88. int main(int argc, char argv[])
  89. {
  90. test2(1000000);
  91. test2(10000000);
  92. test2(100000000);
  93. return 0;
  94. }
复制代码

评分

参与人数 1威望 +2 收起 理由
G-Spider + 2 精彩。

查看全部评分

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-12-9 00:11:28 | 显示全部楼层
非常感谢~~,看了收获很大。 有个小问题: 把 x = x*3 + 1; 写成: x=x*2+x+1; 又没有什么特别的原因? 我运行时间好像差不太多。 我是AMD 2800+ , 512MB内存 计算1000000以内花费406MS-500MS。 判断奇数的方法+查表比我的节约非常多时间。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-12-9 10:50:43 | 显示全部楼层
是基于优化的考虑。 位运算一定比除法快。因此, 1. 判断是否是奇数写成 "(n & 1)==1" 比 " n % 2 ==1" 快 2.“ x=x*2+x+1;” 对通常的编译器,编译成的指令为x=(x<<1)+x+1, 不使用乘法指令。而"x=x*3+1;"对先进的编译器(如intel编译器)会编译成x=(x<<1)+x+1,但对于老的编译器,可能依然使用乘法指令来实现。乘法指令要比移位指令慢得多。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-12-9 22:36:13 | 显示全部楼层
原来这样,好精彩,非常感谢
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-11-24 21:12 , Processed in 0.026878 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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