a、老板让你最快的计算 f(N) = 13+23+…+N3。逐项计算当然是可以的,如果能直接算出来当然会大大加快电脑计算的速度。
实际上f(N) = (1+2+…+N)2 = N2*(N+1)2/4。
b、Josephus(约瑟夫)问题。这是个老生常谈的话题,大意如下:
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
法1,用循环链表。
法2,获得递推公式。t表示约瑟夫环中人数,J(t)表示在t人中的胜利者。
有n个人编号0、1、……、n-1,J(t) = (J(t-1) + m%t)%t = (J(t-1) + m)%t.,且J[1] = 0;最终结果为J[n] +k。
c、老板让你编个程序,以最快的速度计算50以下数的阶乘(比如15!)。如果以前遇到过这个问题或者思维缜密,会在考虑一些问题后可以写出一个程序,考虑的问题先后有:
1、 根据阶乘的定义用一个迭代实现。当然你首先会考虑到n!在n相当大的时候可以用stirling公式去逼近,
____
n! ~ √ 2πn (n/e)n
很不幸这个近似公式在n-> +∞时实际值与近似值比值为1。不过你想至少可以用stirling来做测试近似检验算法是否正确。
2、但是即使是13!都超过了32位表达范围。所以不管是用迭代、直接连乘还是stirling都存在这个问题——上溢。你想到要组织一个自己的记录大数的结构
3、用数组吧。构造一个长度为m数组假设足够长能容纳结果,数组的index从0到m的字节表示位权从20—2m-1。
2m-1 | …… | 213 | 212 | 211 | 210 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 |
|
| 0 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 2 | 3 | 4 | 5 |
比如表格里的数表示9876543212345。(题外话,在这里想起一个比较好玩的数111,111,1112 = 12345678987654321)
4、根据这个记录数据的结构,设计乘法操作,比如对9876543212345 * 15,把15与每位的乘积保存,再加上上一位的进位,除以10的商用作下一位的进位;模10的结果作为这位的值。
unsigned int carry = 0u;
unsigned int mul = Num[i] * 15u + carry;
carry = mul/10u;
mul /= 10u;
Num[i] = mul;
还要计算数组到底需要多大才能容纳这个阶乘值。
n!≤ 10m;两边取对数 log10n + log10 (n-1) +…+ log10 (2) ≤ m,对m做取顶操作┌m┐,最好再加上1个字节,担心在取对数时会有微小精度损失。
5、程序写完后应该检验与实际结果是否吻合,于是你打开matlab、mathematica或者os自带的计算器验证,发现结果正确。这时你才发现已经过了好几个钟头,黄花菜都凉了,而老板只是要你算很少几个数的阶乘。是的,你只要用计算器算出结果告诉他就行,可以事先算出来硬联编到程序中,很多快速算法都是类似的建表的思路。(FFT)
建表只是现象,本质是更简单更清晰地解决问题。我经常学了算法后反而笨手笨脚了。