现在的位置: 首页 > 综合 > 正文

编程之美—-1

2013年08月02日 ⁄ 综合 ⁄ 共 3590字 ⁄ 字号 评论关闭
  1. <span style="font-family: 微软雅黑;">这是《编程之美》的2.20题目,给出一段C#代码,要求不用电脑,理解程序并回答问题。下面是从C#代码中改写成的C++代码:</span>  

  1. <span style="font-size:14px;">#include <iostream>  
  2. #include <limits>  
  3. using namespace std;  
  4. int main() {  
  5.      int rg[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,  
  6.          20,21,22,23,24,25,26,27,28,29,30,31};  
  7.      for(__int64 i =1; i < numeric_limits<__int64>::max(); i++) {  
  8.          int hit = 0;  
  9.          int hit1 = -1;  
  10.          int hit2 = -1;  
  11.          for(int j = 0; j < sizeof(rg)/sizeof(*rg) && (hit <= 2); j++) {  
  12.               if((i % rg[j]) != 0) {  
  13.                    hit++;  
  14.                    if(hit == 1) {  
  15.                        hit1 = j;  
  16.                    } else if(hit == 2) {  
  17.                        hit2 = j;  
  18.                    } else break;  
  19.               }  
  20.          }  
  21.          if(hit == 2 && hit1+1==hit2) {  
  22.               cout << "find " << i << endl;  
  23.               break;  
  24.          }  
  25.      }  
  26.      return 0;  
  27. }</span>  

1. 首先看在满足什么条件,才会输出i的值呢?
先看条件hit == 2,该条件预示着:(i % rg[j]) != 0)要有且仅有两次成立。
再看条件hit1+1==hit2,通过该条件可以得出满足条件(i % rg[j]) != 0)的两个数j1, j2必须是相邻的。
基于上面的分析,我们可以得出程序的输出结果i应该满足:
i不能被2-31这30个数中的两个相邻的数整除,但能被其余28个数整除。也就是说:i肯定是其它28个数的最小公倍数的整数倍。
如何找到满足上面条件的2个数呢?
简单的考虑,素数是满足要求的,因为素数不可能被其他的自然数的乘机所整除。还有没其他的数也满足呢?
i不能被两个相邻的数整除,所以必然是i分解质因子后要么i的质因子中不包括这两个数的质因子,要么是i的质因子的次数小于这两个数中相同质因子的次数。
举个例子,假设8是满足条件的两个数之一(即8不能被其余数的乘积所整除),很明显我们可以找到一个反例--16,因为16i的一个因子,由于16能被8整除,那么i必然能被8整除,假设不成立,所以8不是我们所求的数。
但16是可以的,想想这是为什么?
那么,只需要给2-31这30个数分解质因数,找一下是否有这样的相邻的两个数,要么它们的质因子中有其它数没有的质因子,要么对于相同的一个质因子,这两个数包含这个质因子的次数高于其它所有次数(注意只需要该质因子高于其他所有rg中数的该因子的次数,而非其他所有该因子的次数之和,原因是最后只需最小公倍数)。为此建立一张表如下:

由上表中可以看出,只有16、17、19、23、25、27、29、31这几个数包含次数最高的质因子。而相邻的则只有16,17。所以,这段程序所要求的数i就是,它不能被16、17整除,但能被30个数中的其它28个数整除,最小的i就是其它28个数的最小公倍数,从上表中知道,这个最小的i是:8(2的3次方)*27(3的3次方)*25(5的2次方)*7*11*13*19*23*29*31,用计算器计算出这个数是:2123581660200。可以把上述程序中的for循环中的i初始化成这个数来检验。
拓展阅读1:最小公倍数是如何求得的?
素数是不能被1和自身数以外的其它数整除的数;素数X的N次方,是只能被X的N-1以下次方,1和自身数整除.
所以,在求A,B,C,D,E,…,Z的最小公倍数时,只需要把这些数分解为素数的N次方之间的乘积后,取各素因子的最高次方的乘积,就是这些数的最小公倍数.
举例说明:
求756,4400,19845,9000的最小公倍数?
因756=2*2*3*3*3*7,4400=2*2*2*2*5*5*11,19845=3*3*3*3*5*7*7,9000=2*2*2*3*3*5*5*5,这里有素数2,3,5,7,11.2最高为4次方16,3最高为4次方81,5最高为3次方125,7最高为2次方49,还有素数11.得最小公倍数为16*81*125*49*11=87318000.
拓展阅读2:
JOJ的2042题目也是一个程序理解题目,这个题目非常有意思,给出了下面一段C++源代码,要求计算出最后的输出结果,源代码如下:
  1. <span style="font-size:14px;">#include<cstdio>  
  2. int main(void)  
  3. {  
  4.      int x = 987654321, c = 0, d = 1, e = 6;  
  5.      while(x--){  
  6.          c += d,  
  7.          d += e,  
  8.          e += 6;  
  9.      }  
  10.      printf("%d/n", c);  
  11.      return 0;  
  12. }</span>  

原题目如下:
We can use axioms to calculate programs just like what we do in algebras. Dijkstra is the one who advocates such constructive approach whereby a program is designed together with its correctness proof. In short, one has to start from a given postcondition Q
and then look for a program that establishes Q from the precondition. Often, analyzing Q provides interesting hints to finding the program. This approach is quite different from the well known Hoare Logic.
For example, the following program is calculated by Dijkstra's approach. Unfortunately, its annotation is lost so that its function is hard to grasp. You are to help with finding the final value of the variable c. Note that the program is designed under 128-bit
architecture.

代码就是上面那一段。

这个题目通过小数据计算可以看出规律:x=1, c = 1; x=2, c=8; x=3, c=27; x=4, c=64,于是可以猜测这段程序是用来计算x^3的。用计算器计算出987654321^3,提交上去就AC了。

这个题目是超级大牛SIYEE出的。从题目本身的叙述中就学到了很多东西。又知道了一个数的立方还可以这样计算。可惜数学功底差,不知道在数学上是如何推导出来的。

参考链接:http://blog.csdn.net/jcwKyl/article/details/3889802

抱歉!评论已关闭.