Компьютерные вычисления
(введение в систему Mathematica)

Лекция 1

Простейшие операции, переменные и функции

Для выполнения команды нажмите ⇧+⌤ одновременно

(2^2)

Sin[(π/4)]

Чтобы набрать показатель степени, я нажал Ctrl^. Чтобы получить букву π, я набрал EscpEsc. Вообще, для набора  спецсимволов и форм служит меню шаблонов (Palettes в меню Математики), из которого нужный можно выбрать при помощи мышки. Это меню дает всплывающие подсказки, как все это можно набирать на клавиатуре, не обращаясь к меню.

∫((\[DifferentialD]x)/(Sin[x]+1))

(∑_(n=1)^∞)(1/(n^2))

N[π,300]

Строки ввода можно редактировать и отправлять на выполнение повторно. В одной ячейке ввода может стоять последовательность из нескольких операций. Если в конце строки стоит “;”, то промежуточный результат не печатается. Если отсутствует, то - печатается.

b=(((a+3))^2);
b+5
a=2;
b+5

На результат последней выполненной команды можно ссылаться, используя “%”

((%)^2)

Помимо непосредственых присваиваний “=”, бывают т.н. отложенные “:=”. Сравните:

a=1;
b=a+1;
b
a=20;
b

a=1;
b:=a+1;
b
a=20;
b

Отложенные присваивания следует использовать, в частности, для определения функций (с аргументами). Аргументы всегда заключаются в квадратные скобки (а круглые используются только для группировки выражений).

f[x_,y_]:=(((x+y))^2);
f[(z^2),1]

Знак подчеркивания означает, что в качестве x и y могут стоять любые выражения. Без него определение применялось бы только к самим символам x и y, и только:

g[x]:=(((x+1))^2);
g[6]
g[x]

Вместе со схемой вида x_ можно использовать всевозможные дополнительные условия - после “/;” . В тех случаях, когда эти условия неприменимы, выражение остается непреобразованным.

Clear[f];
f[n_Integer]/;n>0:=n f[n-1];
f[n_Integer]/;n<0:=∞;
f[0]=1;
(1/(f[5]))
(1/(f[-5]))
(1/(f[c]))

Команда Clear здесь использована для удаления всех предыдущих определений, связанных с символом f. Текущие определения можно проверить при помощи “?”

?f

Mathematica (М.) рассматривает переданное ей выражение, выискивает в нем те куски, для которых к этому времени имеются определения, и применяет. При этом более частные определения применяются, по возможности, раньше, чем более общие. Так, встретив f[-5], М. сначала проверяет, является ли аргумент -5  числом 0. Так как не является, первое определение не применимо. Далее проверяет, явяется ли аргумент целым положительным. Так как не является, то второе определение тоже неприменимо. Третье определение, наконец, применимо, и получаем результат (1/∞)=0.

Помимо стандартного обозначения для действия функций с использованием квадратных скобок, можно применять также инфиксную и постфиксную форму: с помощью символов “@” и “//” соответственно.

f[5]
f@5
5//f

Кроме именованных, часто используются т.н. чистые, т.е. неименованные, функции. Чистая функция обозначается символом “&”, а ее аргумент - символом “#”. Сравните:

g[x_]:=(((x+1))^2);
g[q]
(((#+1))^2)&[q]

Постфиксное обозначение, в частности, в сочетании с чистыми функциями, полезно использовать для форматирования окончательного ответа в проведенных вычислениях

b=((((c^3)+7+x))^2);
(b^2)  //Expand
(b^2)  //Collect[#,x]&

Списки и другие выражения

Векторы и матрицы в Математике реализованы как списки и вложенные списки. Список состоит в последовательности его элементов, заключенной в фигурные скобки. При вводе матрицу можно задавать в виде таблицы, но интерпретируется она все равно как вложенный список.

{1,3,a,x+5}
(({{1, z, u+1}, {0, 1, 0}, {(d^2), 0, 1}}))

Список - это простейший пример выражения. В действительности, в Математике ВСЁ является выражением. Выражение состоит из заголовка и последовательности его элементов. Каждый элемент, в свою очередь, может быть или атомом (число, переменная, ...) или снова выражением со своими заголовком, последовательностью элементов, и  т.д. Получается структура в виде дерева. Например, операции +, *, возведения в степень имеют заголовки Plus, Times, Power, а список имеет заголовок List.

Plus[c,4]
Times[c,4]
Power[c,4]
List[c,4]

Взглянуть на фактическую структуру выражения можно с помощью команд FullForm или TreeForm

{x,(((2y+1))^2)}//FullForm

{x,(((2y+1))^2)}//TreeForm

Аналогичным образом в виде структуры составного выражения интерпретируются и все остальные сокращения, которые нам встречались до сих пор, такие как “&”, “>”, “_”, и т.п., и которые еще будут встречаться, включая даже такие, как “=”, “:=”, и даже “;”! В следующем примере мы используем команду Hold, предотвращающую выполнение действия, чтобы показать структуру выполняемого выражения.

Hold[
g[x_]:=x+1;
g[2]             ]//TreeForm

К элементу списка (или матрицы) можно обращаться при помощи двойных квадратных скобок. Вот несколько абсолютно эквивалентных (с точки зрения интерпретации) способов

M=(({{1, z, u+1}, {0, 1, 0}, {(d^2), 0, 1}}))
M[[1,3]]
M[[1,3]]
(Subscript[M, [[1,3]]])
M[[1]][[3]]

Выражение M[[1,3]] означает, что мы берем 1-й элемент в выражении M, а в нем, в свою очередь, выбираем 3-й элемент. Но аналогичные действия применимы к любым выражениям, не только составленным из списков!

b
b[[1,2]]
b[[1,2,2]]

К заголовку выражения можно обратиться или с помощью команды Head, или как к элементу с номером 0

b
Head[b]
b[[0]]

Замены

Замена является одной из наиболее эффективных операций в Математике. Она состоит в замещении в данном выражении частей, удовлетворяющих определенной схеме, на новые.

b
b/.x->y
b/.Plus->Times
b/.Power->pow

В качестве образца для замены можно использовать схемы с использованием _ и условия, аналогично отложенным присвоениям, и в этом случае вместо обычного оператора замены “->” следует применять оператор отложенной замены, “:>”, примерно по тем же принципам.
Вот как можно, например, из геометрической прогрессии изготовить экспоненту!

(1/(1-x s))+((O[s])^8)
%/.(x^(k_)):>((x^k)/(k!))
Log[%]

Символ ((O[s])^8) используется здесь для разложения в степенной ряд. А теперь все вместе:

(1/(1-x s))+((O[s])^8)/.(x^(k_.)):>((y^k)/(k!)) //Log

Здесь мы заодно поменяли переменную x на y. Точка в схеме “k_.” означает “значение по умолчанию”, которое для возведения в степень равно 1. Без нее к линейному члену x=(x^1) замена бы не применилась . Эквивалентным (и более явным) образом значение по умолчанию можно было бы задать схемой “k_:1”
Дополнительная переменная s в качестве параметра разложения введена не зря. Следующий вариант не работает:

(1/(1-x))+((O[x])^8)
%/.(x^(k_)):>((x^k)/(k!))

Причина заключается в специфике представления степенных рядов в Математике

(1/(1-2x))+((O[x])^5)
%//FullForm

Мы видим, что ряд задается переменной разложения, точкой разложения, списком его членов и заданием их степеней. Ни сами мономы (x^k), ни, кстати, буква O нигде во внутреннем представлении не фигурируют. Они используются только для внешнего отображения ряда.

В следующем примере мы используем замены, чтобы произвести список разбиений заданного числа n

n=6;
(∏_(i=1)^n)(1/(1-(Subscript[x, i])(s^i)))//
SeriesCoefficient[#,{s,0,n}]&//
#/.Plus->List&//
#//.{(Subscript[x, i__])(Subscript[x, j__]):>(Subscript[x, i,j]),(Subscript[x, i__]^(k_)):>(Subscript[x, i,i])(Subscript[x, i]^(k-2))}&//
#/.(Subscript[x, k__]):>Sort[{k},Greater]&//
Sort

Теперь подробнее. 
1) У произведения геометрических прогрессий выделяем однородный член степени n. Это сумма всех мономов данной квазиоднородной степени

(∏_(i=1)^n)(1/(1-(Subscript[x, i])(s^i)))//
SeriesCoefficient[#,{s,0,n}]&

2) Превращаем сумму в список

%/.Plus->List

3) Заменяем произведение нескольких переменных с индексами на одну переменную с мультииндексом. Оператор “//.” означает “повторять замену многократно до тех пор, пока она применима”. Список правил замены состоит из двух штук, поэтому он заключен в фигурные скобки. Схема “i__” (с двумя подчеркиваниями) обозначает “последовательность одного или нескольких элементов”.

%//.{(Subscript[x, i__])(Subscript[x, j__]):>(Subscript[x, i,j]),(Subscript[x, i__]^(k_)):>(Subscript[x, i,i])(Subscript[x, i]^(k-2))}

Осталось только превратить полученные мультииндексы, после сортировки, в разбиения

%/.(Subscript[x, k__]):>Sort[{k},Greater]//
Sort

Конечно, той же цели можно было достичь несколько более коротким путем...

IntegerPartitions[n]

Лекция 2

Точность вычислений

Помимо ячеек ввода и вывода, полезно использовать ячейки заголовков разных уровней, стиль которых можно менять через меню. Также имеются текстовые ячейки, куда можно вставлять комментарии. Кроме того, комментарии можно вставлять прямо в тело программы при помощи парных “(*” и “*)”.

(* Точность вычислений в Математике не ограничена *)
p=N[π,200]
(p^2)

(* ни в вещественных числах, ни в целых *)
100!

С одной стороны, это полезно: никогда не наступает переполнение. С другой стороны, это вызывает избыточные затраты ресурсов. У многих команд предусмотрены опции вычисления по модулю некоторого натурального числа. Во многих случаях этого вполне достаточно для приложений, при этом существенно повышается эффективность.

PolynomialRemainder[(x^1000),(x^3)-x+1,x]

PolynomialRemainder[(x^1000),(x^3)-x+1,x,Modulus->11]

Программирование

Стандартный набор приемов программирования, таких, как ветвления (If, Which, Switch) и циклы (Do, While, For), имеется и в Математике. Точный синтаксис можно посмотреть в Help’е, мы обратим внимание только на некоторые особенности. 
Все эти команды, как и всё в Математике, состоят из заголовка и списка аргументов в квадратных скобках

Z[a_]:=If[a==0,"ноль","не ноль"];
Z[0]
Z[1]
Z[x]

Обратите внимание, что если результатом проверки условия не является ни True, ни Faulse, то команда If никак не интерпретируется. Можно добавить 4-й аргумент в команду If для таких случаев, или использовать “===” вместо “==”

x==0
x===0

Теперь рассмотрим 4 идентичные по синтаксису команды Do, Sum, Product и Table

Clear[a];
tot=0;
Do[tot=tot+(Subscript[a, i]),{i,5}]
tot
Sum[(Subscript[a, i]),{i,5}]
Product[(Subscript[a, i]),{i,5}]
Table[(Subscript[a, i]),{i,5}]

Они и делают одно и то же: вычисляют по очереди значения выражения для последовательных значений итератора. Только подставляют разные заголовки для полученного списка значений: Plus (для Sum), Times (для Product), List (для Table) или никакого заглововка для Do. 
Обратим внимание на две особенности. Во-первых, у Sum и Product имеется приятный 2D вариант для ввода

(∑_(i=1)^5)(Subscript[a, i])

Во-вторых, в качестве итератора можно использовать список. Это часто бывает очень удобно.

list={1,q,Sin};
tot=0;
Do[tot=tot+(Subscript[a, i]),{i,list}]
tot
(∑_i^list)(Subscript[a, i])

Переменные, используемые в качестве итератора, являются “локальными” и не меняют  значений этих переменных, присвоенных вне цикла. Такие локальные переменные можно организовать явно, например, одним из следующих способов.

tot=0;
Block[{tot=5},tot]
Module[{tot=6},tot]
With[{tot=7},tot]
tot

Как видите, эффект во всех трех случаях одинаков. Отличие этих команд в их реализации. Я рекомендую использовать Block, а если любопытно узнать подробности, читайте Help.

Вот пример использования While для нахождения разложения Тейлора функции, заданной неявно. Обратите внимание, что у While два аргумента (условие остановки и тело цикла), но каждый аргумент может содержать последовательность из нескольких команд, разделенных точкой с запятой. Это - проявление общего принципа в Математике, хотя и несколько непривычного, что

запятая важнее точки с запятой

А именно, запятая разделяет аргументы функций, а точка с запятой собирает последовательность выполняемых команд внутри одного аргумента.

Clear[f,y]
f[x_,y_]:=Sin[ x y]+(x^3)-(y^2);
y==f[x,y]
y=Block[{y=0,k=0,ynew,z,eq,sol},
While[k=k+1;
k≤10,
ynew=y+a (x^k);
z=ynew-f[x,ynew];
eq=SeriesCoefficient[z,{x,0,k}]==0;
sol=Solve[eq];
y=ynew/.sol[[1]]];
y+((O[x])^11)]
(* проверка *)
f[x,y]-y

Такое большое количество вспомогательных переменных введено для того, чтобы продемонстрировать использование “;”. Конкретно в этом случае без них можно было обойтись:

Block[{y=0},
Do[
y=y+a (x^k);
y-f[x,y]//
SeriesCoefficient[#,{x,0,k}]&//
Solve[#==0]&//
(y=y/.#[[1]])&,
{k,10}];
y+((O[x])^11)]

В Математике имеются полезные разновидности циклов, не имеющие аналогов в популярных языках программирования. Это циклы Nest, NestWhile и FixedPoint. Их интересная особенность заключается в том, что в качестве одного из аргументов этих команд используется функция.

Nest[g,x,5]

Nest применяет функцию к начальному значению заданное количество раз, NestWhile применяет до тех пор, пока выполняется условие продолжения цикла, а FixedPoint - до тех пор, пока применение функции меняет результат.
Вот как с помощью этих циклов можно решить задачу разложения в ряд Тейлора функции, заданной неявно

Clear[y];
y==f[x,y]
Nest[f[x,#]&,O[x],10]
NestWhile[f[x,#]&,O[x],#[[5]]<11&]
FixedPoint[(f[x,#]+((O[x])^11))&,0]

Манипуляции со списками

Несмотря на наличие всех стандартных атрибутов программирования, таких, как циклы различных видов, стиль программирования на Математике имеет свою специфику. Она состоит, прежде всего, в большом разнообразии средств манипуляции со списками. Списки можно объединять, резать, упорядочивать по-разному, добавлять и выкидывать элементы и т.д. Вот как можно, например, выбрать все элементы списка, начиная с 3-го

list=Table[(Subscript[a, k]),{k,7}]
Drop[list,2]
Take[list,-5]
list[[3;;]]

Все другие средства легко находятся в Help’е в разделе “см. также” к любой из уже знакомых команд.
Обратим внимение только еще на существовании важных команд Map ( /@) и Apply (@@). Первая применяет функцию последовательно к каждому элементу списка. Вторая применяет функцию ко всему списку целиком (т.е. использует элементы списка как аргументы функции).

g/@{a,b,c}
g@@{a,b,c}

Пример. Перечисление плоских деревьев

“Древовидная” структура данных в Математике хорошо приспособлена для перечисления плоских деревьев. Вот пример плоского дерева. Вершины обозначены символом “v”.

t=v[v[v[],v[v[v[]],v[v[]]],v[]]]
TreeForm[t,VertexLabeling->False]

Чтобы не преобразовывать каждый раз к подходящему виду выводимые на печать деревья, я использую команду Format. Она никак не влияет на результат вычислений, только лишь автоматически меняет в дальнейшем  внешний вид выводимых выражений

Format[t_v]:=TreeForm[t,
VertexRenderingFunction->({Blue,Point[#]}&),
ImageSize->Scaled[0.08]]
t

В действительности, перечисляемые деревья являются корневыми. Поэтому стоит при печати корень выделять каким-нибудь образом

Format[t_v]:=TreeForm[u@@t,
VertexRenderingFunction->(
If[#2===u,Text["☺",#],{Blue,Point[#]}]&),
ImageSize->Scaled[0.08]]
t

Теперь займемся перечислением деревьев. Пусть tlist[k] производит список деревьев с k ребрами.

tlist[0]=v[]
tlist[1]=v[v[]]

Введем операцию “склейки” двух поддеревьев по верхнему правому ребру. Всякое дерево с n ребрами получается склейкой двух поддеревьев, суммарное число ребер которых равно n-1

a_v·b_v:=Append[a,b];
tlist[k_]:=(∑_(j=0)^(k-1))tlist[j]·tlist[k-1-j];
tlist[2]
tlist[3]

Чтобы это работало, достаточно еще добавить свойство дистрибутивнотси для введенной операции склейки

a___·b_Plus·c___:=a·#·c&/@b;
a___·(k_ b_)·c___/;FreeQ[k,v]:=k a·b·c;
tlist[3]
tlist[4]

Назовем весом вершины переменную (Subscript[x, k]), где k - валентность вершины, и весом всего дерева - произведение весов его внутренних вершин (не включая корень). Будем перечислять деревья вместе с весами. Для этого добавим соответствующий коэффициент в определение операции склейки. Вот окончательный код (состоящий всего из 5 строчек!)

Clear[tlist];
a_v·b_v:=(Subscript[x, Length[b]+1])Append[a,b];
a___·b_Plus·c___:=a·#·c&/@b;
a___·(k_ b_)·c___/;FreeQ[k,v]:=k a·b·c;
tlist[0]=v[];
tlist[k_]:=(∑_(j=0)^(k-1))tlist[j]·tlist[k-1-j];

Collect[tlist[5],(Subscript[x, _])]

Теперь можно заняться экспериментами. Производящая функция T для чисел плоских (некорневых) деревьев удовлетворяет ряду интересных тождеств, которые мы сейчас проверим экспериментально. Для некорневого дерева выбрать корень и выходящую из него начальную ветвь можно 2n способами (по числу полуребер). Отсюда коэффициент (1/(2n)) в производящей функции. Кроме того, нужно не забыть вес корня.

ord=6;
T=(∑_(n=1)^ord)(1/(2n))(tlist[n]/.t_v:>(Subscript[x, Length[t]]))(s^n)+((O[s])^(ord+1))

Производная по (Subscript[x, k]) дает перечисление уже корневых деревьев с корнем валентности k

Y=(∂_(Subscript[x, 1]))T

Вот тождество, иллюстрирующее операцию склейки дерева из двух поддеревьев

(∑_(k=1)^ord)k (Subscript[x, k])(∂_(Subscript[x, k]))T-(s^(-1))(Y^2)

Следующие равенства называются “бездисперсионные уравнения КдФ”. Они означают, что, стирая корень валентности k, мы получаем k деревьев, каждое из которых имеет корень валентности 1.

Table[
k(∂_(Subscript[x, k]))T-(Y^k),
{k,5}]

Если задано дерево с корнем в листе, то, удалив этот корень вместе с выходящим из него ребром, получаем дерево с корнем произвольной валентности. Это приводит к неявному уравнению на функцию Y (т.н. “уравнение струны”)

Y-s(∑_(k=0)^ord)(Subscript[x, k+1])(Y^k)
(s^(-1))Y==(Subscript[x, 1])+(Subscript[x, 2])Y+(Subscript[x, 3])(Y^2)+(Subscript[x, 4])(Y^3)+(Subscript[x, 5])(Y^4)+(Subscript[x, 6])(Y^5)+((O[s])^6)//ExpandAll

Наконец, имеется простая замкнутая факториальная формула для коэффициентов ряда T

(Subscript[x, μ_List]):=(∏_i^μ)(Subscript[x, i])/.(Subscript[x, k_]^(i_)):>((Subscript[x, k]^i)/(i!));
T== (∑_(n=1)^ord)(n-1)!(s^n)(∑_μ^(IntegerPartitions[2n,{n+1}]))(Subscript[x, μ])+((O[s])^(ord+1))//ExpandAll

Лекция 3

Ввод и вывод

Вывести на печать результаты вычислений можно не только опустив “;” в конце строки. Можно еще пользоваться командой Print. Она печатает подряд без пробелов все свои аргументы. Чтобы разделять выводимые поля, их следует перемежать текстовыми строками (заключенными в двойные кавычки). В текстовые строки можно, в свою очередь, вставлять переменные при помощи команды StringForm.

Do[Print["n = ",n,StringForm[",  (2^(`1`)) = `2`",n,(2^n)]],{n,5}];

Представление структур в Математике не всегда самое наглядное. Чтобы не преобразовывать при печати ответ всякий раз к требуемому виду, предусмотрена команда Format. Она не меняет значения выражения, а только лишь выводит выражение в требуемом виде. Предположим, что у нас имеется степенной ряд, коэффициенты которого нумеруются разбиениями (диаграммами Юнга).

F=(∑_(n=1)^5)(∑_λ^(IntegerPartitions[n]))Y[λ](∏_i^λ)(Subscript[x, i])

Вот как можно сделать внешний вид разбиений более наглядным.

Format[Y[λ_List]]:=(Subscript[Y, Row[λ]])
F

Также для более приятного ввода и вывода в Математике имеется большое количество функций с сокращенными инфиксными и префиксными обозначениями, у которых никакие значения изначально никак не заданы. Это скобки разных видов, бинарные операции вида ·,∘,⋀,⊗,⊕ и многие другие. Их можно использовать, чтобы определять свои собственные операции (подобно тому, как мы на прошлой лекции определяли операцию склейки деревьев). Но нужно понимать, что все равно за каждым сокращенным обозначением стоит соответствующая функция со своим заголовком и т.п.

a⋀b//FullForm

Наконец, если и этого не хватает, в Математике предусмотрена возможность вводить свои собственные обозначения. Для этого нужно подключить дополнительно пакет Notation

Needs["Notation`"]

Пример собственного обозначения для предела

Notation[(NotationTemplateTag[(lim_(x_-> a_))f_]) ⟺ (NotationTemplateTag[Limit[f_,x_->a_]])]

(lim_(x->0))(Sin[Tan[x]]-Tan[Sin[x]])/(ArcSin[ArcTan[x]]-ArcTan[ArcSin[x]])

А вот пример собственной операции возведения в квадрат

Notation[(NotationTemplateTag[■ x_]) ⟺ (NotationTemplateTag[((x_)^2)])]

■ 5
(((a+b))^3)//Expand

Отладка и повышение быстродействия

Если программа “зависла” или слишком долго считает, можно прервать вычисления, набрав одновременно “Alt,” или “Alt.” В самом крайнем случае можно в меню выбрать Evaluation->Quit Kernel. Чтобы к этим кардинальным средствам прибегать как можно реже, полезно контролировать ход вычислений. Простейший прием - вставлять в код отладочные печати. Более хитрый состоит в использовании динамических выражений

Dynamic[n]

Значение такого выражения обновляется автоматически в реальном времени по мере его изменения

n=0
Do[++n;Pause[0.2],{20}]

Приведу еще несколько приемов, повышающих эффективность работы Математики (это лишь те приемы, которые встречались в моей практике. В действительности их наверняка гораздо больше).
В комбинаторике часто приходится иметь дело с матрицами больших размеров, большинство компонент которых равны нулю. Матрицы такого вида лучше всего релизовывать при помощи команды SparseArray. В дальнейшем с ними можно обращаться как с обычными матрицами - складывать, перемножать, искать собственные числа, собственные векотры, вычислять определитель и т.п.

M=SparseArray[{{i_,i_}->1,{2,5}->5},{6,6}]

M//MatrixForm
M.M//MatrixForm

Если результаты сложных и объемных вычислений хранятся в виде списка замен большого размера, то для повышения эффективности обращения к такому списку можно применить к нему команду Dispatch. Эта команда упаковывает список замен, то есть приводит к виду, удобному для эффективного обращения к нему ядра Математики.

repl=Table[(Subscript[x, i])->(i^2),{i,10000}];

Short[repl]

Timing[(∑_(i=1)^10000)(Subscript[x, i])/.repl]

disp=Dispatch[repl];

Timing[(∑_(i=1)^10000)(Subscript[x, i])/.disp]

Кстати, мы использовали здесь команду Timing - еще одно средство контроля эффективности выполняемых расчетов.

При рекурсивном определении функций можно запоминать уже вычисленные значения, чтобы не заставлять рекурсию всякий раз работать заново. Это повышает скорость вычислений на несколько порядков. Вот определение многочленов Чебышева

Clear[T];
T/:(Subscript[T, 1])=x;
T/:(Subscript[T, n_]):=x (Subscript[T, n-1])-((1-(x^2))/(n-1))(∂_x)(Subscript[T, n-1])//Expand;
Timing[(Subscript[T, 20])]

А вот как это считается, если сохранять промежуточные вычисления

Clear[T];
T/:(Subscript[T, 1])=x;
T/:(Subscript[T, n_]):=T/:(Subscript[T, n])=x (Subscript[T, n-1])-((1-(x^2))/(n-1))(∂_x)(Subscript[T, n-1])//Expand;
Timing[(Subscript[T, 20])]

Здесь “/:” используется для того, чтобы определение (Subscript[T, n]) было связано именно с символом T, а не с заголовком Subscript, и чтобы командой Clear[T] можно было очищать определения в процессе отладки.

Если проделанные в Математике вычисления хочется сохранить до следующей сессии работы с программой, вот как это можно сделать

SetDirectory[NotebookDirectory[]]

DumpSave["tmp.mx",{T}]

Без указания директории будет использоваться системная (которая, ко всему прочему, скорее всего, защищена от записи). Вместо DumpSave можно использовать Save. Отличие в том, что Save сохраняет в текстовом виде, а DumpSave - в бинарном внутреннем формате Математики, что, конечно же, эффективнее. Теперь можно выйти из программы или остановить ядро, а потом загрузить сохраненный файл

SetDirectory[NotebookDirectory[]];
<<tmp.mx

Все определения, связанные с символом T (как вычисленные многочлены Чебышева, так и рекурсивная формула для них) восстановились полностью.

?T

Графика

Возможности работы с графикой в Математике поистине безграничны. Все аспекты описать невозможно. Я приведу лишь несколько начальных простых примеров, совершенствовать и усложнять которые слушатели смогут самостоятельно.

Простейшая графическая команда - Plot, для рисования графика функции

Plot[x Sin[1/x],{x,-1.2,1.2}]

Сделаем несколько усовершенствований. 1) добавим еще две функции x и -x для наглядности; 2) добавим в конце команды опции PlotRange для задания границ выводимых значений и AspectRatio для согласования масштабов по осям; 3) добавим параметр и простой манипулятор для изменения его значений

Manipulate[
Plot[{x Sin[a/x],x,-x},{x,-1.2,1.2},
PlotRange->{-1.2,1.2},AspectRatio->1],
{a,0,10}]

А вот знакомые нам многочлены Чебышева. У них все критические точки вещественны, половина из них имеет критическое значение 1, а остальные -1. Для команыд Plot “лучше”, если выводимая функция вычислена заранее, а не вычисляется в процессе построения графика.

Manipulate[y=(Subscript[T, n]);
Plot[{y,1,-1},{x,-1.2,1.2},
PlotRange->{-1.2,1.2},AspectRatio->1],
{n,1,30,1}]

Здесь я использовал команду ParametricPlot, чтобы нарисовать циклоиду.

r[ϕ_]:={Cos[ϕ],Sin[ϕ]};
n=4;
ParametricPlot[n r[ϕ]+r[-n ϕ],{ϕ,0,2π}]

Чтобы нарисовать что-нибудь вручную, нужно использовать окружение Graphics. Графика состоит из последовательности “Примитив” и “Директив”, сгрупированных в фигурных скобках. Примитивы - это “что мы рисуем” - точка, круг, линия, прямоугольник и т.д. Дирекивы - это “как мы рисуем” - цвет, заливка, толщина линий и т.д. Вот простейшая графика, состоящая из двух окружностей и точки.

Manipulate[
Graphics[{Circle[{0,0},n+1],Circle[n r[ψ],1],
{PointSize[Large],Red,Point[n r[ψ]+r[-n ψ]]}}],
{ψ,-2π,2π},{n,2,10,1}]

Построенная выше циклоида - это след точки на маленькой окружности, катящейся по большой. А как наложить два изображения друг на друга? Это можно сделать с помощью команды Show. Кроме того, в Show можно добавить графические опции, которые будут применены ко всем использованным внутри графическим командам

Manipulate[
Show[
ParametricPlot[n r[ϕ]+r[-n ϕ],{ϕ,0,ψ}],
Graphics[{Circle[{0,0},n+1],Circle[n r[ψ],1],
{PointSize[Large],Red,Point[n r[ψ]+r[-n ψ]]}
}],
Axes->False,PlotRange->(n+1){{-1,1},{-1,1}}],
{ψ,-2π,2π},{n,2,10,1}]

В следующем примере добавим немного интерактивности. Пусть имеется матрица, состоящая из нулей и единиц.

n=10;
A=RandomInteger[1,{n,n}];
MatrixForm[A]

Изобразим эту матрицу в виде шахматной доски, поля которой раскрашены в два цвета в зависимости от значений компонент матрицы. Заметим, что координаты в графике имеют привычное обозначение (первая слева направо, вторая снизу вверх). Для согласованности с традиционной нумерацией строк и столбцов матрицы координаты придется повернуть на 90 градусов.

Graphics[Table[{EdgeForm[Thin],If[(Subscript[A, [[n+1-j,i]]])==0,Cyan,Pink],Rectangle[{i,j}]},{i,n},{j,n}],ImageSize->150] //
Row[{#,"  A = ",MatrixForm[A]}]&

А теперь сделаем так, чтобы по клику мышки цвет ячейки (и соответствующая компонента матрицы) менялись. Для этого используем команду ClickPane. Ее первый аргумент - сама графика (к которой применен Dynamic), а второй аргумент - функция, которая выполняется при нажатии на мышку. Аргументом функции служат координаты мышки. В нашем случае мы округляем координаты до целых и меняем соответствующую компоненту матрицы.

ClickPane[Dynamic[
{Graphics[Table[{EdgeForm[Thin],If[(Subscript[A, [[n+1-j,i]]])==0,Cyan,Pink],Rectangle[{i,j}]},{i,n},{j,n}],ImageSize->150],"  A = ",MatrixForm[A]}//Row],
({i,j}=Round[#-(1/2)];If[1≤i≤n&&1≤j≤n,(Subscript[A, [[n+1-j,i]]])=1-(Subscript[A, [[n+1-j,i]]])])&]

Помимо двумерной графики, в Математике реализована трехмерная. Такие команды как Plot3D и ParametricPlot3D рисуют поверхности, с помощью Graphics3d можно создавать трехмерные объекты вручную - многогранники, сферы, и т.п. Трехмерные изображения можно вращать при помощи мышки.

Clear[pp];
pp[a_]:=Join@@Table[{Cos[2π (i+a z)/3],Sin[2π (i+a z)/3],z},
{i,0,2},{z,{-1,1}}]//Append[#,#[[1]]]&;
pp[0]

Graphics3D[{Tube[pp[1.1],0.1]},Boxed->False]

Manipulate[
Graphics3D[{Tube[pp[a],.1]},Boxed->False],{{a,1.1},0,3}]

Заключение

Мы рассмотрели лишь малую часть возможностей, предоставляемых Математикой. Больше можно узнать, читая Help’ы и рассматривая всевозможные примеры, собранные в проекте Demonstrations на сайте Wolfram (сылка имеется в пункте меню Help).