快与慢 0 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—
2
m
-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!≤
10
m
;两边取对数 log10n + log10 (n-1) +…+ log10 (2) ≤ m,对m做天花板(很形象的名称)操作┌m┐,最好再加上1个字节,担心在取对数时会有微小精度损失。 5、程序写完后应该检验与实际结果是否吻合,于是你打开matlab、mathematica或者os自带的计算器验证,发现结果正确。这时你才发现已经过了好几个钟头,黄花菜都凉了,而老板只是要你算很少几个数的阶乘。是的,你只要用计算器算出结果告诉他就行,可以事先算出来硬联编到程序中,很多快速算法都是类似的建表的思路。(FFT) 建表只是现象,本质是更简单更清晰地解决问题。我经常学了算法后反而笨手笨脚了。 |