【发布时间】:2008-08-25 15:55:42
【问题描述】:
在编程或特定语言功能方面,哪种算法教会您最多?
我们都有过这样的时刻,我们突然知道,只是知道,我们已经为未来吸取了重要的一课,基于最终理解程序员编写的算法,在进化阶梯上走了几步。谁的想法和代码对你产生了魔力?
【问题讨论】:
标签: algorithm language-agnostic
在编程或特定语言功能方面,哪种算法教会您最多?
我们都有过这样的时刻,我们突然知道,只是知道,我们已经为未来吸取了重要的一课,基于最终理解程序员编写的算法,在进化阶梯上走了几步。谁的想法和代码对你产生了魔力?
【问题讨论】:
标签: algorithm language-agnostic
通用算法:
数字相关:
数论相关:
我还喜欢研究量子计算(例如Shor 和Deutsch-Josza 算法):这教你跳出框框思考。
如你所见,我有点偏向于数学算法:)
【讨论】:
“迭代是人类,递归是神圣的”- 1989 年在大学引用。
附:由 Woodgnome 在等待加入邀请时发布
Floyd-Warshall all-pairs shortest paths algorithm
procedure FloydWarshall ()
for k := 1 to n
for i := 1 to n
for j := 1 to n
path[i][j] = min ( path[i][j], path[i][k]+path[k][j] );
这就是它很酷的原因:当您第一次在图论课程中了解最短路径问题时,您可能会从解决单源最短路径的 Dijkstra 算法开始。一开始很复杂,但后来你克服了它,你就完全理解了。
然后老师说“现在我们要解决相同的问题,但要针对所有来源”。你对自己说:“天啊,这将是一个更难的问题!它至少比 Dijkstra 的算法复杂 N 倍!!!”。
然后老师给你弗洛伊德-沃歇尔。你的头脑会爆炸。然后你开始为这个算法是多么的简单而哭泣。这只是一个三重嵌套循环。它的数据结构只使用了一个简单的数组。
对我来说最令人大开眼界的部分是以下认识:假设您有问题 A 的解决方案。那么您有一个更大的“超级问题”B,其中包含问题 A。问题 B 的解决方案实际上可能比问题更简单问题A的解决方案。
【讨论】:
这可能听起来微不足道,但对当时的我来说是一个启示。 我在上我的第一堂编程课(VB6),教授刚刚教我们随机数,他给出了以下说明:“创建一个虚拟彩票机。想象一个装满 100 个乒乓球的玻璃球,标记为 0 到 99。随机挑选它们并显示它们的编号,直到它们都被选中,没有重复。”
其他人都这样写他们的程序:挑选一个球,将其编号放入“已选择列表”,然后再挑选另一个球。检查它是否已经被选中,如果是则选择另一个球,如果没有将其编号放在“已选中列表”等......
当然,到最后,他们进行了数百次比较,以找到少数尚未被选中的球。这就像选择它们后将球扔回罐子中一样。我的启示是采摘后扔掉球。
我知道这听起来很明显,但这是“编程开关”在我脑海中翻转的那一刻。这是编程从试图学习一门陌生的外语到试图找出一个有趣的谜题的那一刻。一旦我在编程和乐趣之间建立了这种心理联系,就真的没有人能阻止我。
【讨论】:
霍夫曼编码将是我的,我最初通过将编码文本的比特数从 8 降至更少来制作自己的哑版本,但没有考虑根据频率可变的比特数。然后我发现了杂志上一篇文章中描述的霍夫曼编码,它开辟了许多新的可能性。
【讨论】:
Quicksort。它向我展示了递归是强大而有用的。
【讨论】:
Bresenham's line drawing algorithm 让我对实时图形渲染产生了兴趣。这可用于渲染填充的多边形,如三角形,用于 3D 模型渲染等。
【讨论】:
Recursive Descent Parsing - 我记得如此简单的代码可以完成如此看似复杂的事情给我留下了深刻的印象。
【讨论】:
Haskell 中的快速排序:
qsort [] = []
qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)
虽然当时我不会编写 Haskell,但我确实理解了这段代码以及递归和快速排序算法。它只是点击了一下,它就在那里......
【讨论】:
斐波那契的迭代算法,因为对我来说它确定了一个事实,即最优雅的代码(在本例中为递归版本)不一定是最有效的。
详细说明-“fib(10) = fib(9) + fib(8)”方法意味着 fib(9) 将被评估为 fib(8) + fib(7)。因此对 fib(8)(以及 fib7、fib6)的评估都会被评估两次。
迭代方法(curr = prev1 + prev2 in a forloop)不会以这种方式生成树,也不会占用太多内存,因为它只有 3 个瞬态变量,而不是递归堆栈中的 n 帧。
我在编程时倾向于追求简单、优雅的代码,但正是这种算法帮助我意识到,这并不是编写好软件的全部目标,而最终是终点用户并不关心您的代码的外观。
【讨论】:
出于某种原因,我喜欢Schwartzian transform
@sorted = map { $_->[0] }
sort { $a->[1] cmp $b->[1] }
map { [$_, foo($_)] }
@unsorted;
其中 foo($) 表示计算密集型表达式,该表达式采用 $(依次为列表中的每个项目)并生成相应的值,以便对其进行比较。
【讨论】:
Minimax 告诉我国际象棋程序并不聪明,它们只会比你想出更多的动作。
【讨论】:
我不知道这是否属于一种算法,或者只是一种经典的 hack。无论哪种情况,它都帮助我开始跳出框框思考。
在不使用中间变量的情况下交换 2 个整数(在 C++ 中)
void InPlaceSwap (int& a, int &b) {
a ^= b;
b ^= a;
a ^= b;
}
【讨论】:
快速排序:在我上大学之前,我从未质疑过暴力冒泡排序是否是最有效的排序方式。它看起来很直观。但接触 Quicksort 等非显而易见的解决方案后,我学会了忽略显而易见的解决方案,看看是否有更好的解决方案。
【讨论】:
对我来说,这是弱堆排序算法,因为它展示了 (1) 明智选择的数据结构(以及处理它的算法)可以在多大程度上影响性能,以及 (2) 即使在旧的情况下也可以发现令人着迷的东西,众所周知的事情。 (弱堆排序是所有堆排序中最好的变种,八年后是proven。)
【讨论】:
【讨论】:
出于某种原因,冒泡排序对我来说一直很突出。不是因为它优雅或好,只是因为我想它有/有一个愚蠢的名字。
【讨论】:
斐波那契的迭代算法,因为对我来说它确定了一个事实,即最优雅的代码(在本例中为递归版本)不一定是最有效的。
迭代方法(curr = prev1 + prev2 in a forloop)不会以这种方式生成树,也不会占用太多内存,因为它只有 3 个瞬态变量,而不是递归堆栈中的 n 帧。
您知道斐波那契有一个封闭形式的解决方案,它允许以固定的步数直接计算结果,对吧?即,(phin - (1 - phi)n) / sqrt(5)。我总是觉得这应该产生一个整数有点不可思议,但确实如此。
当然,phi 是黄金比例; (1 + sqrt(5)) / 2.
【讨论】:
我没有最喜欢的——有很多漂亮的可供选择——但我一直觉得很有趣的是Bailey–Borwein–Plouffe (BBP) formula,它可以让你计算出一个不知道前面的数字的任意数字。
【讨论】:
RSA向我介绍了模运算的世界,可以用来solveasurprisingnumberofinterestingproblems!
【讨论】:
并没有教会我太多,但Johnson–Trotter Algorithm 总是让我大吃一惊。
【讨论】:
Binary decision diagrams,虽然形式上不是算法,而是数据结构,但可以为各种(布尔)逻辑问题提供优雅且最小的解决方案。它们的发明和开发是为了最大限度地减少芯片设计中的门数,并且可以被视为硅革命的基础之一。生成的算法非常简单。
他们教给我的:
【讨论】:
对我来说,当我第一次看到它时,在 Kelly 和 Pohl 的 A Book on C 中演示引用调用的简单交换让我大吃一惊。我看了看,指针突然就位。逐字。 . .
void swap(int *p, int *q)
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}
【讨论】:
Towers of Hanoi algorithm 是最漂亮的算法之一。它展示了如何使用递归以比迭代方法更优雅的方式解决问题。
另外,斐波那契数列的递归算法和一个数字的计算幂证明了递归算法的相反情况,它被用于递归而不是提供良好的价值。
【讨论】:
一种通过将每个数字与当前素数列表进行比较来生成素数列表的算法,如果没有找到则添加它,并在最后返回素数列表。在几个方面令人费解,其中最重要的是使用部分完成的输出作为主要搜索条件的想法。
【讨论】:
在一个单词中为双向链表存储两个指针让我学到了在 C 中确实可以做非常糟糕的事情(保守的 GC 会遇到很多麻烦)。
【讨论】:
我最自豪的解决方案是编写与 DisplayTag 包非常相似的东西。它教会了我很多关于代码设计、可维护性和重用的知识。我在 DisplayTag 之前就写好了它,它被纳入 NDA 协议,所以我不能开源它,但我仍然可以在工作面试中滔滔不绝地谈论它。
【讨论】:
【讨论】:
不是我最喜欢的,但用于测试素性的Miller Rabin Algorithm 向我表明,几乎所有时间都是正确的,几乎所有时间都足够好。 (即不要仅仅因为概率算法有错误的概率而怀疑它。)
【讨论】:
@克里希纳库马尔
按位解决方案比递归解决方案更有趣。
【讨论】: