|
首先映入我脑海里的是 John Carmack 的Quake引擎里的一段代码, 求一个数的平方根的倒数(注意是倒数,不是导数). John Carmack 是id software的创始人, 也是经典第一视角游戏 Doom 和 Quake 的作者. 他对自己的Quake游戏引擎进行开源, 极大地促进了整个3D游戏行业前进的步伐. 连比尔盖茨都将 Carmack 列为自己的崇拜对象!
一般能想到的办法是:
1. 调用系统库函数: sqrt, 然后再除1;
2. 二分法逼近 (之前Facebook的有道面试题即是);
3. 牛顿迭代法
而在Quake渲染引擎中, 有这么一段代码 (将其标号: #4 Quake InvSqrt 算法):
float InvSqrt (float x){
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
如果有兴趣的朋友自己去写一个程序去测试, 发现上述4中方法的运算效率:
#4 > #1 > #3 > #2.
借用一位朋友的照片:
看见Carmack代码里面的方法比起系统方法还快一倍以上, 而且实现方式短小且神奇. 其本质上也是使用了牛顿迭代法, 但是通过预先猜测的一个神奇数字 0x5f3759df, 来将迭代次数降到极限的一次 (另外通过整形数的移位来进一步加速). 这让我不由得想到苏联飞机米格29引擎的暴力美学. 详细解说看这个: Beyond3D - Origin of Quake3's Fast InvSqrt()
P.S. 为什么InvSqrt在Quake引擎中被重新实现? 这是因为3D变换过程中要涉及到大量的平方根翻转操作 (比如3D向量的normalization), 所以对于这个函数的优化对于3D引擎的效率非常关键. 这也直接促使nVIDIA在最新的GPU的指令集中自带"nrm"; 此命令来借用硬件加速直接完成3D向量的normalization操作.
类似的, Quake3里的开方函数源代码:
double sqrt( double x ) {
float y;
float delta;
float maxError;
if ( x <= 0 ) {
return 0;
}
// initial guess
y = x / 2;
// refine
maxError = x * 0.001;
do {
delta = ( y * y ) - x;
y -= delta / ( 2 * y );
} while ( delta > maxError || delta < -maxError );
return y;
}同样也是短小精妙. 另外附带
Quake引擎架构分析: Quake Source Code Review
Quake3代码分析: Quake 3 Source Code Review: Architecture
Quake代码的GitHub页: id-Software/Quake · GitHub
附带我偶像John Carmack的一张照片 (祝Oculus在Facebook帝国的支持下欣欣向荣):
另外, 收到 @陈然 答案的启发 (那个等概率选数问题我在Facebook的最后一面上刚好碰到, 因为之前面试根本没有准备过, 导致自己最后面试上绞尽脑汁...), 所以我说一个自己对Facebook一道面试题经过多年没事想想后的顿悟!!!
计算Fibonacci(斐波那契)数列的第n项: 这个之前在我的Facebook面试总结里面有写过, 可以递归也可以递推, 这样的复杂度是O(N), 也可以用矩阵相乘的算法, 这样可以O(LogN)的复杂度解决. 本来也就这样作罢, 但是最近看了&#34;星际穿越&#34;之后, 突然顿悟到这个Fibonacci和星际穿越有异曲同工之妙:
在一维世界中只能通过公式 F(n) = F(n-1) + F(n-2) 来计算. 这时, 线性的算法(O(N))是一维世界下的极限. 要如何加速呢? 类推到星际穿越中的话, 可行的方法就是升维, 从一维升到二维空间 (一维的F(n)升级到二维矩阵), 也就是说我们用升维将一维空间进行扭曲从而将距离缩短. 于是 [F(n), F(n-1)] = M^(n-1) [1, 1] ( M 是一个二维的常数矩阵). 这样我们便可以更快地从 [1, 1] 到达 [F(n), F(n-1)]. 看! 多么像一个数学世界里面的时空穿越呀!
先就到这里, 想到更多有趣的, 再进行补充. |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|