【Wolfram 到底有多厉害】- 神奇代码系列 04
书接上文:
原作者是Martin Büttner, 由知乎回答者 AlephAlpha 汉化翻译, 这里已或授权转发, 并且我再整理补充一点内容, 中文链接您可以在最下阅读原文找到链接.
另外,这里的很多代码片段仅适用于Mathematica 10及以上版本。
再来一点组合数学。这段代码给出了由这个列表的元素组成的所有3个元素的组合。即使列表中的元素有重复,给出的组合也不会重复.
一个不太寻常的绘图。它可以在数轴上绘制一系列不同的东西,比如说点和区间。你还可以给出一个条件,它会绘制出使条件成立的区域. 这里的箭头表示这个区域会延伸到无穷大,空心的圈圈则表示这是开区间。如果是闭区间,圈圈会变成实心。
Mathematica包含了一系列的范例数据,从图片、纹理、声音片段到3D模型(比如说犹他茶壶)。Lena的名字相当短,也还算出名,所以作者选择了她.
Mathematica可以用来解方程,或者方程组。和往常一样,给出的也是符号解. 注意解是以规则的形式给出。后面可能会有关于规则的更具体的例子。
一个非常漂亮的绘图,绘制的是一个二维向量场的流线。类似于常规的向量图,它的每一个箭头都相切于向量场;不过,箭头不是按固定的点阵来排列,而是首尾相接连成流线。如果这个向量场是流体的速度场,这些线就是流体中粒子的轨迹。
类似也有 VectorPlot 命令, 您可以尝试比较两者细微的不同之处.
又该展示新的语言特性了。Mathematica中有几个定义函数方面的好处。首先,你可以给一个函数名提供不同的定义,只要参数的数量或类型不同。你可以用模式来描述参数的类别。而且,你还可为给单独的值添加定义。调用函数的时候,Mathematica会自动选取最特殊的定义;如果没有符合的定义,则会不进行任何计算。这使得可以以更自然的方式来定义递归函数,而不必用If来在不同情况间切换。
另一点要注意的是这里同时用到了=和:=。不同之处在于=的右边只是在定义的时候计算一次,而:=的右边则是在每次调用的时候计算。其实在定义变量的时候也可以用:=,这时变量就会有动态的值。
因此上面定义的就是斐波那契数列。不过这是一个非常低效的定义,在作者的电脑上计算前30个数就画了1.625秒。在后面我们会看到,只要稍作改进,甚至不用改掉递归,便能大大提高其性能。
上一个代码片段中提到了模式。模式常被用在规则中,规则则常用于修改匹配某一模式的结构。让我们来看看这个代码片段。{a___, x_, b___, x_, c___} :> {a, x, b, c} 是一个规则。x后面跟着一个下划线 (x_) 是一个模式,它可以代表任意的值 (这个值本身也可以是一个列表或者类似的东西)。a___则是一个序列模式 (参见长度为15的代码片段),它可以代表长度为0或者更长的序列。这里x_出现了两次,说明其代表的是相同的值。因此,整个模式 {a___, x_, b___, x_, c___} 匹配的是一个有重复元素的列表,两个x代表的是重复的元素,a、b和c代表的是围绕这两个元素的序列。根据规则,它被替换为 {a, x, b, c},也就是去掉了第二个x。
//. 会重复地执行一个规则,直到找不到可以匹配这个模式的结构。因此,上面的代码会去掉列表l中所有重复的元素。不过,它的功能并不仅限于此://. 会在任何层次上运用规则,因此如果l中的某个元素也是列表,这个列表中重复的元素也会被去掉。
又回到绘图。它绘制的是是条件成立的二维区域。当你想绘制一个不以显式表示区域的话,这个函数非常有用。
前面说过要把斐波那契数列的定义改得更高效。这段代码展示了menmoization 在Mathematica中是多么平凡。这里唯一的改动就是在第三行增加一个f[n] =。这样,每次f计算一个新的值,比如说f[3] 的时候,执行了f[3] = f[3 - 1] + f[3 - 2]。这计算了f[2] + f[1],然后把结果赋值给f[3] (注意这里用的是 = 而非 :=)。因此,每次调用f计算一个没计算过的值的时候,它就会为这个值添加一条新的定义;这一定义显然比一般的定义更特殊,因此在再次计算这个值的时候就会直接使用这个定义。这里计算前 1000 个近似 0 秒 (请勿在内存太小的计算机上尝试)。
和你预料的一样,SortBy的功能是根据一个列表中每个元素应用某个给定的函数的结果来给列表排序。不过等等,这里调用SortBy的时候并没有给它一个列表!其实,在Mathematica 10中,某些函数开始支持柯里化(Currying)和部分应用。与某些纯函数式的语言不同,这不是一个语言特性,只是一批特定的函数的功能。上面这段代码返回的是一个新的函数,这个函数仅仅以一个列表为参数,并且根据前面给定的函数来为这个列表排序。当你在代码中要多次用到同一个排序规则的时候,这个用法会非常有用。
另外,这里又出现了一个Data。这是根据逃逸速度来给行星的名字排序。
差点忘了Thread。如果你有两个长度相同的列表,想对位置相应的每一对元素进行某个操作。当然,你可以对列表长度进行迭代;也可以连接两个列表,转置这个矩阵,然后对每对元素进行操作。不过Thread让这一切变得更加简单。大致上,它就是把最顶层的Head(函数或类型)往下"推"一层。所以上面的代码算出来是这样的输出结果.
在Mathematica中,让东西动起来很简单,它们甚至不必是图像。你只要给出一个在每帧进行计算的表达式,以及随帧数变化的变量。上面的代码会生成一个正弦波的动画.
前面提到过图。Mathematica中有大量常用的图论方面的功能,除了解决各种图论问题的函数外,还有漂亮的可视化工具。例如上面的代码,对于给定的图graph,找出一个顶点数最小的顶点覆盖,并且将图中的这些顶点突出显示。例如,如果graph是长度为18的代码片段中的PetersenGraph[7,2]的话,会得到上面的输出.
终于有足够多的字数来用上CellularAutomaton这个函数并画出其结果了。据作者所知,这是Mathematica中唯一一个关于元胞自动机的函数。但Stephen Wolfram似乎认为自己是元胞自动机领域的第一人,因此这个函数尤其强大。这里展示的只是它最简单的用法。它模拟的是一个一维元胞自动机的前100步,事实上它返回的是每一步的状态,因此结果是一个二维的数组。函数的第一个参数是元胞自动机的规则,可以用一个列表来详细地指定,也可以只用一个简单的数字编码。在这个例子中,作者选择了著名的图灵完备元胞自动机,Rule 110。{{1},0}定义了初始条件:单独的一个1,以及作为背景的0。在后面会展示CellularAutomaton的更多特性:它可以模拟更高维数、更大邻域、更多状态的元胞自动机。
ArrayPlot是又一个绘图的工具,用于绘制二维数组,以不同颜色的方块表示数组中不同的值。在最简单的情形下,0对应于白色,1对应于黑色。
然后是图像处理。Mathematica包含了一系列的范例数据,包括图像(例如Lena)、纹理、3D模型和声音片段。
Import是一个相当强大的命令。它既可以导入磁盘中的文件,也可以下载网上的文件。它懂得很多种不同的文件格式,还可以从其中的一些(例如HTML)直接提取数据。上面的代码会下载Google Doodle页面的所有图片。
还有更好精彩的代码展示, 我们下次再继续探索......
祝各位春节假期愉快!
相关文章: