我比较喜欢Mathematica,感觉用起来特别方便,因为我大多时候需要把公式计算出来,涉及到符号运算、数值计算。没有Mathematica,我会手动计算公式,经过多次转化,结果也不知道对不对。如果有了结果,才会运用Matlab进行数值计算。这个过程特别烦心。因为中间的数学符号运算太难算了。
有了Mathematica减少了不少的工作压力。真的很方便。符号运算应该是Mathematica最突出的地方。数值计算有些地方做得不错,像解方程、微分方程。但是规划求解就弱了很多,很多地方不如Lingo,图论也不如Sege。当然一个软件不可能做得尽善尽美的。
我所涉及到的内容做如下 比较:
matlab:矩阵相关运算、求解模拟器很方便。
mathematica:符号运算、方程求解、特别是微分方程求解很方便。
SageMath:用python做出来的,对别人的包特别熟悉,那特别方便。
Lingo:线性规划、非线性规划等特别方便。
Julia:最近特别火,便捷性要与Matlab、python、mathematica、Lingo等对齐,速度要与C对齐,如果真的实现了,那一定是完美的。但是包管理不是很好,经常出错。用了一段时间,感觉Julia 是mathematica和Lingo两个合体。逻辑是采用了mathematica的逻辑,一切都是表达式。
下面开始说说Mathematica。说说Mathematica逻辑:
1 第一条原则:任何元素都是一个表达式(Expression)
Mathematica 所处理的每一个对象都是一个表达式(Expression),而每一个表达式,要么是一个被称为原子(Atom)的个体,要么是一个普通的表达式(Normal Expression)。
1.1 原子和内置的 AtomQ 判定函数
原子包括数、符号和字符串,其中数又包括整数、实数、有理数和复数。其它所有对象均为复合表达式,称为普通表达式。通过应用内置断语函数 AtomQ,我们可以检验一个表达式是一个原子还是一个复合表达式。例如
In[x]= Clear[Global '*']#清除变量所有值,注意mathematica函数体要用[...]括起来。而且mathematica函数首字母大写,所以以后我们给函数命名时首字母小写,小驼峰法,避免与系统函数冲突。
In[x]= {AtomQ[x], AtomQ[Sin[x]], AtomQ[1+I*2], AtomQ[2/4]}#Atom[]就是检验是否为原子。
Out[X]:= {True, False, True, True}#In是输入,Out是返回
1.2 Mathematica 普通(复合)表达式
在 Mathematica 语言中,每一个普通(复合)表达式都是根据如下的通用模式构建的:
expr[el1, ..., eln] #以后看到expr就是表达式的意思。
该模式中必须含有一个符号< expr >和一对单方括号。(符号< expr >本身也可以是一个常规表达式,不必一定要是原子)。方括号内可以没有任何元素,也可以有一个或由逗号分隔开的若干个元素<el1>,...,<eln>。这些元素本身又可以是原子或普通表达式。例如在表达式 Sin[x] 中,符号<expr> 为 Sin,它含有一个元素 <x>,是一个原子(只要 x 到目前还没有做过其他定义的话。这将涉及到一个表达式的计算运行问题,我们会在后面讨论)。由此可见在 Mathematica 语言中,任何一个表达式都具有树的结构,其中树的枝为普通(子)表达式,而其叶则都是原子。
1.3 内置函数的文字表达形式和 FullForm 命令
由于如上所述的结构,任何一个 Mathematica 内置函数/命令,都相应有一个等价的文字表述形式,使得所有表达式都能按这种结构表示为一种统一的形式。我们可以借助内置的 FullForm 函数来看清这一点。FullForm 给出一个对象/表达式的一个内部表达形式,它是真正被内核直接接受的形式。例如:
由于如上所述的结构,任何一个 Mathematica 内置函数/命令,都相应有一个等价的文字表述形式,使得所有表达式都能按这种结构表示为一种统一的形式。我们可以借助内置的 FullForm 函数来看清这一点。FullForm 给出一个对象/表达式的一个内部表达形式,它是真正被内核直接接受的形式。例如:
In[X]:= {z * Sin[x +y], FullForm[z * Sin[ +y]]}
Out[X]:= {z Sin[x +y], Times[z, Sin[Plus[x, y]]]}
这里,大括号中的第二个表达式与第一个是等价的,但是它明确地显示了前面描述的结构。 任何函数也都可以用文字形式给出。例如:
In[X]:= {Plus[1, 2, 3, 4], Times[1, 2, 3, 4]}
Out[X]:= {10, 24}
1.4 所有的普通表达式都是一个树(tree),TreeForm 命令
普通表达式是一个树这一事实可以通过内置的 TreeForm 命令非常清楚地看出来:
In[X]:= TreeForm[z * Sin[x +y]]
既然有树的结构,我们就可以对一个表达式的子树加注指标或进行访问。在下面的例子中,<expr> 是 Times (乘法命令):
In[X]:= a = z * Sin[x +y];
In[X]:= FullForm[a]
Out[X]:= Times[z, Sin[Plus[x, y]]]
1.5 表达式的头和 Head 命令
一般来说,一个表达式在方括号前面都有一个名称,这个名称称为表达式的头。Mathematica 有一个内置函数叫 Head,它可以用来得到任何表达式的头。例如:
In[X]:= Head[a]
Out[X]:= Times
一个表达式的头本身可以是一个原子,也可以是一个普通表达式。例如:
In[X]:= Clear[b, f, g, h, x];
In[X]:= b = f[g][h][x];
In[X]:= Head[b]
Out[X]:= f[g][h]
In[X]:= Head[f[g][h]]
Out[X]:= f[g]
In[X]:= Head[f[g]]
Out[X]:= f
每一个表达式都有一个头,即使是原子也一样。原子的头有 String,Symbol,Integer,Real,Rational 以及 Complex。例如:
In[X]:= {Head[f], Head[2], Head[Pi],Head[3.14], Head["abc"], Head[2 / 3], Head[1 +I]}
Out[X]:= {Symbol, Integer, Symbol, Real, String, Rational, Complex}
1.6 通过指标来访问表达式的各个部分
通过应用指标({Part 命令}),我们也可以访问一个表达式内部的组成部分(即方括号里面的元素)。这一点可以从下面这个例子中清楚地看到:
In[X]:= {a0, a1, a2, a2, 0, a2, 1, a2, 1, 0, a2, 1, 1, a2, 1, 2}
Out[X]:= {Times, z, Sin[x +y], Sin, x +y, Plus, x, y}
(双重方括号是Part 命令的简写形式:
In[X]:= a2, 1
Out[X]:= x +y
In[X]:= Part[a,{2, 1}]
Out[X]:= x +y
在这个例子中,我们实际上是把一个表达式解构成它的各个组成部分。对照前面由 TreeForm 得到的数图,可以看出我们的步骤是从树干出发,沿着树枝,最后到达叶子。我们也看到所有最后一个元素为零的地址(即指标序列)所对应的都是子表达式的 Heads,这是一个约定。原则上讲,任何一个表达式都可以按此方法进行解构,更重要的是,我们可以对其子表达式进行变更。
1.7 表达式的层级和 Level 命令
利用内置函数 Level 命令,我们还可以进入离树干某一距离上的所有枝(或子表达式)。考虑下面的例子:
In[X]:= Clear@aD;
In[X]:= a = z * Sin[x +y] +z1 * Cos[x1 +y1]
Out[X]:= z1 Cos[x1 +y1] +z Sin[x +y]
它的完全形式是:
In[X]:= FullForm[a]
Out[X]:= Plus[Times[z1, Cos[Plus[x1, y1]]], Times[z, Sin[Plus[x, y]]]]
它的树图是:
In[X]:= TreeForm[a]
那么该树的层级依次为:
In[X]:= Level[a, {0}]
In[X]:= Level[a, {1}]
In[X]:= Level[a, {2}]
In[X]:= Level[a, {3}]
In[X]:= Level[a, {4}]
Out[X]:= {z1 Cos[x1 +y1] +z Sin[x +y]}
Out[X]:= {z1 Cos[x1 +y1], z Sin[x +y]}
Out[X]:= {z1, Cos[x1 +y1] z, Sin[x +y]}
Out[X]:= {x1 +y1, x +y}
Out[X]:= {x1, y1, x, y}
Level[a, {n}]给出了 a 离树干相距 n 层的所有枝或叶。如果想要得到具有 n 层亚枝或叶的所有的枝,我们则要在层级命令中设定负数层级:Level[a, {-n}]。 如:
In[X]:= Level[a, {-1}]
In[X]:= Level[a, {-2}]
In[X]:= Level[a, {-3}]
In[X]:= Level[a, {-4}]
Out[X]:= {z1, x1, y1, z, x, y}
Out[X]:= {x1 +y1, x +y}
Out[X]:= {Cos[x1 +y1], Sin[x +y]}
Out[X]:= {z1 Cos[x1 +y1], z Sin[x +y]}
注意,一般情况下,负数层级是不可能用正数层级来得到的。它们给出的是不同类型的信息。我们这里所描述的,在 Mathematica 中称为标准层级设定(Standard Level Specification)。有许多内置函数将标准层级设定作为一个(往往是可选的)变量。