Документ взят из кэша поисковой машины. Адрес оригинального документа : http://tex.bog.msu.ru/numtask/maxhelpb.ps
Дата изменения: Sat Feb 7 13:46:35 2004
Дата индексирования: Mon Oct 1 19:41:21 2012
Кодировка: IBM-866
Московский Государственный Университет
физический факультет
В.А.Ильина, П.К.Силаев
Краткое руководство по работе с системой
аналитических вычислений MAXIMA
Москва 2004

Общие сведения о MAXIM'е
MAXIMA | это одна из первых систем аналитических вычислений. К
настоящему времени существует несколько таких систем | это, прежде все-
го, язык аналитических вычислений REDUCE; системы аналитических вычи-
слений MAXIMA, Mathematica и Maple; программы Derive и Eureca (которые
скорее являются игрушками, чем рабочим инструментом) и другие.
Имеет смысл различать язык аналитических вычислений, т.е. минималь-
ный набор синтаксических конструкций, который расширяется библиотечны-
ми функциями (типичный пример | REDUCE), и систему аналитических вы-
числений, в которой изначально определено очень много функций, причем эти
функции как правило избыточны. Система аналитических вычислений стара-
ется "уметь все" и на каждую задачу в ней найдется своя функция (типичные
примеры | MAXIMA и Mathematica).
При выборе системы аналитических вычислений следует учитывать:
Во-первых, насколько удачен замысел системы (естественность синтакси-
са, простота реализации тех или иных действий, автоматическое или прину-
дительное упрощение, полнота набора функций и т.п.).
Во-вторых, насколько удачна ее конкретная реализация (так, "старый"
REDUCE 3.0 компактен, но крайне ограничен и по памяти и по скорости по
сравнению с более "новым" REDUCE 3.4, а оформление REDUCE 5.0 заметно
лучше, чем у всех предыдущих; Mathematica 1.0 | это программа, которая
почти не работает, Mathematica 2.0 и 2.2 работают, но с "причудами", Math-
ematica 3.0 для LINUX (один из вариантов UNIX) работает в 3 раза быстрее,
чем Mathematica 3.0 для Windows 95, если они инсталлированы на идентичных
компьютерах, и т.д.).
Наконец, в-третьих, легальность ее использования (стоимость REDUCE
| 99$, стоимость Mathematic'и под Solaris (один из вариантов UNIX) | 500$,
GNU MAXIMA 5.2 под UNIX | программа общего пользования, ее можно
найти по адресу "www.gnu.ai.mit.edu").
В настоящее время профессионалами используется главным образом RE-
DUCE и MAXIMA. Что касается Mathematic'и, то основные ее идеи и сина-
таксические конструкции позаимствованы у MAXIM'ы. Значительно улучшен
только аппарат подстановок по шаблону, который в MAXIM'е крайне неудо-
бен. Если бы реализация Mathematic'и была удачна, ею вполне можно было
бы пользоваться. К сожалению, Mathematica изначально развивалась как ком-
мерческая программа и была ориентирована на массового пользователя. Так
что конструировалась система, которая "умеет все", но делает это "все" не
1

слишком хорошо. Соответственно, при усовершенствовании системы основ-
ное внимание уделялось оформлению, а не оптимизации работы. Реализация
именно аналитических вычислений в Mathematic'е не очень удачна (медленно
и не экономно по памяти), и при большом объеме выкладок они не могут быть
выполнены за конечное время. Кроме того, она вполне может выдавать невер-
ные ответы (в сущности вся история развития той части Mathematic'и, которая
работает с аналитическими выражениями | это исправление многочисленных
ошибок, на которые указывали пользователи). Самый замечательный пример
такого рода | это способ раскрывания скобок (впоследствии исправленный):
(a+b).(c+d) ) a.c+b.c+a.d+b.d
т.е. в общем случае ответ правильный,
(a+b).(a+b) ) a.a+a.b+b.b
а в этом случае неправильный.
MAXIMA написана на диалекте LISP'а, который называется "Common
LISP", и поддерживается проектом GNU. Регулярный способ установки MAX-
IM'ы на UNIX-машине состоит в том, что следует установить GNU'тый ком-
пилятор "C" | gcc (это полезно и само по себе, т.к. компилятор очень хорош),
затем с его помощью собрать GNU Common LISP | gcl, и затем на его базе
собрать собственно MAXIM'у. Исходники всех трех предметов можно найти
по адресу "www.gnu.ai.mit.edu".
MAXIMA при работе с аналитическими выражениями не старается упро-
стить выражение до некоторой канонической формы, если ее об этом не просят
(в отличие, скажем, от REDUCE, который всегда сводит выражение к канони-
ческому виду, и проводит упрощения до тех пор, пока выражение не перестает
меняться. Единственное, на что может повлиять пользователь REDUCE |
это поменять сам канонический вид с помощью флагов). Она делает в точно-
сти то, о чем ее просит пользователь с помощью тех или иных функций или
флаговых переменных. При этом она не проводит упрощения или преобразова-
ния "до конца" (т.е. до тех пор, пока выражение не перестает меняться), так
что часто повторный вызов той же самой фукнции меняет выражение.
Первоначальные сведения о
работе с MAXIM'ой
Идентификаторы в MAXIM'е составляются из 26 латинских букв (она не
различает строчные и прописные буквы), 10 цифр, символа подчеркивания "_",
процента "%". Как правило с "%" начинаются специальные имена, например
"%i" | это мнимая единица, "%pi" | это , а "%e" | основание натурального
логарифма.
2

При реальной работе MAXIMA дублирует ввод и печатает его вперемеж-
ку с выводом, причем все это делается заглавными буквами. Для удобства
чтения мы будем в примерах выделять вывод другим шрифтом и связывать
ввод с выводом стрелочкой. Это позволяет сделать наши примеры абсолютно
непохожими на то, что Вы увидите, непосредственно работая с MAXIM'ой.
Ввод в MAXIM'е завершается одним из двух терминаторов | ";" или "$".
В первом случае результат вычислений печатается, во втором | нет. Ка-
ждый ввод нумеруется с помощью меток "label" | "c1", "c2", "c3", "c4", и
т.д. Каждая из них является переменной, которой присвоено значение, рав-
ное введенной команде. Соответственно, каждый вывод также нумеруется с
помощью меток | "d1", "d2", "d3", "d4", и т.д. Опять-таки, каждая из них
является переменной, которой присвоено значение, равное результату выкла-
док. Существуют метки третьего типа "e1", "e2", и т.д., о которых будет
сказано ниже.
Наличие меток является постоянным источником ошибок, т.к. очень часто
хотелось бы работать со "своей" переменной "c2", "e5" или "d3". Делать это
не рекомендуется, т.к. немедленно окажется, что эта переменная уже равна
чему-нибудь крайне неожиданному.
Переменной "%" по определению присваивается результат последней вы-
кладки.
Основные математические операции в MAXIM'е пишутся обычным обра-
зом | "+", "-", "*", "/"; возведение в степень | это "^" (крышечка), а при-
своение (пожалуй, это довольно неудачная идея) записывается как ":" (двое-
точие). Попытка записать присвоение в виде "=" | постоянная ошибка при
работе с MAXIM'ой.
Переменные могут принимать числовые значения | целые, рациональ-
ные, с плавающей точкой фиксированной (машинной) точности и с плавающей
точкой неограниченной точности:
x:-7;
x:-13/5;
x:77.7777e-5;
x:77.77777777777777777777777777777777b-5;
Кроме того, переменным могут быть присвоены значения в виде аналитиче-
ских выражений
x:a^2+b;
Кроме того, переменным могут быть присвоены значения в виде строковых
переменных
x:"abcdefgh";
и, наконец, логические значения | "true" или "false" ("истина" или "ложь"):
x:false;
3

Знак "::" (два двоеточия подряд) | это присвоение с вычислением левой
части. Значением левой части должен быть объект, которому можно что-либо
присваивать. Так что в результате исполнения
s:2*a+b$
t:b-a$
(s+t)::777$
переменной "a" будет присвоено значение "777". Круглые скобки здесь необ-
ходимы, т.к.
s+t::777$
вызовет сообщение об ошибке.
Знак равенства резервирован для "уравнений" ("equation"). Уравнение
| это левая и правая части равенства, соединенные знаком "=". К уравне-
нию можно что-либо прибавить, из него можно что-либо вычесть, его можно
умножить или поделить на то или иное выражение. Те же операции можно
проделать с двумя уравнениями:
eq1:a=b$
eq1-c; ) a-c = b-c
eq2:x=y$
eq1*eq2; ) a x = b y
Знак ":=" применяется для определения функций, именно, записи
f(x):=x^2+5$
f(a,b):=a^2+3/b$
определяют функции одного и двух аргументов соответственно.
Совершенно аналогично знак "::=" применяется для определения макро-
сов.
f(x)::=x^2+5$
f(a,b)::=a^2+3/b$
Можно считать, что макросы работают так же, как функции. Разницу
между ними мы обсуждать не будем.
Разумеется, определены стандартные математические функции
exp(x) log(x) sqrt(x)
sin(x) cos(x) tan(x) cot(x) csc(x)
sinh(x) cosh(x) tanh(x) coth(x) csch(x)
asin(x) acos(x) atan(x) acot(x) acsc(x)
asinh(x) acosh(x) atanh(x) acoth(x) acsch(x)
atan2(x,y)
При этом MAXIMA знает, что sin( x) = sin(x), cos(0) = 1, log(1) = 0, и
т.д.
Определены операции факториала и двойного факториала:
5!; ) 120
4

6!!; ) 48
5!!; ) 15
 Функция max
перебирает свои аргументы и находит максимальное число
max(33,-22,11); ) 33
 Функция min
перебирает свои аргументы и находит минимальное число
min(33,-22,11,44); ) -22
Чтобы можно было реализовывать вывод из сложных синтаксических кон-
струкций типа блоков или циклов, предусмотрены специальные функции вы-
вода.
 Функция disp
печатает значения своих аргументов
x:77$ y:44$ z:11$
(c2) disp(x,y);
77
44
(d2) done
 Функция display
печатает значения своих аргументов вместе с их именем
(c3) display(x,y);
x=77
y=44
(d3) done
 Функция ldisp
печатает значения своих аргументов вместе с метками "e". Эта функция
"сбивает" нумерацию меток "c" и "d".
(с5) ldisp(x,y,z);
(e5) 77
(e6) 44
(e7) 11
(d7) [e5, e6, e7]
(с8) x;
(d8) 77
5

 Функция ldisplay
печатает значения своих аргументов вместе с их именем и метками "e".
Эта функция также "сбивает" нумерацию меток "c" и "d".
(с5) ldisplay(x,y);
(e5) x=77
(e6) y=44
(d6) [e5, e6]
 Функция print
печатает свои аргументы не каждый в отдельной строке, а в одну строку
(c15) print("x is equal to",77,
"or",88," or ",99);
x is equal to 77 or 88 or 99
(d15) 99
Для иллюстрации приведем коротенький пример, иллюстрирующий ввод
и вывод в MAXIM'е.
(c1) 2+3; ) (d1) 5
(c2) %; ) (d2) 5
(c3) exp(x); ) (d3) %e
x
(c4) (a+b)^2/(c+d); ) (d4) (a+b) 2
c+d
(c5) diff(f(x),x); ) (d5) d
dx
(f(x))
(c6) d3; ) (d6) %e
x
(c7) 3+4$ )
(c8) %; ) (d8) 7
В дальнейшем мы как правило будем опускать метки в примерах.
Как видно из приведенного примера, MAXIMA старается нарисовать свою
выдачу "красиво". Способ рисования определяется несколькими переменными,
перечислим некоторые из них.
 Переменная linel
определяет длину строки, в которую должна вписываться выдача. Из-
начально установлена величина "80". Если желательно получить более узкую
страницу (например, 60 позиций для двухколоночной печати), следует присво-
ить переменной "linel" значение "60":
linel:60; ) 60
6

 Переменная display2d
включает или выключает "двумерное" рисование дробей, степеней, и
т.п. Изначально установлено значение "true". При этом дроби рисуются кра-
сиво, но выдача не может быть использована для ввода в MAXIM'у. Если
установить значение "false", то вывод может быть впоследствии использован
как ввод:
(x^2+a)/(y^2+b); ) x 2 +a
y 2 +b
display2d:false$
(x^2+a)/(y^2+b); ) (x^2+a)/(y^2+b)
 Переменная showtime
включает или выключает печать времени, затраченного на каждое дей-
ствие. Изначально установлено значение "false". Если установить значение
"true", то будет печататься, во-первых, "идеальное" процессорное время, в
течение которого выполнялась выкладка, и, во-вторых, "физическое" время,
затраченное на выкладку (в системах с делением времени второе всегда пре-
вышает первое).
showtime:true$
Evaluation took 0.00 seconds (0.00 elapsed)
fun1(a,b,c)$
Evaluation took 4.08 seconds (4.20 elapsed)
 Функция fortran
позволяет получить выдачу, которую можно использовать как фрагмент
фортрановской программы
fortran(sum(x^i,i,0,15));
x**15+x**14+x**13+x**12+x**11+
1 x**10+x**9+x**8+x**7+x**6+x**5+
2 x**4+x**3+x**2+x+1
Работа с файлами
 Функция batch
запускает файл с прогpаммой. Операторы выполняются один за другим
либо до конца файла, либо до синтаксической ошибки, либо до некорректной
операции.
batch("myfile.mxm");
7

 Функция load
загружает тот или иной файл.
load(somefile);
Тип загрузки зависит от типа файла. Именно, можно загружать файл с ма-
кросами, т.е. фактически файл с программой на MAXIMA (типичные расши-
рения имен таких файлов "mxm", "mc" или "mac"), можно загружать файл
с программой на LISP (типичные расширения имен таких файлов "lisp" или
"lsp"), и можно загружать двоичный файл с уже оттранслированными кодами
(типичное расширение имен таких файлов "o").
Как правило эта функция необходима для загрузки того или иного па-
кета, который не загружается автоматически. Интересно, что разные пакеты
хранятся в разных форматах, так что может грузиться и файл в формате MAX-
IMA, и LISP-файл и двоичный файл. Стандартные функций в MAXIMA либо
входят в ядро системы и поэтому доступны изначально, либо являются авто-
загрузочными, т.е. при их первом вызове происходит автоматическая загрузка
необходимого файла, либо требуют явной загрузки того или иного файла | в
этом случае до их вызова необходимо написать "load(packetname)".
При этом для загрузки, например, пакета интегрирования по частям мож-
но написать как
load(bypart);
так и
load("bypart");
и в том и в другом случае загрузится файл "bypart.mc".
 Функция writefile
начинает писать всю выдачу MAXIM'ы в указанный файл. При этом (в
отличие от REDUCE) выдача на терминал не прекращается:
writefile("myoutput.mxm")$
 Функция closefile
прекращает вывод в файл:
closefile()$
8

Функции, преобразующие аналитические
выражения
Функции для выражений общего вида
 Функция ev
| это основная функция, обрабатывающая выражения. Ее синтаксис
довольно разнообразен.
ev(expr);
ev(expr,flag1,flag2,...);
ev(expr,x=a+b,y:c/d,...);
ev(expr,flag1,x=a,y:b,flag2,...);
Можно даже опускать имя функции ev
expr,flag1,flag2,...;
expr,x=val1,y=val2,...;
expr,flag1,x=val1,y=val2,flag2,...;
Следует, однако, иметь в виду, что в то время как записи "ev(expr, ag);" и
"expr, ag;" являются синонимами, записи "expr;" и "ev(expr);" не идентичны,
именно:
v:a+b$ a:7$
v; ) a+b
ev(v); ) b+7
v,expand; ) b+7
На выражение "expr" по умолчанию действует функция упрощения. Если ука-
заны флаги (их имена как правило совпадают с именами других функций,
преобразующих выражения), то с выражением производятся действия в соот-
ветствии с этими флагами. Вот некоторые из флагов:
expand factor trigexpand trigreduce
(на выражение действуют одноименные функции),
pred diff simp
("pred" вызывает вычисление значения логического выражения, "di " вызыва-
ет выполнение "замороженного" дифференцирования, "simp" вызывает выпол-
нение функции упрощения даже в том случае, когда переменная "simp" равна
"false").
Если указаны подстановки (в виде "x=val1" или "x:val2"), то они выпол-
няются.
При этом повторный вызов функции "ev" вполне способен еще раз изме-
нить выражение, т.е. обработка выражения не идет до конца при однократном
вызове функции "ev".
9

ev((a+b)^2,expand); ) a 2
+ 2 a b + b 2
ev((a+b)^2,a=x); ) (x+b) 2
ev((a+b)^2,a=x,expand,b=7);
=) x 2 + 14 x + 49
(a+b)^2,a=x,expand,b=7;
=) x 2
+ 14 x + 49
 Переменная simp
разрешает либо запрещает упрощение выражений. Изначально она рав-
на "true", если установить ее равной "false", то упрощения производиться не
будут:
simp:false$
x+y+x; ) x + y + x
simp:true$
x+y+x; ) y + 2 x
 Функция factor
факторизует выражение.
factor( a*c+b*c+a*d+b*d )
=) (a+b)(c+d)
factor( (x^3+2*x^2*y+y^3)/
(x^2+2*x*y+y^2)+
x^2*y/(x^2+2*x*y+y^2)+
3*x*y^2/(x+y)^2 );
=) x+y
 Функция gfactor
отличается от функции "factor" тем, что умеет работать с мнимой еди-
ницей, т.е. может факторизовать выражения типа x 2 + a 2 и x 2 + 2ixa a 2
factor(x^2+a^2); ) x 2 +a 2
factor(x^2+2*%i*x*a-a^2);
=) x 2
+ 2%ixa - a 2
gfactor(x^2+a^2); ) (x+%ia)(x-%ia)
gfactor(x^2+2*%i*x*a-a^2);
=) (x+%ia) 2
10

 Функция factorsum
факторизует отдельные слагаемые в выражении.
factorsum( a^3+3*a^2*b+3*a*b^2+b^3
+x^2+2*x*y+y^2 ) (y+x) 2 +(b+a) 3
 Функция gfactorsum
отличается от "factorsum" тем же, чем "gfactor" отличается от "factor":
gfactorsum( a^3+3*a^2*b+3*a*b^2+b^3
+x^2+y^2 ) (y+%ix)(y-%ix)+(b+a) 3
 Функция expand
раскрывает скобки.
expand( (a+b)*(c+d) );
=) a c + b c + a d + b d
expand( (x^3+2*x^2*y+y^3)/
(x^2+2*x*y+y^2)+
x^2*y/(x^2+2*x*y+y^2)+
3*x*y^2/(x+y)^2 );
=) x 3
x 2 +2xy+y 2
+ 3x 2
y
x 2 +2xy+y 2
+ 3xy 2
x 2 +2xy+y 2
+ y 3
x 2 +2xy+y 2
 Функция combine
объединяет слагаемые с идентичным знаменателем
combine( x^3/(x^2+2*x*y+y^2)+
3*x^2*y/(x^2+2*x*y+y^2)+
3*x*y^2/(x^2+2*x*y+y^2)+
y^3/(x^2+2*x*y+y^2)+
a/(c+d)+b/(c+d)
=) x 3
+3x 2
y+ 3xy 2
+y 3
x 2 +2xy+y 2
+ a+b
c+d
 Функция xthru
приводит выражение к общему знаменателю, не раскрывая скобок и не
пытаясь факторизовать слагаемые
xthru(1/(x+y)^10+1/(x+y)^12
11

=) (x+y) 2
+ 1
(x+y) 12
xthru( m/(x^2+2*x*y+y^2)+
n/(x+y)^4);
=) m(x+y 4
) + n(x 2
+ 2xy + y 2
)
(x 2 + 2xy + y 2 )(x+y) 4
Разумеется, в последнем случае разумнее сначала факторизовать каждое
слагаемое, а уж потом применять "xthru". Применить функцию "factor" к
отдельным слагаемым выражения можно с помощью функции "map".
 Функция multthru
умножает каждое слагаемое в сумме на множитель, причем при умно-
жении скобки в выражении не раскрываются. Она допускает два варианта
синтаксиса
multthru(mult,sum);
multthru(mult*sum);
(порядок сомножителей в последнем варианте не существенен).
multthru((x+y)^2,(x+y)^5
+1/(x+y)^7+(x+y)^2);
=) (x+y) 7 + (x+y) 4 + 1
(x+y) 5
multthru( ((x+y)^5+1/(x+y)^7
+(x+y)^2)*(x+y)^2);
=) (x+y) 7 + (x+y) 4 + 1
(x+y) 5
multthru( ((x^3+2*x^2*y+y^3)/
(x^2+2*x*y+y^2)+
x^2*y/(x^2+2*x*y+y^2)+
3*x*y^2/(x+y)^2 )*
(x^2+2*x*y+y^2)*
(m+n)/(x+y) );
=) (n+m)(x 3 + 2x 2 y + y 3 )
x+y +
3(n+m)xy 2
(x 2
+ 2xy + y 2
)
(x+y) 3
+ (n+m)x 2
y
x+y
12

Функции для рациональных выражений
Хотя функции, преобразующие выражения, не приводят их к канониче-
ской форме (в отличие от REDUCE), каноническое представление (CRE) су-
ществует | это каноническая форма для дробно-рациональных выражений.
Выражение, приведенное к CRE, снабжается меткой /R/ сразу после метки
"d". Дальнейшая работа с ним идет быстрее, а вероятность его упрощения
выше, чем для выражения общего вида.
 Функция rat
приводит выражение к каноническому представлению и снабжает его
меткой /R/. Она упрощает любое выражение, рассматривая его как дробно-
рациональную функцию, т.е. работает с операциями "+", "-", "*", "/" и с
возведением в целую степень. Нецелые степени она не упрощает, т.е. она не
знает, что (x a=2 ) 2 = x a . При этом вид ответа зависит от того, какие пере-
менные она считает более главными, а какие менее главными. Упорядочение
сначала идет по степеням самой главной переменной, внутри коэффициентов
при этих степенях | по степеням менее главной переменной, и т.д. Изна-
чально переменные упорядочены в алфавитном порядке и от начала к концу
"главность" возрастает. Этот порядок можно откорректировать, добавив в
аргументы функции имена переменных в порядке возрастания главности.
(c11) rat( (x^3+2*x^2*y+y^3)/
(x^2+2*x*y+y^2)+
x^2*y/(x^2+2*x*y+y^2)+
3*x*y^2/(x+y)^2 );
=) d(11) /r/ x+y
(c12) v1:m/(a+b)+n/(x+y)$
=)
(c13) rat(v1);
=) (d13) /r/
my+mx+(b+a)n
(b+a)y+(b+a)x
(c14) rat(v1,y,x,n,m,b,a);
=) (d14) /r/
na+nb+(x+y)m
(x+y)a+(x+y)b
(c15) rat(v1,m,n,a,b,x,y);
=) (d15) /r/ my+mx+nb+na
(b+a)y+(b+a)x
(c16) rat(v1,m,n);
=) (d16) /r/ (b+a)n+(y+x)n
(b+a)y+(b+a)x
13

(c17) rat( (x^(a/2)-1)^2*
(x^(a/2)+1)^2 )/(x^a-1);
=) (d17) /r/ (x a/2
) 4
- 2(x a/2
) 2
+ 1
x a
- 1
 Функция ratvars
позволяет изменить алфавитный порядок "главности" переменных, при-
нятый по умолчанию.
ratvars(z,y,x,w,v,u,t,s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a)$
меняет порядок главности в точности на обратный, а
ratvars(m,n,a,b)$
упорядочивает переменные "m,n,a,b" в порядке возрастания "главности", и
делает их более главными, чем все остальные переменные.
 Переменная ratfac
включает или выключает частичную факторизацию выражений при све-
дении их к CRE. Изначально установлено значение "false". Если установить
значение "true", то будет производиться частичная факторизация.
(c4) v2:m/(a+b)^2+n/(x+y)^2$
=)
(c5) rat(v2);
=) (d5) /r/ (my 2 +2mxy+mx 2 +
(b 2 +2ab+a 2 )n) / ((b 2 +2ab+a 2 )y 2 +
(2b 2 +4ab+2a 2 )xy + (b 2 +2ab+a 2 )x 2 )
(c6) ratfac:true$ )
(c7) rat(v1); ) (d7) /r/
my+mx+(b+a)n
(b+a)(y+x)
(c8) rat(v2);
=) (d8) /r/
my 2 +2mxy+mx 2 + (b 2 +2ab+a 2 )n
(my 2
+2mxy+mx 2
) (b 2
+2ab+a 2
)
 Функция ratsimp
приводит все куски (в том числе аргументы функций) выражения, ко-
торое не является дробно-рациональной функцией, к каноническому предста-
влению, производя упрощения, которые не делает функция "rat". Не снабжает
выражение меткой /R/. Повторный вызов функции может изменить результат,
т.е. упрощение не идет до конца.
14

(c77) ratsimp( (x^(a/2)-1)^2*
(x^(a/2)+1)^2 )/(x^a-1);
=) (d77) x 2a
- 2x a
+ 1
x a
- 1
(c78) ratsimp(%); ) (d78) x a
- 1
 Функция fullratsimp
вызывает функцию "ratsimp" до тех пор, пока выражение не перестанет
меняться.
fullratsimp( (x^(a/2)-1)^2*
(x^(a/2)+1)^2 )/(x^a-1);
=) x a
- 1
 Переменная ratsimpexpons
управляет упрощением показателей степени в выражениях. Изначально
установлено значение "false". Забавно, что при этом аргумент любой функции
упрощается:
fullratsimp( sin( (x^(a/2)-1)^2*
(x^(a/2)+1)^2)/(x^a-1));
=) sin(x a
- 1)
а показатель степени (в том числе показатель экспоненты) | нет:
fullratsimp( exp( (x^(a/2)-1)^2*
(x^(a/2)+1)^2)/(x^a-1));
=) %e
x 2a
x a
-1
-
2x a
x a
-1
+
1
x a
-1
Если установить значение "true", то показатели степени начнут упрощаться:
ratsimpexpons:true$
fullratsimp( exp( (x^(a/2)-1)^2*
(x^(a/2)+1)^2)/(x^a-1));
=) %e
x a
- 1
 Функция ratexpand
раскрывает скобки в выражении. Не снабжает выражение меткой /R/.
Отличается от функции "expand" тем, что приводит выражение к канониче-
ской форме, поэтому ответ может оказаться короче, чем при применении "ex-
pand":
15

ratexpand( (x^3+2*x^2*y+y^3)/
(x^2+2*x*y+y^2)+
x^2*y/(x^2+2*x*y+y^2)+
3*x*y^2/(x+y)^2 );
=) x+y
(см. выше аналогичный пример с "expand").
Функции для тригонометрических выражений
 Функция trigexpand
раскладывает все тригонометрические функции от сумм в суммы про-
изведений тригонометрических функций
trigexpand(sin(x+y));
=) sin(x)cos(y)+sin(y)cos(x)
 Переменная trigexpand
управляет работой функции "trigexpand". Изначально переменная "trig-
expand" равняется "false", это приводит к тому, что функция "trigexpand" не
работает до конца, т.е. ее повторный вызов может изменить выражение. Если
переменная "trigexpand" будет равна "true", то функция "trigexpand" будет
работать до тех пор, пока выражение не перестанет меняться.
trigexpand(sin(2*x+y));
=) cos(2x)sin(y) + sin(2x)cos(y)
trigexpand(%); ) (cos 2
(x)-sin 2
(x))sin(y) +
2 cos(x) sin(x) cos(y)
trigexpand:true; ) true
trigexpand(sin(2*x+y));
=) (cos 2 (x)-sin 2 (x))sin(y) +
2 cos(x) sin(x) cos(y)
 Функция trigreduce
свертывает все произведения тригонометрических функций в тригоно-
метрические функции от сумм. Функция работает не до конца, так что по-
вторный вызов может изменить выражение
trigreduce((cos(x)^2-
sin(x)^2)*sin(y) +
2*cos(x)*sin(x)*cos(y) );
=) sin(y+2x)
2 -
sin(y-2x)
2 + cos(2x)sin(y)
16

trigreduce(%); ) sin(y+2x)
 Функция trigsimp
вовсе не упрощает выражение , а применяет к нему правило sin 2 (x) +
cos 2 (x) = 1:
trigsimp((cos(x)^2-
sin(x)^2)*sin(y) +
2*cos(x)*sin(x)*cos(y) );
=) (2cos 2
(x)-1)sin(y) + 2 cos(x) sin(x) cos(y)
 Функция trirat
пытается свести выражение с тригонометрическими функциями к неко-
му универсальному каноническому виду (в общем, пытается упростить выра-
жение). Как правило существенно упрощает выражение, но иногда работает
очень долго (иногда бесконечно долго).
trigrat((cos(x)^2-
sin(x)^2)*sin(y) +
2*cos(x)*sin(x)*cos(y) );
=) sin(y+2x)
Функции для выражений со степенями и логарифмами
 Функция radcan
упрощает выражения со вложенными степенями и логарифмами:
radcan( log( x^3*exp(4*y)*
exp(5*log(w))/z^6 ) );
=) -6log(z) + 4y + 3log(x) + 5log(w)
radcan( (x^(a/2)-1)^2*
(x^(a/2)+1)^2 )/(x^a-1);
=) x a
- 1
 Функция rootscontract
компактифицирует возведения в степень в данном выражении
rootscontract( x^(1/6)*
y^(1/12)*z^(1/30) );
=) (x sqrt(y) z 1/5 ) 1/6
rootscontract( x^(1/2)*y^(1/2)*
z^(1/4) ); ) sqrt(x y sqrt(z))
17

 Функция logcontract
компактифицирует логарифмы в данном выражении
logcontract( a*log(x)+
3*log(y)-4*log(x) );
=) a log(x) + log(
y 3
x 4 )
Логические выражения и база данных
Логические выражения образуются из операций сравнения
> < >= <= = #
(символ # означает "не равно", а запись "a=b" имеет синоним "equal(a,b)").
Странноватой особенностью операций сравнения является то, что если их по-
ставить в качестве условий в циклах и условных выражениях, то они будут
вычислены, но взятые сами по себе, они не вычисляются:
3>2; ) 3>2
equal(3,2); ) equal(3,2)
3#2; ) 3#2
Флаг "pred" в функции "ev" вызывает вычисление логических выражений:
ev(3#2,pred); ) true
 Функция is
инициирует вычисление логического выражения
is(3>2); ) true
is(3=2); ) false
is(equal(3,2)); ) false
is(3#2); ) true
Кроме того, определены встроенные логические функции, перечислим не-
которые из них.
 Функция atom
возвращает "true", если аргумент не имеет структуры, т.е. составных
частей (например, число или переменная не имеют структуры).
 Функция zeroequiv
возвращает "true", если аргумент равен нулю
18

 Функция freeof
возвращает "true", если второй ее аргумент не содержит ("свободен от")
первого
freeof(x,f(x+g(y))); ) false
freeof(g,f(x+g(y))); ) false
freeof(z,f(x+g(y))); ) true
 Функция symbolp
возвращает "true", если ее аргумент является символом:
symbolp(f(x)); ) false
symbolp(3); ) false
symbolp(f); ) true
 Функция scalarp
возвращает "true", если ее аргумент является константой:
scalarp(f); ) false
scalarp(sin(1/3)); ) true
scalarp(f(1/3)); ) false
scalarp(1/3); ) true
 Функция listp
возвращает "true", если ее аргумент является списком.
 Функция matrixp
возвращает "true", если ее аргумент является матрицей.
 Функция numberp
возвращает "true", если ее аргумент является числом:
numberp(sin(1/3)); ) false
numberp(1/3); ) true
numberp(1.3b22); ) true
 Функция integerp
возвращает "true", если ее аргумент является целым числом.
 Функция oddp
возвращает "true", если ее аргумент является целым нечетным числом.
 Функция evenp
возвращает "true", если ее аргумент является целым четным числом.
19

 Функция primep
возвращает "true", если ее аргумент является целым простым числом.
 Функция floatp
возвращает "true", если ее аргумент является действительным числом
машинной точности.
 Функция bfloatp
возвращает "true", если ее аргумент является действительным числом
неограниченной точности.
Кроме того, логическими выражениями являются запросы из базы данных:
is(a>3);
Следует подчеркнуть, что речь идет не о значении переменной "a" (которое
неизвестно), а об информации, в какой области эта переменная может менять-
ся.
 Функция assume
вводит информацию о переменной в базу данных.
assume(n>4); ) [n>4]
После этого можно вводить запрос типа
is(n>1); ) true
Однако, запросы типа
is(n<7);
is(n>7);
вызовут сообщение об ошибке, т.к. в базе данных такой информации нет.
Точно так же вызовет сообщение об ошибке более сложный запрос (который в
принципе должен был бы дать значение "true"):
is(n^2+n>19);
Повторное применение функции "assume" проверяется на противоречи-
вость и на избыточность. В случае, если новая информация не противоречит
предыдущим данным и не вытекает из них, она аддитивно добавляется к базе
данных. К сожалению, предыдущие условия не проверяются на избыточность
при появлении новых условий:
assume(n>3); ) [redundant]
assume(n<3); ) [inconsistent]
assume(n>10); ) [n>10]
assume(n<30); ) [n<30]
is(n<9); ) false
is(n>31); ) false
20

 Функция properties
печатает свойства переменной и, тем самым, позволяет выяснить, какая
именно информация содержится в базе данных о данной переменной
properties(n);
=) [database info,n>4,n>10,n<30]
(новое условие n > 10 не отменило избыточное теперь условие n > 4).
Из приведенных примеров видно, что поменять свойство переменной на
противоположное с помощью функции "assume" невозможно:
assume(x>0)$
assume(x<0); ) [inconsistent]
is(x>0); ) true
 Функция forget
отменяет сведение, введенное в базу данных. Это позволяет поменять
свойство переменной на противоположное.
forget(n<30); ) [n<30]
properties(n); ) [database info,n>4,n>10]
Забавно, что после всех этих манипуляций можно присвоить переменной
"n" значение, которое будет противоречить информации из базы данных:
n:-77; ) -77
is(n>0); ) false
properties(n);
=) [value,database info,n>4,n>10]
 Функция kill
уничтожает всю информацию об объекте. Это позволяет за один раз
ликвидировать всю ранее введенную информацию о переменной "n".
kill(n); ) done
properties(n); ) [ ]
Составные логические выражения формируются с помощью логических
операций "and", "or", "not".
is( 3>1 and -1>=-3 and
2#1 and not(equal(2,1)) )
=) true
is( 3<1 or -1<=-3 or
not 2#1 or 2=1 )
=) false
21

Условные выражения и циклы
Синтаксис условного выражения может быть проиллюстрирован приме-
ром
a:1$
if a>3 then x:1 else x:-1;
=) -1
x; ) -1
if a<3 then x:x+1 else x:x-1;
=) 0
x; ) 0
Как обычно, часть с else можно опустить
if a>3 then x:1; ) false
if a<3 then x:1; ) 1
Синтаксис цикла допускает три варианта
(c5) for i:1 thru 3 step 2 do disp(i);
1
3
(d5) done
(c6) for i:1 step 2 while i<6 do ldisplay(i);
(e6) i=1
(e7) i=3
(e8) i=5
(d8) done
(c9) for i:1 step 2 unless i>4 do display(i);
i=1
i=3
(d9) done
(заодно мы еще раз проиллюстрировали работу функций "disp", "display" и
"ldisplay").
Как обычно, если шаг равен единице, его можно опустить:
x:0$
for i:1 thru 5 do x:x+1$
x; ) 5
Кроме того, возможны циклы, в которых переменная цикла меняется не
на фиксированную величину, а по произвольному закону:
for i:1 next 2*i thru 5 do disp(i)$
1
2
4
22

Разумеется, допустимы вложенные циклы и вложенные условные выраже-
ния.
Существуют также циклы суммирования и умножения.
 Функция sum
реализует цикл суммирования
sum(x^i,i,3,5); ) x 5 +x 4 +x 3
sum(x^i,i,a+3,a+5); ) x a+5
+x a+4
+x a+3
 Функция product
реализует цикл умножения
product(x+i,i,3,5); ) (x+3)(x+4)(x+5)
Блоки
Как в условных выражениях, так и в циклах вместо простых операторов
можно писать составные операторы, т.е. блоки.
Стандартный блок имеет вид:
block([r,s,t],r:1,s:r+1,t:s+1,x:t,t*t);
Сначала идет список локальных переменных блока (глобальные переменные с
теми же именами никак не связаны с этими локальными переменными), этот
список может быть пустым. Далее идет набор операторов.
Упрощенный блок имеет вид:
(x:1,x:x+2,a:x);
обычно в циклах и в условных выражениях применяют именно эту форму бло-
ка.
Приведем несколько вполне бессмысленных примеров применения блоков
в циклах и в условных выражениях
if a>3 then (r:x,r:(r+1)/2) else block([s],s:x,s:s+1,r:s/2);
for i:1 thru 5 do
(s:1,x:a^i,
for j:1 thru 25 do
block([],s:s+x,x:x/j),
display(i,s) );
Внутри данного блока допускаются оператор перехода на метку и опера-
тор "return".
23

В отсутствие оператора "return" значением блока является значение по-
следнего оператора. Оператор return прекращает выполнение текущего бло-
ка и возвращает в качестве значения блока свой аргумент
block([],x:2,x:x*x,
return(x), x:x*x);
=) 4
x; ) 4
В отсутствие оператора перехода на метку операторы в блоке выполня-
ются последовательно. (В данном случае слово "метка" означает отнюдь не
метку типа "c5" или "d7"). Оператор "go" выполняет переход на метку, рас-
положенную в этом же блоке
block([a],a:1,metka,
a:a+1,go(metka) )$
выполнение этого блока никогда не завершится, т.к. в нем реализован беско-
нечный цикл. Меткой может быть произвольный идентификатор.
Следует иметь в виду, что цикл сам по себе является блоком, так что
(в отличие от языка "C") прервать выполнение циклов (особенно вложенных
циклов) с помощью оператора "go" невозможно | оператор "go" и метка ока-
жутся в разных блоках.
То же самое относится к оператору "return". Если цикл, расположенный
внутри блока, содержит оператор "return", то при исполнении оператора "re-
turn" произойдет выход из цикла, но не выход из блока:
block([],x:for i:1 thru 15 do if i=2 then
return(555),display(x),777);
x=555
777
block([],x:for i:1 thru 15 do if i=52 then
return(555),display(x),777);
x=done
777
Если необходимо выйти из нескольких вложенных блоков сразу (или не-
скольких блоков и циклов сразу) и при этом возвратить некоторое значение,
то следует применять блок "catch"
catch( block([],a:1,a:a+1,
throw(a),a:a+7),a:a+9 );
=) 2
a; ) 2
catch(block([],for i:1 thru 15 do
if i=2 then throw(555)),777);) 555
catch(block([],for i:1 thru 15 do
24

if i=52 then throw(555)),777);) 777
Оператор "throw" | это аналог оператора "return", но он обрывает не
текущий блок, а все вложенные блоки вплоть до первого встретившегося блока
"catch".
Наконец, блок "errcatch" позволяет перехватывать некоторые из ошибок,
которые в нормальной ситуации привели бы к прерыванию работы системы.
Например, если в базе данных нет информации про переменную "y", то запрос
is(y>0);
вызовет сообщение об ошибке и выход из системы. Однако, если поместить
этот запрос в блок "errcatch", то произойдет только выход из этого блока.
Значением блока в этом случае является пустой список:
errcatch(x:1,a:x+1,
is(y>0),a:-77); ) [ ]
a; ) 2
Списки, матрицы, массивы
Списки
Список в MAXIMA | это объект, вполне аналогичный списку в LISP, т.е.
упорядоченная совокупность произвольных (возможно разнородных) объектов
(что-то вроде одномерного массива). Чтобы задать список, достаточно запи-
сать его элементы через запятую и ограничить запись квадратными скобками
li1:[a,b,11]; ) [a,b,11]
Элементом списка может быть любой объект, в том числе и другой список
li2:[a,b,[c,d],e,f];
=) [a,b,[c,d],e,f]
Список может быть пустым
li3:[ ]; ) [ ]
или состоять из одного элемента
li4:[77]; ) [77]
Ссылка на элемент списка производится так:
li1[2]; ) b
li1[2]:c; ) c
li1; ) [a,c,11]
li2[3]; ) [c,d]
li2[3][2]; ) d
 Функция length
возвращает длину списка
25

length([a,b,[c,d],e,f]);
=) 5
 Функция part
позволяет выделить тот или иной элемент часть списка.
part([a,b,c],2); ) b
part([a,[b,c],d],2); ) [b,c]
Если список вложенный, то необязательно писать
part(part([a,[b,c],d],2),2);
=) c
можно просто написать
part([a,[b,c],d],2,2);
=) c
Следует подчеркнуть, что при присвоении списков
li1:[a,b,c]$
li2:li1; ) [a,b,c]
отнюдь не создается копия списка "li1", просто переменная "li2" становится
еще одним указателем на тот же самый список. Поэтому
li1[3]:d$
li2; ) [a,b,d]
 Функция copylist
изготовляет "настоящую" копию списка
li1:[a,b,c]$
li3:copylist(li1); ) [a,b,c]
li1[3]:d$
li3; ) [a,b,c]
 Функция makelist
позволяет создавать списки и допускает 2 варианта синтаксиса
makelist(i^3,i,1,3);
=) [1, 8, 27]
(здесь реализуется цикл по переменной "i" в пределах от 1 до 3), либо
makelist(x=i^2,i,[c,d,e]);
=) [x=c^2, x=d^2, x=e^2]
(здесь переменная "i" пробегает все элементы заданного списка).
 Функция append
позволяет склеивать два списка
append([a,b],[c,d]);
=) [a, b, c, d]
26

li1:[a]$ li2:[b,c]$
append(li1,li2); ) [a, b, c]
 Функция cons
позволяет добавлять элемент в начало списка
cons(x,[a,b]); ) [x, a, b]
 Функция endcons
позволяет добавлять элемент в конец списка
endcons(x,[a,b]); ) [a, b, x]
 Функция reverse
меняет порядок элементов в списке на обратный
reverse([a,b,[c,d],e,f]);
=) [f,e,[c,d],b,a]
 Функция sort
упорядочивает элементы списка
sort([3,a,-1,x,b,0]);
=) [-1,0,3,a,b,x]
сначала идут числа (в порядке возрастания), затем идентификаторы (в алфа-
витном порядке).
 Функция sublist
составляет список из тех элементов исходного списка, для которых за-
данная логическая функция возвращает значение "true"
sublist([3,a,-1,x,b,0],numberp);
=) [3,-1,0]
(numberp выдает "true" для чисел и "false" во всех остальных случаях). При
этом может использоваться логическая функция, определенная пользователем
f(x):=is(x>5)$
sublist([7,3,6,4,5],f);
=) [7,6]
 Функция member
возвращает "true", если ее первый аргумент является элементом задан-
ного списка, и "false" в противном случае
li1:[a,b,[c,d],e,f]$
member(b,li1); ) true
member(c,li1); ) false
member([c,d],li1); ) true
27

 Функция first
выделяет первый элемент списка
first([a,b,c]); ) a
 Функция rest
выделяет остаток после удаления первого элемента списка
rest(a,b,c); ) [b,c]
 Функция last
выделяет последний элемент списка
last([a,b,c]); ) c
 Функция map
применяет заданную функцию к каждому элементу списка
map(sin,[-1,0,1]) ) [-sin(1),0,sin(1)]
при этом может использоваться как стандартная функция, так и функция,
определенная пользователем
f(x):=2*x$ map(f,[1,2,3]);
=) [2,4,6]
 Функция apply
применяет заданную функцию ко всему списку (список становится спис-
ком аргументов функции). Например, если Вы соорудили список, состоящий
из чисел:
li1:[33,-22,11]$
то, чтобы найти максимальное или минимальное число, надо вызвать функ-
цию "max" или "min". Однако, обе функции в качестве аргумента ожидают
несколько чисел, а не список, составленный из чисел. Применять подобные
функции к спискам и позволяет функция "apply":
apply(max,li1); ) 33
apply(min,li1); ) -22
28

Массивы
Массивы | это объекты с индексами. Ссылка на элемент массива произ-
водится обычным образом:
ar[2,0,1];
выведет значение элемента массива, а
ar[55]:77+x$
присвоит элементу массива указанное значение.
В MAXIM'е определены массивы 3 видов.
Во-первых, это массивы неопределенного размера ("hashed" массив). Зна-
чения элементов такого массива задается либо "индивидуально"
ar1[23]:7777$
ar1[-4]:5555$
ar2[2,3]:777$
ar2[-22,-33]:555;$
либо как функция индексов
ar3[i,j]:=i+j; ) ar3
i , j
:=i+j
Индексы этого массива могут принимать любые целочисленные (в том числе и
отрицательные) значения. Если элементы заданы как функция индексов, то их
значения будут вычисляться по приведенной формуле. При повторной ссылке
на этот же элемент выкладки не производятся | используется вычисленное
предыдущий раз значение. Кроме того, даже если задана функция индексов,
отдельные элементы массива можно переопределять индивидуально
ar3[1,2]; ) 3
ar3[-2,-3]; ) -5
ar3[-2,-3]:7; ) 7
ar3[3,4]:5; ) 5
Во-вторых, это масивы заданного размера
 Функция array
определяет массив с данным именем, определенным количеством индек-
сов и заданным размером. Можно также указать его тип
array(ar4,2,1); ) ar4
array(ar5,2); ) ar5
array(ar6,float,2,1);
=) ar6
array(ar7,float,1,1,1);
=) ar7
29

Индексы этих массивов пробегают значения от 0 до указанного числа, так что
фактический размер массивов "ar4" и "ar6" | 3  2, массива "ar5" | 3, а
массива "ar7" | 2  2  2.
Первоначальные (сразу после определения) значения элементов для масси-
ва с определенным типом | это нули, а для массива с неопределенным типом
первоначальные значения не определены, и при попытке их вывода печатаются
5 диезов (#####).
В-третьих, это "самопечатающиеся" массивы
 Функция make_array
создает массивы третьего вида, содержимое которых печатается автома-
тически. Целесообразность использования массивов этого вида сомнительна.
ar8:make_array('any,3,2);
=) {Array: #2a((nil nil)(nil nil)(nil nil)) }
ar9:make_array('float,3);
=) {Array: #(0.0 0.0 0.0) }
Индексы здесь пробегают значения от 0 до указанного числа минус 1 , т.е.
размерность "ar8" | 3  2, а "ar9" | 3.
Первоначальные (сразу после создания) значения элементов для массива с
определенным типом | это нули, а для массива с неопределенным типом (т.е.
с типом "any") | это значение "nil".
Для массивов первого и второго видов идентификатор | это именно имя
массива и его использование без квадратных скобок не вызывает никаких дей-
ствий:
ar4 ) ar4
Для массивов третьего вида идентификатор | это не имя массива, а обыч-
ная переменная, значение которой есть массив третьего вида. Но содержимое
массива третьего вида печатается автоматически, поэтому
ar9; ) {Array: #(0.0 0.0 0.0) }
 Переменная arrays
содержит список имен массивов первого и второго видов, определенных
на данный момент
arrays;
=) [ar1,ar2,ar3,ar4,ar5,ar6,ar7]
 Функция arrayinfo
печатает информацию о массиве | его вид (для массивов первого ви-
да | hashed, для массивов второго вида с неопределенным типом | declared,
для второго вида с определенным типом | complete, для массивов третьего
30

вида | declared). Далее печатается число индексов массива. Далее для масси-
вов первого вида печатаются индексы элементов, значения которых известны
(т.е. либо были присвоены либо были вычислены по формуле), а для массивов
второго и третьего видов | размер (в виде списка максимальных значений
каждого из индексов, при этом для массива третьего вида из заданного при
определении размера автоматически вычитается единица).
arrayinfo(ar1); ) [hashed,1,[-4],[23]]
arrayinfo(ar2); ) [hashed,2,[-22,-33],[2,3]]
arrayinfo(ar3);
=) [hashed,2,[-2,-3],[1,2],[3,4]]
arrayinfo(ar4); ) [declared,2,[2,1]]
arrayinfo(ar5); ) [declared,1,[2]]
arrayinfo(ar6); ) [complete,2,[2,1]]
arrayinfo(ar7); ) [complete,3,[1,1,1]]
arrayinfo(ar8); ) [declared,2,[2,1]]
arrayinfo(ar9); ) [declared,1,[2]]
 Функция listarray
печатает содержимое массивов первого и второго видов (сначала меня-
ется последний индекс, потом предпоследний и.т.д., т.е. 0,0 0,1 0,2 : : : 1,0 1,1
1,2 : : : 2,0 2,1 2,2 : : :    для массива с двумя индексами). Вызов этой функции
для массива третьего вида приводит к сообщению об ошибке. Как уже было
сказано, содержимым массива первого вида считаются те элементы, которые
вычислялись либо присваивлись явно. Поэтому
listarray(ar3); ) [7,3,5]
ar3[6,6]; ) 12
listarray(ar3); ) [7,3,5,12]
listarray(ar4);
=) [#####, #####, #####, #####, #####, #####]
ar4[2,0]:3$ ar4[0,1]:a+b$
listarray(ar4);
=) [#####, a+b, #####, #####, 3, #####]
listarray(ar6); ) [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
ar8[1,0]:77$
ar8;
=) {Array: #2a((nil nil)(77 nil)(nil nil)) }
 Функция fillarray
позволяет заполнять одноиндексные массивы третьего вида из списка
(при этом длина списка может не совпадать с размерностью массива)
31

ar9:make_array('float,3);
=) {Array: #(0.0 0.0 0.0) }
fillarray(ar9,[4.0,5.0])$
ar9; ) {Array: #(4.0 5.0 0.0) }
fillarray(ar9,[6.0,7.0,8.0,9.0])$
ar9; ) {Array: #(6.0 7.0 8.0) }
 Функция kill
уничтожает указанный объект или объекты, в том числе и массив
kill(ar1,ar4); ) done
arrays; ) [ar2,ar3,ar5,ar6,ar7]
 Функция remarray
уничтожает массив или массивы
remarray(ar2,ar3,ar5,ar7);
=) [ar2,ar3,ar5,ar7]
arrays; ) [ar6]
remarray(ar6)$
arrays; ) [ ]
Матрицы
В MAXIM'е определены прямоугольные матрицы.
 Функция matrix
возвращает матрицу, заданную поэлементно
ma1:matrix([a,b,c],[d,e,f]);
=)

a b c
d e f

Значение элемента матрицы извлекается так
ma1[1,2]; ) b
Точно так же можно поменять отдельный элемент матрицы
ma1[2,3]:77$
ma1; )

a b c
d e 77

Существуют матрицы, состоящие из одной строки
ma2:matrix([a,b]) ) [ a b ]
и из одного столбца
ma3:matrix([a],[b]) )

a
b

32

 Функция genmatrix
возвращает матрицу заданной размерности, составленную из элементов
двухиндексного массива
ma4:genmatrix(ar1,2,2)
=)
2
4
ar1
1,1
ar1
1,2
ar1
2,1
ar1
2,2
3
5
при этом можно задать элемент массива в общем виде
ar2[i,j]:=10*i+2*j$
ma5:genmatrix(ar2,2,2);
=)

12 14
22 24

 Функция zeromatrix
возвращает матрицу заданной размерности, составленную из нулей
zeromatrix(2,3); )

0 0 0
0 0 0

 Функция ident
возвращает единичную матрицу заданной размерности
ident(2); )

1 0
0 1

Присвоение матриц устроено в MAXIM'е так же, как и присвоение списков,
именно, набор операторов
ma1:matrix([a,b],[c,d])$ ma2:ma1$
отнюдь не создает копию матрицы "ma1" с именем "ma2", а делает перемен-
ную "ma2" еще одним указателем на ту же самую матрицу. В результате
ma1[2,2]:77$
приведет к
ma2; )

a b
c 77

 Функция copymatrix
изготовляет "настоящую" копию матрицы
ma1:matrix([a,b],[c,d])$
ma2:copymatrix(ma1)$
ma1[2,2]:77$ ma2; )

a b
c d

 Функция row
выделяет заданную строку матрицы
33

ma1:matrix([a,b],[c,d])$
row(ma1,2); ) [ c d ]
 Функция col
выделяет заданный столбец матрицы
col(ma1,1); )

a
c

 Функция addrow
добавляет строку к матрице
addrow(ma1,[e,f]); )
2
4
a b
c d
e f
3
5
 Функция addcol
добавляет столбец к матрице
addcol(ma1,[e,f]); )

a b e
c d f

 Функция submatrix
выделяет из матрицы подматрицу. Аргументы функции имеют следу-
ющий вид: сначала через запятую идут номера вычеркиваемых строк, затем
сама матрица, а затем номера вычеркиваемых столбцов
ar2[i,j]:=10*i+j$
ma2:genmatrix(ar2,4,5);
=)
2
6
4
11 12 13 14 15
21 22 23 24 25
31 32 33 34 35
41 42 43 44 45
3
7
5
submatrix(1,3,ma2,2,4,5);
=)

21 23
41 43

На матрицах определены обычные операции умножения на число, сложе-
ния и матричного умножения. Последнее реализуется с помощью бинарной
операции "." (точка). Разумеется, размерности матриц должны обеспечивать
математическую корректность операций
ma1:matrix([a,b],[c,d])$
ma1+3*ma1+ident(2); )

4a+1 4b
4c 4d+1

34

ma1.ma1; )
2
6
4
bc+a 2 bd+ab
cd+ac d 2 +bc
3
7
5
По не очень ясной причине определена загадочная операция возведения
матрицы в "обычную" степень
ma1^3; )
2
6
4
a 3 b 3
c 3
d 3
3
7
5
Однако, определена и операция матричного возведения в целую степень
ma1^^2; )
2
6
4
bc+a 2
bd+ab
cd+ac d 2 +bc
3
7
5
ma1^^(-1); )
2
6
6
4
d
ad-bc
-b
ad-bc
-c
ad-bc
a
ad-bc
3
7
7
5
Детерминант можно вынести за пределы матрицы, если вызвать функцию
"ev" с флагом "detout":
ev(ma1^^(-1),detout);
=)

d -b
-c a

ad-bc
Кроме того, отдельно определена функция обращения матрицы, которая
является синонимом операции возведения матрицы в степень "-1".
invert(ma1); )
2
6
6
4
d
ad-bc
-b
ad-bc
-c
ad-bc
a
ad-bc
3
7
7
5
Следует отдельно обсудить свойства умножения матриц на строки и
столбцы. Как это ни странно, если матрица стоит слева, то правым сомножи-
телем может быть не только столбец, но и строка и даже список.
li1:[e,f]$
str1:matrix([e,f])$
35

stlb1:matrix([e],[f])$
После этого операторы
ma1.li1;
ma1.str1;
ma1.stlb1;
приведут к одной и той же выдаче
)

bf+ae
df+ce

Если матрица стоит справа, то в качестве левого сомножителя допустимы
список и строка, но не столбец | в случае столбца появится сообщение об
ошибке. Так что операторы
li1.ma1;
str1.ma1;
приведут к одной и той же выдаче
) [ cf+ae df+be ]
 Функция transpose
транспонирует матрицу
transpose(ma1); )

a c
b d

 Функция determinant
вычисляет детерминант матрицы
determinant(ma1); ) ad-bc
 Функция mattrace
вычисляет след матрицы (сумму ее диагональных элементов). Перед
тем, как вызывать ее первый раз, необходимо загрузить пакет "nchrpl" коман-
дой
load(nchrpl)$
После этого
mattrace(ma1); ) a+d
 Функция charpoly
является до некоторой степени избыточной | она вычисляет характе-
ристический полином матрицы, т.е. det( ^
m x) (корни этого полинома | соб-
ственные значения матрицы)
charpoly(ma1,t); ) (a-t)(d-t)-bc
36

 Функция ncharpoly
| это улучшенная версия функции "charpoly". Перед тем, как вызывать
ее первый раз, необходимо загрузить пакет "nchrpl" командой
load(nchrpl)$
после этого можно вызывать функцию "ncharpoly"
ncharpoly(ma1,t); ) t 2 +(-a-d)t+ad-bc
 Функция eigenvalues
вычисляет собственные значения матрицы аналитически, если это воз-
можно. Выдача этой функции достаточно прихотлива | она возвращает спи-
сок, состоящий из двух списков. Первый содержит собственные значения, а
второй | их кратности
ma2:matrix([0,1,0],[1,0,0],
[0,0,1]);
)
2
4
0 1 0
1 0 0
0 0 1
3
5
eigenvalues(ma2); ) [[-1,1],[1,2]]
 Функция eigenvectors
аналитически вычисляет собственные значения и собственные вектора
матрицы, если это возможно. Выдача этой функции чрезвычайно прихотлива
| она возвращает список, первый элемент которого | это в точности выдача
функции "eigenvalues", а далее идут собственные вектора, каждый из которых
представлен как список своих компонент (т.е. как строка)
eigenvectors(ma2);
=) [[[-1,1],[1,2]],[1,-1,0],[1,1,0],[0,0,1]]
 Функция uniteigenvectors
отличается от функции "eigenvectors" тем, что возвращает нормирован-
ные на единицу собственные вектора
uniteigenvectors(ma2);
=) [[[-1,1],[1,2]], [ 1
sqrt(2)
, -1
sqrt(2)
, 0],
[
1
sqrt(2) ,
1
sqrt(2) , 0], [0,0,1]]
37

Внутреннее представление в MAXIM'е
Как и в большинстве других систем аналитических вычислений, анали-
тические выражения в MAXIM'е представляются в виде вложенных списков.
Поэтому те функции, которые действуют на списки, вполне можно при-
менять и к аналитическим выражениям. Для определения количества частей
в выражении следует применять функцию "length", для выделения отдельной
части | функцию "part". При этом надо помнить, что применение функции
"part" к объекту без структуры вызовет ошибку. Поэтому если природа вы-
ражения заранее неизвестна, прежде чем применять к выражению функцию
"part", надо применить к нему функцию "atom". Только если "atom" выдаст
"false", можно применять "part".
Чтобы проиллюстрировать особенности внутреннего представления, при-
ведем несколько примеров
length(a+b) ) 2
length(a+b+c) ) 3
length(-a) ) 1
length(f(a)) ) 1
part([a,b],0); ) [
part([a,b],1); ) a
part([a,b],2); ) b
part(a=b,0); ) =
part(a=b,1); ) a
part(a=b,2); ) b
part(a+b,0); ) +
part(a+b,1); ) b
part(a+b,2); ) a
part(a-b,0); ) +
part(a-b,1); ) a
part(a-b,2); ) -b
part(-b,0); ) -
part(-b,1); ) b
part(a*b,0); ) *
part(a*b,1); ) a
part(a*b,2); ) b
part(a/b,0); ) //
(именно так | два знака деления подряд!)
part(a/b,1); ) a
part(a/b,2); ) b
part(f(x),0); ) f
38

part(f(x),1); ) x
part(a+b+c,0); ) +
part(a+b+c,1); ) c
part(a+b+c,2); ) b
part(a+b+c,3); ) a
part(a*b*c,0); ) *
part(a*b*c,1); ) a
part(a*b*c,2); ) b
part(a*b*c,3); ) c
part((a+b)/(c+d),0); ) //
part((a+b)/(c+d),1); ) a+b
part((a+b)/(c+d),2); ) c+d
part((a+b)/(c+d),2,2);
=) c
Очень полезны при выделении частей выражения функции " rst", "rest" и
"last".
first(x+y+z); ) z
rest(x+y+z); ) y+x
last(x+y+z); ) x
Существуют и специальные функции для выделения частей выражения.
 Функция lhs
выделяет левую часть уравнения
lhs(a+b=c+d); ) a+b
 Функция rhs
выделяет правую часть уравнения
rhs(a+b=c+d); ) c+d
 Функция num
выделяет числитель
num((a+b)/(c+d)); ) a+b
num( a+b ); ) a+b
 Функция denom
выделяет знаменатель
denom((a+b)/(c+d)); ) c+d
denom( a+b ); ) 1
Для модификации выражений можно применять функции "map", "apply"
и "subst".
39

Сумма является списком своих слагаемых, так что
map(factor, m/(x^2+2*x*y+y^2)+
n/(x+y)^4); ) m
(x+y) 2 +
n
(x+y) 4
Одним из имен функции сложения является символ "+". Так что
apply("+",[a,b]); ) a+b
Функция "subst" видит все части выражения, в том числе и с номером "0".
Поэтому
subst("+","[",[a,b]);
=) a+b
 Функция pickapart
позволяет разложить выражение на части вплоть до указанного уровня.
c(23) v1:pickapart( (a+b)/2+
sin(c)-d^2,1);
(e23) -d
2
(e24) sin(c)
(e25)
b+a
2
(d25) e23+e24+e25
После этого можно упрощать не "v1", а работать с его частями | с перемен-
ными "e23", "e24" и "e25", поскольку переменная "v1" все равно выражается
через них:
e25:77$
ev(v1); ) -d
2
+sin(c)+77
 Функция isolate
позволяет отделить часть выражения, которая содержит указанную пе-
ременную, от части, которая ее не содержит
(c5) isolate(x*x+a*x+b*x+a+b+1,x);
(e5) a+b+1
(d5) x 2
+ax+bx+e5
40

Подстановки в выражениях
Синтаксические подстановки
 Функция subst
реализует "чисто синтаксическую" подстановку. Ее аргументы идут в
таком порядке: новое значение, старое значение, выражение, в котором произ-
водится подстановка. Эта функция не понимает, что x 4 | это x 2 x 2 , поэтому
в выражении
subst(y,x,x^2+x^4+x^5);
=) y 2
+ y 4
+ y 5
подстановка выполняется полностью, в выражении
subst(y^2,x^2,x^2+x^4+x^5);
=) y 2 + x 4 + x 5
выполняется частично, а в выражении
subst(m,x+y,x+y+z); ) x+y+z
не выполняется вовсе.
Подстановка возможна не только для имен переменных, но и для имен
функций
subst(gg,ff,ff(7+ff(5));
=) gg(7+gg(5))
 Переменная exptsubst
запрещает или разрешает "степенные" подстановки. Изначально уста-
новлено значение "false", в результате
subst(y,%e^x,%e^x+%e^(a*x)+
%e^(a*x+b*x) ); ) y+%e ax
+%e ax+bx
Если установить значение "true", то
exptsubst:true$
subst(y^2,x^2,x^2+x^4+x^5);
=) y 2
+ y 4
+ x 5
subst(y,%e^x,%e^x+%e^(a*x)+
%e^(a*x+b*x) ); ) y+y a +%e ax+bx
 Функция ratsubst
работает так же, как subst, но понимает, что x 4 | это x 2  x 2 , так что
ratsubst(y^2,x^2,x^2+x^4+x^5);
41

=) y 2
+ y 4
+ xy 4
ratsubst(m,x+y,x+y+z);
=) z+m
ratsubst( m,3*x*y,
expand((x+y)^3) );
=) x 3 + mx + my + y 3
Алгебраические подстановки
В рациональных выражениях существует особый класс подстановок, вы-
полнение которых инициируется флагом "algebraic" в функции "ev".
Сначала задается одна или несколько подстановок.
 Функция tellrat
аддитивно добавляет алгебраическую подстановку к уже определенным
подстановкам и печатает весь их список.
tellrat(z=y^3); ) [z-y 3
]
tellrat(w^3=x); ) [w 3
-x,z-y
3
]
tellrat(); ) [w 3
-x,z-y
3
]
Для реализации подстановок выражение должно быть рациональным
(должно быть снабжено меткой /R/), и к нему надо применить функцию "ev"
с флагом "algebraic":
ev(z^2+z^3,algebraic);
=) z 3 +z 2
rat(%); ) /r/ z 3 +z 2
ev(%,algebraic); ) /r/ y 9
+y 6
ev(rat(w^3+w^6),algebraic);
=) /r/ x 2
+x
42

Подстановки по шаблону
Аппарат подстановок по шаблону в MAXIM'е заметно уступает по удоб-
ству работы и продуманности синтаксиса аналогичному аппарату в REDUCE.
Более того, по сути единственным реальным усовершенствованием Mathemat-
ic'и по сравнению с MAXIM'ой является удобный аппарат подстановок по ша-
блону. Если в REDUCE достаточно написать
for all x,y let sin(x+y)=sin(x)*cos(y)+cos(x)*sin(y);
for all x,n such that numberp(n) and n>1 let sin(n*x) =
sin((n-1)*x)*cos(x)+cos((n-1)*x)*sin(x);
чтобы задать правила преобразования выражений типа "sin(a+b+3*с)", то в
MAXIM'е это потребует значительно больших усилий.
Зато вариантов функций, реализующих подстановки по шаблону в MAX-
IM'е гораздо больше, и все они работают по-разному.
Прежде всего, следует определить шаблон.
 Функция matchdeclare
определяет шаблон, удовлетворяющий тому или иному условию.
matchdeclare(a,true)$
matchdeclare(b,true)$
matchdeclare(n,numberp)$
matchdeclare(m,matrixp)$
после этого шаблоны "a" и "b" означают "что угодно", шаблон "n" означа-
ет "любое число" (т.е. любой объект, который при подстановке в функцию
"numberp" дает "true"), шаблон "m" означает "любая матрица".
К сожалению, в отличие от REDUCE и Mathematic'и, запись "a+b" не
означает теперь "любая сумма". Чтобы определить шаблон "любая сумма",
придется проделать следующее
summap(x):=block([],if atom(x) then
return(false), if part(x,0)="+" then
return(true) else return(false))$
matchdeclare(anys,summap)$
Аналогично, чтобы определить шаблон "число, большее единицы", следу-
ет написать
num_g_1(x):=block([],if not numberp(x) then
return(false), if x>1 then
return(true) else return(false))$
matchdeclare(nnn,num_g_1)$
Теперь существует три возможности.
43

Во-первых, можно определять правила и применять их.
 Функция defrule
определяет "правило". Ее аргументы | имя правила, старое выражение,
новое выражение.
defrule( ru1, fff(m),
m^^(-3) + m.m^^(-1) );
=) ru1 : f(m) --> m^^(-3) + m.m^^(-1)
defrule( ru2, ff(n), n+x^n );
=) ru2 : (n) --> n+x^n
defrule( ru3, f(a), a+a^2 );
=) ru3 : f(a) --> a+a^2
defrule( ru4, sin(anys),
sin(first(anys))*cos(rest(anys)) +
cos(first(anys))*sin(rest(anys)) )$
defrule( ru5, cos(anys),
cos(first(anys))*cos(rest(anys)) -
sin(first(anys))*sin(rest(anys)) )$
 Функция disprule
печатает правила.
(c5) disprule(ru2,ru3);
(e5) ru2 : (n) --> n+x^n
(e6) ru3 : f(a) --> a+a^2
(d6) done
 Функция apply1
применяет указанные правила к выражению. Правила применяются
"сверху вниз" (от более высокого уровня в выражении к более мелким его ча-
стям), притом на каждом уровне выражения сначала целиком (пока выражение
не перестанет меняться) отрабатывается первое правило, потом второе, и т.д.
 Функция applyb1
отличается от "apply1" тем, что тот же самый алгоритм подстановок
применяется "снизу вверх".
 Функция apply2
отличается от "apply1" тем, что на каждом уровне выражения целиком
отрабатывается весь указанный список правил.
ma1:matrix([1/2,0],[0,1/2])$
apply1(fff(ma1)*fff(333),ru1);
44

=)

9 0
0 9

* f(333)
apply1(ff(5)+ff(y),ru2);
=) 5+x 5 + (y)
apply1(f(sin(z)^2),ru3);
=) sin(z) 2
+ sin(z) 4
apply1(cos(x+y+z),ru4,ru5);
=) (cos(x)cos(y)-sin(x)sin(y))cos(z) -
sin(y+x)sin(z)
apply1(%,ru4,ru5);
=) (cos(x)cos(y)-sin(x)sin(y))cos(z) -
(cos(x)sin(y)+sin(x)cos(y))sin(z)
apply1(cos(x+y+z),ru5,ru4,ru5);
=) (cos(x)cos(y)-sin(x)sin(y))cos(z) -
(cos(x)sin(y)+sin(x)cos(y))sin(z)
apply2(cos(x+y+z),ru4,ru5);
=) (cos(x)cos(y)-sin(x)sin(y))cos(z) -
(cos(x)sin(y)+sin(x)cos(y))sin(z)
apply1b(cos(x+y+z),ru4,ru5);
=) cos(y+x)cos(z)-sin(y+x)sin(z)
Как уже было сказано, попытка работать с шаблоном "a+b" ("любая сум-
ма") или с "a+nnn" ("что угодно плюс число, большее единицы") "a*n" ("что
угодно умножить на число") некорректны и вызывают предупреждения об
ошибке:
defrule(ru6,f(a+b), g(a)+g(b) );
a b partitions sum
ru6: f(a+b) --> g(a)+g(b)
defrule(ru7,f(a+nnn), g(a)+g(nnn) );
a nnn partitions sum
ru7: f(a+nnn) --> g(a)+g(nnn)
defrule(ru8,f(n*a), n*g(a) );
n a partitions product
ru8: f(n*a) --> n g(a)
В строгом смысле эти записи действительно некорректны: "a" | это уже "что
угодно", так что "a+b" это просто "a", точно так же "a+nnn" и "a*n" | это
"a". Если Вы проигнорируете эти предупреждения, то начнутся чудеса:
apply2(f(5),ru7); ) g(0)+g(5)
45

Этот результат вполне логичен, хотя и нежелателен. Впрочем, иногда
правило "ru7" работает так, как задумано:
apply2(f(x+5),ru7); ) g(x)+g(5)
apply2(f(x+1),ru7); ) f(x+1)
Для правила "ru6" ситуация еще хуже:
apply2(f(x+y),ru6); ) g(x+y)+g(0)
apply2(f(x),ru6); ) g(x)+g(0)
apply2(f(0),ru6); ) 2 g(0)
Корректный способ определить шаблон "что угодно умножить на число,
большее единицы", выглядит так:
pronum(x):=block([aaa],
if atom(x) then return(false),
if not part(x,0)="*" then return(false),
aaa:first(x),
if not numberp(aaa) then return(false),
if aaa>1 then return(true) else return(false) )$
matchdeclare(ppp,pronum)$
После этого можно определять правило преобразования выражений типа
"sin(3x)":
defrule(ru7,sin(ppp),
sin(rest(ppp))*cos((first(ppp)-1)*rest(ppp))+
cos(rest(ppp))*sin((first(ppp)-1)*rest(ppp)) )$
Разумеется, это крайне неудобно по сравнению с REDUC'ом или с Math-
matic'ой.
Во-вторых, можно определить безымянные let-правила, которые накапли-
ваются аддитивно
 Функция let
определяет let-правило.
let( fff(m), m^^(-3) +
m.m^^(-1) );
=) f(m) --> m^^(-3) + m.m^^(-1)
let( ff(n), n+x^n );
=) (n) --> n+x^n
let( f(a), a+a^2 ); ) f(a) --> a+a^2
 Функция remlet
отменяет определенные ранее правила. При этом
remlet(ff(n))$
отменит только указанное правило, а
46

remlet(all)$
отменит все определенные к настоящему моменту правила.
 Функция letsimp
применяет к своему аргументу все известные на данный момент let-
правила. Правила отрабатываются целиком, т.е. до тех пор, пока выражение
не перестанет меняться.
letsimp(ff(5)+ff(y)+f(sin(z)^2);
=) 5 + x 5
+ (y) + sin(z) 2
+ sin(z) 4
В-третьих, можно довести то или иное правило до сведения основной функ-
ции, упрощающей выражения. В этом случае все замены будут выполняться
автоматически, без каких либо действий со стороны пользователя.
 Функция tellsimp
вводит правило в базу данных основной функции, упрощающей выраже-
ния.
tellsimp( sin(anys),
sin(first(anys))*cos(rest(anys)) +
cos(first(anys))*
sin(rest(anys)) );
=) [sinrule1, simp-%sin]
tellsimp( cos(anys),
cos(first(anys))*cos(rest(anys)) -
sin(first(anys))*
sin(rest(anys)) );
=) [cosrule1, simp-%cos]
cos(x+y+z);
=) (cos(x)cos(y)-sin(x)sin(y))cos(z) -
(cos(x)sin(y)+sin(x)cos(y))sin(z)
47

Работа с действительными числами
В общем MAXIMA старается работать с бесконечной точностью:
exp(1); ) %e
Однако, ее можно заставить работать и с действительными числами. Дей-
ствительные числа машинной точности (как правило 16 знаков) записываются
обычным образом:
2.5 -1.0e20 5.768e-34
Действительные числа неограниченной точности обязаны иметь показатель
степени "b":
2.5b0 -1.0b20 5.768b-34
 Переменная fpprec
определяет количество значащих цифр для чисел неограниченной точ-
ности. Изначально она равна 16.
exp(1.0); ) 2.7182818284590451
exp(1.0b0); ) 2.718281828459045b0
fpprec:21; ) 21
exp(1.0b0);
=) 2.71828182845904523536b0
 Функция float
конвертирует любые числа в выражениях в числа машинной точности.
float(1/3); ) 0.3333333333333333
float(f(1/3)); ) f(0.3333333333333333)
float(%e); ) %e
float(%pi); ) %pi
float(1.0b0); ) 1.0e0
float(sin(%pi/6); ) 0.5
 Функция bfloat
конвертирует любые числа в выражениях в числа неограниченной точ-
ности. При этом, если встречается действительное число машинной точности,
она печатает предупреждение о изменении точности:
bfloat(1/3); ) 0.3333333333333333b0
bfloat(%e); ) 2.718281828459045b0
bfloat(%pi); ) 3.141592653589793b0
bfloat(exp(1.0));
Warning: oat to big oat conversion
of 2.7182818284590451
2.718281828459045b0
48

bfloat(sin(%pi/6); ) 0.5b0
Существует и обратное преобразование. Каноническая форма рациональ-
ного выражения не должна содержать действительных чисел. Поэтому функ-
ция "ratsimp" преобразует любое действительное число в рациональное и пе-
чатает при этом предупреждение.
 Переменная ratepsilon
задает точность преобразования действительного числа в рациональное.
Изначально установлено значение 2:0  10 8 .
ratsimp(x+1.23456789);
RAT replaced 1.2345678900000001 by
100//81=1.2345679012345678
81x+100
81
ratepsilon:0.001$
ratsimp(x+1.23456789);
RAT replaced 1.2345678900000001 by
21//17=1.2352941176470589
17x+21
17
Существует целый набор функций, который реализует не аналитические,
а численные алгоритмы, и выдает в качестве ответов действительные числа.
 Функция allroots
функция, которая выдает все (в том числе и комплексные) корни поли-
номиального уравнения с действительными либо комплексными коэффициен-
тами.
 Переменная polyfactor
определяет форму выдачи функции "allroots". Изначально она равна
"false", при этом корни выводятся в виде списка. Если установить ее равной
"true", то вместо списка корней будет в общем случае выводиться разложе-
ние полинома на линейные сомножители. Для полинома с действительными
коэффициентами будет выводиться разложение на линейные сомножители для
действительных корней и на квадратичные для каждой из пар сопряженных
друг другу комплексных корней.
allroots(x^2-3*%i*x-2=0);
=) [x=%i + 1.504632769052528e-36,
x=2.0*%i - 1.504632769052528e-36]
49

allroots(x^6+1=x^2+x^4);
=) [x=%i-1.132509520258601e-19,
x=-1.0%i-1.132509520258601e-19,
x=-1.0, x=-1.0, x=1.0, x=1.0]
polyfactor:true; ) true
allroots(x^6+1=x^2+x^4);
=) (x-1.0) 2
(x+1.0) 2
(x 2
+2.2650190405172025e-19x+1)
allroots(x^2-3*%i*x-2=0);
=) (x-%i - 1.504632769052528e-36)
(x-2.0*%i + 1.504632769052528e-36)
 Функция realroots
функция, которая выдает действительные корни полиномиального урав-
нения с действительными коэффициентами.
 Переменная multiplicities
после выполнения функции "realroots" содержит список кратностей кор-
ней.
realroots(x^6+1=x^2+x^4);
=) [x=-1,x=1]
multiplicities; ) [2,2]
 Функция nroots
функция, которая выдает количество действительных корней полиноми-
ального уравнения с действительными коэффициентами, которые локализова-
ны в указанном интервале
nroots(x^6+1=x^2+x^4,minf,inf);
=) 4
nroots(x^6+1=x^2+x^4,0,inf);
=) 2
 Функция interpolate
находит корень уравнения на заданном интервале методом деления от-
резка пополам.
6*interpolate(sin(x)=0.5,x,0,1);
=) 3.141592653589795
 Функция newton
находит корень указанной функции на заданном интервале методом Нью-
50

тона. Перед первым вызовом этой функции необходимо загрузить пакет "new-
ton", в котором она определена
load(newton)$
6*newton(sin(x)-0.5,x,0,1);
=) 3.141592653589795
 Функция romberg
численно находит определенный интеграл функции на заданном отрезке.
При этом используется алгоритм Ромберга, т.е. экстраполяция интегральных
сумм с шагом h 1 = h 0 =2, h 2 = h 1 =2, h 3 = h 2 =2, : : : в точку h1 = 0.
 Переменная rombergtol
задает относительную точность, которая должна быть достигнута при
интегрировании. Изначально установлено значение 1.0e-4. Это разумный
выбор, т.к. фактическая точность алгоритма Ромберга обычно заметно больше
заказанной
romberg(cos(x)^2,x,0,2*%pi)-
4.0*atan(1.0); ) -4.2479656877873199e-9
rombergtol:1.0e-11$
romberg(cos(x)^2,x,0,2*%pi)-
4.0*atan(1.0);
=) -7.8179366199075434e-17
 Переменная rombergit
задает максимальное количество итераций. Изначально устанавлива-
ется значение 11. Если требуемая точность за это количество итераций не
достигается, выдается сообщение об ошибке и происходит выход из системы.
rombergtol:1.0e-11$
errcatch(romberg(sin(x)/
(x+0.01)^2,x,0,20));
Romberg failed to converge
[ ]
rombergit:20$
errcatch(romberg(sin(x)/
(x+0.01)^2,x,0,20));
[1.4978968613751638]
Если Вы считаете нужным вычислять определенный интеграл, пользуясь
именно MAXIM'ой (в действительности это лучше сделать на языке "C"), то
подынтегральную функцию следует откомпилировать, причем непременно с
декларацией типа функции и аргумента.
51

Комплексные числа и выражения
Мнимая единица в MAXIM'е записывается как "%i". С ее помощью можно
конструировать комплексные выражения:
a+%i*b
 Функция realpart
возвращает действительную часть выражения
realpart(a+%i*b); ) a
 Функция imagpart
возвращает действительную часть выражения
imagpart(a+%i*b); ) b
 Функция cabs
возвращает модуль комплексного выражения
cabs(a+%i*b); ) sqrt(a 2
+b 2
)
 Функция carg
возвращает фазу комплексного выражения
carg(a+%i*b); ) atan2(b,a)
Как видно из приведенных примеров, по умолчанию все переменные счи-
таются действительными. Можно декларировать, что та или иная переменная
является комплексной:
declare(z,complex); ) done
Эта информация записывается в базу данных и является свойством перемен-
ной.
 Функция properties
печатает свойства переменной
properties(z);
=) [database info,kind(z,complex)]
 Функция remove
удаляет свойство переменной
remove(z,complex); ) done
properties(z); ) [ ]
Если "z" декларирована как комплексная переменная, то функции типа
"realpart" это учитывают, например:
realpart(z^2); ) realpart(z) 2
-imagpart(z)
2
52

 Функция rectform
приводит выражение к виду Re(z) + i  Im(z)
rectform(r*exp(%i*fi));
=) %i sin( ) r + cos( ) r
 Функция polarform
приводит выражение к виду mod(z)  exp(i  arg(z))
polarform(x+%i*y);
=) sqrt(x 2
+y 2
) %e
%i atan2(y,x)
 Функция exponentialize
заменяет все тригонометрические функции на соответствующие комби-
нации экспонент
exponentialize(cos(a+%i*b));
=) %e
%i(a+%ib)
+ %e
-%i(a+%ib)
2
 Функция demoivre
заменяет все экспоненты с мнимыми показателями на соответствующие
тригонометрические функции
demoivre(exp(a+%i*b) ) %e
a
(cos(b) + %i sin(b))
Дифференцирование и пределы
Дифференцирование
 Функция diff
выполняет дифференцирование. Ее синтаксис довольно разнообразен
diff(x^2,x) ) 2 x
diff(x^3,x,2) ) 6 x
diff(y^3 * x^3,x,2,y,1)
=) 18 x y 2
При этом первоначально все переменные считаются независимыми
diff(y,x) ) 0
Работать с производной одной переменной по другой можно тремя способа-
ми: "заморозить" операцию дифференцирования, указать на зависимость явно
и декларировать зависимость неявно. Запретить выполнение функции "di "
53

и получить "замороженную" производную можно, если перед именем функции
"di " поставить одиночную кавычку:
'diff(y,x) ) dy
dx
Можно явно указать на зависимость, т.е. работать с функцией:
diff(y(x),x) ) d
dx (y(x))
diff(v(x,y),x,2,y,1)
=) d 3
dx 2
dy
(v(x,y))
(здесь предполагается, что функции "y" и "v" не были ранее определены с
помощью ":=").
 Функция depends
позволяет декларировать, что переменная зависит от одной или несколь-
ких других переменных
depends(y,x); ) [y(x)]
depends(u,[x,y]); ) [u(x,y)]
 Переменная dependecies
содержит список "зависимостей", определенных на данный момент
dependecies; ) [y(x),u(x,y)]
Зависимость "u" от "x" и "y" является "свойством" переменной "u".
 Функция properties
печатает список свойств заданной переменной
properties(u); ) [dependency]
 Функция remove
удаляется указанное свойство данной переменной
remove(u,dependency);
=) done
dependecies; ) [y(x)]
Интересно, что MAXIMA правильно учитывает зависимости переменных
для случая вложенных функций:
diff(v(x,y),x,1,y,1)
=) d 2
dx dy (v(x,y))
54

diff(u,x,1,y,1) ) d 2
u
dy 2
dy
dx
+ d 2
u
dx dy
(как нетрудно понять, обе записи абсолютно корректны математически).
 Функция gradef
определяет результат дифференцирования функции по своим аргумен-
там
gradef(f(a,b,c),g(a,b,c),
77,c*f(a,b,c)); ) f(a,b,c)
тем самым определено, что
@
@a
f(a; b; c) = g(a; b; c);
@
@b
f(a; b; c) = 77; @
@c
f(a; b; c) = c  f(a; b; c)
(здесь предполагается, что функция "f" не была ранее определена с помощью
":=").
Градиент функции, как и зависимость переменных, есть свойство:
properties(f) ) [gradef]
 Функция prinprops
печатает указанное свойство данной переменной
printprops(f,gradef);
d
da
(f(a,b,c)) = g(a,b,c)
d
db (f(a,b,c)) = 77
d
dc (f(a,b,c)) = c f(a,b,c)
Следует заметить, что информация о зависимости переменных может
быть распечатана с помощью переменной "dependencies", поэтому функция
"prinprops" отказывается ее печатать. Так что
prinprops(y,dependency);
вызовет сообщение об ошибке.
55

N-кратное дифференцирование
 Функция gendiff
| функция N-кратного дифференцирования, где N | переменная.
Перед первым применением этой функции необходимо загрузить пакет
"gendif"
load(gendif)$
gendiff(exp(a*x),x,n);
=) a n
%e
a x
gendiff(x^a,x,n); ) genfact(a,n,1) x a-n
Функция не слишком умна | приведенные примеры и их комбинации в
сущности исчерпывают класс производных, которые могут быть вычислены.
Даже при логарифм функция уже не знает:
gendiff(log(x),x,n); ) d n
dx n (log(x))
При подстановке вместо "n" натурального числа функция возвращает
обычную функцию дифференцирования, правда, в "замороженном" виде. Ини-
циировать вычисление производной можно с помощью флага "di " в функции
"ev":
y:gendiff(exp(x^2),x,n)$
y:ev(y,n=2) ) d 2
dx 2 %e
x 2
ev(y,diff) ) 4 x 2
%e
x 2
+ 2 %e
x 2
Пределы
 Функция limit
вычисляет предел заданного выражения при стремлении переменной к
указанному значению. В тех случаях, когда левый и правый предел не совпа-
дают, можно уточнить, с какой стороны берется предел. Существует четыре
специальные значения | "inf" (+1), "minf" (1), "und" (1), "ind" (неопре-
деленность):
limit(sin(x)/x,x,0); ) 1
limit(1/x,x,0); ) und
limit(1/x,x,0,plus); ) inf
56

limit(1/x,x,0,minus);
=) minf
limit(sin(1/x),x,0); ) ind
limit((x+1)/(x+2),x,inf);
=) 1
Функция применяет правило Лопиталя.
 Переменная lhospitallim
ограничивает число дифференцирований при вычислении предела, при-
чем если этого количества дифференцирований не хватает, то функция воз-
вращает саму себя. Изначально установлено значение "4", поэтому
limit((sin(x)-x)^2/
(x^4*(cos(x)-1)),x,0);
=) limit
x->0
(sin(x)-x) 2
x 4 cos(x)-x 4
lhospitallim:16;
limit((sin(x)-x)^2/
(x^4*(cos(x)-1)),x,0);
=) -
1
18
Вообще функция реализована не очень аккуратно, примером чему может
служить такая выкладка
lhospitallim:16;
limit((sin(x)-x)^2/x^6,x,0);
=) 1
36
limit((cos(x)-1)^3/x^6,x,0);
=) -
1
8
limit((cos(x)-1)^3/
(sin(x)-x)^2,x,0);
=) -
9
2
limit((sin(x)-x)^2/
(cos(x)-1)^3,x,0);
=) inf
Последний ответ, очевидно, неверен.
 Функция tlimit
57

отличается от функции "limit" только алгоритмом | она раскладывает
выражение в ряд Тейлора. Благодаря этому она (в отличие от функция "limit")
выдает верный ответ и для случая
limit((sin(x)-x)^2/
(cos(x)-1)^3,x,0);
=) -
2
9
Интегрирование
 Функция integrate
выполняет интегрирование заданного выражения по указанной перемен-
ной (неопределеная константа не добавляется). Можно также указать пределы
интегрирования | в этом случае вычисляется определенный интеграл.
integrate(1/(x+a),x);
=) log(x+a)
integrate(x^3,x,a,b);
=) b 3
3 -
a 3
3
Определенный интеграл, зависящий от параметра, может быть по нему
продифференцирован
w:integrate(f(x,y),x,a(y),b(y));
=)
b(y) R
a(y)
f(x,y) dx
diff(w,y);
=) f(b(y),y) d
dy
(b(y)) - f(a(y),y) d
dy
(a(y))
+
b(y) R
a(y)
d
dy (f(x,y)) dx
 Функция changevar
реализует замену переменных в интеграле. Ее аргументы должны иметь
вид: сам интеграл, связь старой и новой переменной, новая переменная, старая
переменная. Связь переменных задается либо в явной форме ("x=g(t)"), либо в
виде выражения, один из корней которого и дает связь переменных ("x-g(t)").
w:integrate(f(x),x)$
58

changevar(w,x=g(t),t,x);
=) R
f(g(t)) d
dt
(g(t)) dt
changevar(w,x^2-g(t),t,x);
=) -
1
2
R f(-sqrt(g(t)))
sqrt(g(t))
d
dt (g(t)) dt
Для определенных интегралов выполняется подстановка в пределах ин-
тегрирования, так что функция, связывающая старую и новую переменную,
должна быть обратима. Поэтому попытка написать
w:integrate(f(x),x,a,b)$
changevar(w,x=g(t),t,x);
вызовет сообщение об ошибке ("g 1 (a)" и "g 1 (b)") не могут быть вычислены).
Однако, вполне допустимо
changevar(w,x=t+c,t,x);
=)
b-c R
a-c
f(t+c) dt
и
changevar(w,x^2-t, t, x);
=) -
1
2
b 2
R
a 2
f(-sqrt(t))
sqrt(t) dt
 Функция byparts
выполняет интегрирование по частям. Перед первым вызовом функции
необходимо загрузить пакет "bypart", в котором она определена:
load(bypart)$
Аргументы должны иметь вид: интеграл, переменная интегрирования,
" u " и " v 0 " (реализуется формула "
R
uv 0 = uv
R
v 0 u "). Функция не
слишком удачно реализована | она не отличает определенный интеграл от
неопределенного и всегда возвращает неопределенный, поэтому наборы опера-
торов
w:integrate(f(x)*cos(x),x)$ byparts(w,x,f(x),cos(x));
и
w:integrate(f(x)*cos(x),x,a,b)$ byparts(w,x,f(x),cos(x));
приведут к одной и той же выдаче
=) f(x)sin(x) -
R
sin(x) d
dx (f(x)) dx
59

Функция "integrate" может сообразить, что интеграл от производной про-
извольной функции есть сама функция
integrate(diff(u(x),x,2));
=) d
dx (u(x))
Но в более сложных слуаях он не замечает полной производной
integrate(diff(u(x),x,2)*sin(u(x))
+diff(u(x),x)^2*cos(u(x)),x);
=) R
(sin(u(x))
d 2
dx 2 (u(x))
+ (cos(u(x)) (
d
dx (u(x))) 2
) dx
 Функция antidiff
выполняет интегрирование вражений с произвольными функциями, пе-
ред ее первым вызовом следует загрузить пакет "antid"
load(antid)$
Разумеется, интегрирование выполняется только для случая полной производ-
ной
antidiff(diff(u(x),x,2)*sin(u(x))
+diff(u(x),x)^2*cos(u(x)),x);
=) sin(u(x))
d
dx (u(x))
Ряды и уравнения
Ряды, паде-аппроксимация и цепные дроби
 Функция taylor
раскладывает функцию в ряд Тейлора. Результат считается рядом и
снабжается меткой "/T/" сразу после метки "d". Аргументы функции таковы:
выражение, которое будет разложено, переменная, по которой идет разложение,
точка, в которой мы раскладываем и порядок, до которого идет разложение:
(c7) ta1:taylor(sin(x),x,0,3)
=) (d7) /t/ x -
x 3
6 + . . .
(c8) ta2:taylor(cos(x),x,0,6)
60

=) (d8) /t/ 1 -
x 2
2
+ x 4
24 -
x 6
720
+ . . .
Ряды можно складывать, вычитать, умножать и делить друг на друга,
при этом точность разложения учитывается автоматически, например
(c9) ta1/ta2; ) (d9) /t/ x +
x 3
3 + . . .
т.е. разложение идет до третьего порядка (точность первого ряда).
При этом фактически речь идет о ряде Лорана, т.е. допускается
taylor(sin(x)/x^3,x,0,5);
=) 1
x 2 -
1
6
+ x 2
120 -
x 4
5040
+ . . .
Более того, существует экзотическая возможность
taylor(exp(1/x),[x,0,3,'asymp]);
=) 1 + 1
x
+ 1
2x 2
+ 1
6x 3
+ . . .
 Функция pade
аппроксимирует отрезок ряда Тейлора до N-го порядка включительно
дробно-рациональной функцией. Ее аргументы | ряд Тейлора, порядок чи-
слителя n, порядок знаменателя m. Разумеется, количество известных ко-
эффициентов ряда Тейлора должно совпадать с общим количеством коэффи-
циентов в дробно-рациональной функции минус один, т.к. числитель и зна-
менатель определены с точностью до общего множителя. Иными словами,
N + 1 = (n + 1) + (m + 1) 1.
ta1:taylor(log(1+x),x,0,6);
=) x -
x 2
2 +
x 3
3 -
x 4
4 +
x 5
5 -
x 6
6 + . . .
pade(ta1,3,3) ) 11x 3 +60x 2 +60x
3x 3 +36x 2 +90x+60
pade(ta1,4,2) ) -
x 4
-12x
3
-150x
2
-180x
72x 2 +240x+180
 Функция cf
Создает цепную дробь, аппроксимирующую данное выражение. Выра-
61

жение должно состоять из целых чисел, квадратных корней целых чисел и
знаков арифметических операций. Выдача имеет вид списка.
 Переменная cflength
определяет количество периодов цепной дроби. Изначально установлено
значение 1.
 Функция cfdisrep
преобразует список (как правило выдачу функции "cf") в собственно цеп-
ную дробь.
cf(sqrt(3)); ) [1,1,2]
cfdisrep(%); ) 1+ 1
1+
1
2
float(%-sqrt(3.0)); ) 0.065384140902210657
cflength:2$
cf(sqrt(3)); ) [1,1,2,1,2]
cfdisrep(%); ) 1+
1
1+
1
2+
1
1+
1
2
cflength:5$
cf(sqrt(3)); ) [1,1,2,1,2,1,2,1,2,1,2]
cfdisrep(%)$
float(%-sqrt(3.0)); ) 1.7707912940423398e-6
Преобразование Лапласа и вычеты
 Функция laplace
реализует прямое преобразование Лапласа
laplace(exp(t),t,s); ) 1
s-1
laplace(sin(t),t,s); ) 1
s 2 +1
laplace(diff(x(t),t),t,s);
=) s*laplace(x(t),t,s)-x(0)
(последнее соотношение позволяет решать линейные дифференциальные урав-
нения).
62

 Функция ilt
реализует обратное преобразование Лапласа
ilt(1/(s-1),s,t); ) %e
t
ilt(laplace(x(t),t,s),s,t);
=) x(t)
(последнее свойство позволяет получать явные ответы при решении линейных
дифференциальных уравнений).
 Функция residue
позволяет вычислять вычеты
residue(1/(s^2+a^2),s,%i*a);
=) -
%i
2a
Уравнения
 Функция solve
решает уравнения и системы уравнений. Ее аргументы | список урав-
нений и список переменных, а выдача | список решений. При этом для урав-
нений с нулевой правой частью эту правую часть можно опускать
solve([x^2-3*x+2=0],[x]);
=) [x=1,x=2]
solve([x^2-3*x+2],[x]);
=) [x=1,x=2]
 Переменная multiplicities
содержит список кратностей корней, найденных функцией "solve"
solve([x^4-3*x^3+2*x^2=0],[x]);
=) [x=1,x=2,x=0]
multiplicities; ) [1,1,2]
Для систем уравнений решение | это список, составленный из значений
каждой из переменных, так что список решений представляет собой двойной
вложенный список:
solve([x+y=4,x-y=2],[x,y]);
=) [[x=3,y=1]]
(в данном случае решение одно).
 Переменная solveradcan
включает или выключает использование функции "radcan" при работе
63

функции "solve". Изначально установлено значение "false", т.е. функция "rad-
can" не используется (это ускоряет работу, но понижает вероятность получе-
ния аналитического решения).
 Функция eliminate
исключает из системы уравнений указанные переменные. Оставшиеся
уравнения приводятся к виду с нулевой правой частью, которая опускается:
eliminate([x+y+z=1,
x-y+z=2,x+y-z=3],[z]);
=) [2(y+x-2),-2y-1]
eliminate([x+y+z=1,
x-y+z=2,x+y-z=3],[x,y]);
=) [-2(z+1)]
 Функция algsys
отличается от "solve" тем, что решает исключительно алгебраические
системы уравнений. Она использует специальные методы решения алгебраи-
ческих систем, а в случае неудачи вызывает функцию "solve".
Дифференциальные уравнения
 Функция ode2
решает дифференциальные уравнения первого и второго порядков. Ее
аргументы | само дифференциальное уравнение в форме с "замороженной"
производной (т.е. с производной, вычисление которой запрещено с помощью
одиночной кавычки: 'diff(y,x)), функция и переменная. Неопределенные кон-
станты для уравнений первого порядка пишутся как "%C", а для уравнений
второго | как "%K1", "%K2".
 Переменная method
после работы функции "ode2" содержит указание на тип уравнения.
Функция распознает линейные уравнения первого порядка
ode2('diff(y,x)=2*y+exp(x),y,x);
=) y=(%c-%e -x
)%e 2x
method; ) linear
Функция распознает уравнения первого порядка c разделяющимися перемен-
ными
ode2('diff(y,x)=(x^2+1)*y^4,y,x);
64

=) -
1
3y 3
= x 3
+3x
3
+ %c
method; ) separable
Функция распознает точно интегрируемые уравнения первого порядка
ode2('diff(y,x)=(x^2+3*y^2)/
(2*x*y)2*y+exp(x),y,x);
=) y 2 + x 2
x 3 = %c
method; ) exact
Для уравнений этого типа вводится еще и
 Переменная intfactor
после работы функции "ode2" для уравнений типа "exact" содержит ин-
тегрирующий множитель
intfactor; ) 1
x 4
Функция распознает линейные неоднородные уравнения второго порядка
ode2('diff(y,x,2)-3*'diff(y,x)+
2*y=4*exp(3*x),y,x);
=) 2%e 3x + %k1 %e
2x + %k2 %e
x
method; ) variationofparameters
Для уравнений этого типа вводится еще и
 Переменная yp
после работы функции "ode2" для уравнений типа "variationofparame-
ters" содержит частное решение
yp; ) 2%e 3x
 Функция ic1
позволяет учесть начальное условие в решениях дифференциальных
уравнений первого порядка, ее аргументы | решение, значение "x" в виде
уравнения и соответствующее значение "y" тоже в виде уравнения.
ode2('diff(y,x)=2*y+exp(x),y,x);
=) y=(%c-%e -x
)%e 2x
ic1(%,x=0,y=1); ) y = 2%e 2x
- %e
x
65

 Функция ic2
позволяет учесть начальные условия в решениях дифференциальных
уравнений второго порядка, ее аргументы | решение, значение "x" в виде
уравнения и соответствующие значения "y" и "замороженной" производной
"y 0 " ('diff(y,x)) тоже в виде уравнения.
 Функция bc2
позволяет учесть краевые условия в решениях дифференциальных урав-
нений второго порядка, ее аргументы | решение, значение "x" в первой точке
в виде уравнения и соответствующее значение "y", значение "x" во второй
точке и соответствующее значение "y" тоже в виде уравнений.
re1:ode2('diff(y,x,2)-3*'diff(y,x)+
2*y=4*exp(3*x),y,x);
=) 2%e 3x + %k1 %e
2x + %k2 %e
x
ic2(re1,x=0,y=4,'diff(y,x)=9);
=) y = 2%e 3x
+ %e
2x
+ %e
x
bc2(re1,x=0,y=4,x=1,
y=exp(1)+exp(2)+2*exp(3) );
=) y = 2%e 3x
+ %e
2x
+ %e
x
Совершенно по-другому организована альтернативная функция, которая
также умеет решать дифференциальные уравнения, и, кроме того, системы
дифференциальных уравнений.
Она существенным образом использует свойство функций, которое назы-
вается "atvalue".
 Функция atvalue
позволяет задать значение функции и ее производных при некоторых
значениях аргументов.
atvalue(x(t),t=0,5); ) 5
atvalue(diff(x(t),t),t=0,55);
=) 55
atvalue(diff(x(t),t),t=1,77);
=) 77
atvalue(f(a,b),[a=0,b=1],5);
=) 555
atvalue(diff(f(a,b),b),
[a=1,b=0],777); ) 777
Эта информация является свойством функций "x(t)" и "f(a,b)".
66

 Функция properties
печатает свойства переменной
properties(x); ) [atvalue]
 Функция printprops
печатает информацию о заданом свойстве переменной
printprops(x,atvalue);
d
d@1
(x(@1))
@1=0
= 55
d
d@1
(x(@1))
@1=1
= 77
x(0)=5
printprops(f,atvalue);
d
d@2
(f(@1,@2))
@1=1, @2=0
= 777
f(0,1)=555
 Функция remove
отменяет указанное свойство переменной
remove(x,atvalue); ) done
properties(x); ) [ ]
 Функция at
вычисляет значение выражения в заданной точке с учетом свойства "at-
value".
at(x(t)+10*diff(x(t),t), t=0);
=) 555
at( f(a,b) + diff(f(a,b),b) ,
[a=1,b=0]); ) 777+f(1,0)
 Функция desolve
решает дифференциальные уравнения и системы дифференциальных
уравнений. Ее аргументы | список уравнений, в которых функции записа-
ны явно ("y(t)") и список неизвестных функций (также в виде "y(t)").
re2:desolve( [diff(y(t),t)=
2*y(t)+%e^t], [y(t)] );
=) y(t)=(y(0)+1)%e 2t
- %e
t
atvalue(y(t),t=0,1)$
67

ev(re2,at); ) y(t)=2 %e
2t
- %e
t
desolve( [diff(y(t),t)=
2*y(t)+%e^t], [y(t)] );
=) y(t)=2%e 2t
- %e
t
atvalue(x(t),t=0,2)$
atvalue(y(t),t=0,0)$
desolve( [diff(x(t),t)=y(t),
diff(y(t),t)=x(t)],
[x(t),y(t)]);
=) [x(t)=%e t + %e
-t
, y(t)=%e t
- %e
-t
]
desolve( [diff(x(t),t,2)
-3*diff(x(t),t)
+2*x(t)],[x(t)]);
=) x(t) = %e
2t ( d
dt
(x(t))
t=0
-x(0)) +
%e
t (2x(0) -
d
dt
(x(t))
t=0
)
Транслятор и компилятор в MAXIMA
Определив ту или иную функцию, можно заметно ускорить ее выполнение,
если ее оттранслировать или откомпилировать.
При выполнении определенной Вами функции MAXIMA каждый раз зано-
во выполняет те действия, которые входят в определение функции, т.е. факти-
чески разбирает соответствующее выражение на уровне синтаксиса MAXIM'ы.
 Функция translate
транслирует функцию MAXIM'ы на язык LISP.
f(x):=1+x+x^2+x^3+x^4+x^5+x^6+x^7$
translate(f); ) [f]
После этого функция как правило начинает считаться быстрее. Однако
следует иметь в виду, что оттранслированную функцию нельзя уничтожить
(даже функцией "kill").
 Функция compile
сначала транслирует функцию MAXIM'ы на язык LISP, а затем компи-
лирует эту функцию LISP'а до двоичных кодов и загружает их в память.
68

compile(f);
Compiling gazonk0.lsp
End of Pass 1.
End of Pass 2.
OPTIMIZE levels: Safety=0 (No runtime
error checking), Space=0, Speed=3
Finished compiling gazonk0.lsp
Loading gazonk0.o
Finished loading gazonk0.o
[f]
После этого функция как правило начинает считаться еще быстрее, чем
после трансляции. Компилированную функцию тоже нельзя уничтожить функ-
цией "kill".
Следует иметь в виду, что как при трансляции, так и при компиляции
MAXIM'а старается оптимизировать функцию по скорости, не заботясь об ак-
куратности. Поэтому при работе с большими по объему функциями могут
возникнуть чудеса. В этом случае следует отказаться от трансляции или ком-
пиляции, либо переписать функцию.
Выигрыш во времени существенным образом зависит от типа машины,
от вида функции, от того, декларирован ли тип функции и ее аргумента при
определении функции, от типа аргумента, с которым вызывается функция.
Например, если определить функцию явным выражением
f(x):=1+x+x^2+x^3+x^4+x^5+x^6+x^7$
то при ее вызове с "аналитическим" аргументом типа "cos(3*a)" соотношение
времен (без трансляции, с трансляцией, с компиляцией) составит:
13.43 11.95 12.01
(приведенные цифры | это, разумеется, не абсолютное время счета в секун-
дах) а при вызове с числовым аргументом
3.16 4.00 4.08
К сожалению, не каждую функцию можно задать явно. Если определить
функцию с циклом
f(x):=block([s],s:1,for i:1 thru 7 do s:s+x^i,s)$
(она, разумеется, должна давать тот же ответ, что и предыдущая), то эта "не-
явная" функция будет считаться заметно медленнее, чем явная. Для функций
такого типа эффект от трансляции и компиляции гораздо заметнее.
При вызове с "аналитическим" аргументом
21.31 16.61 14.88
при вызове с числовым аргументом
8.19 6.30 4.65
69

Если предполагается использовать функцию только для работы с действи-
тельными числами (например для вычисления определенного интеграла с по-
мощью функции "romberg" или поиска корня с помощью функций "interpolate"
и "newton"), то обязательно следует декларировать тип аргумента и самой
функции как " oat". Это во много раз усилит эффект от трансляции или
компиляции. Делается это так:
f(x):=block([],mode_declare([function(f),x],float),
1+x+x^2+x^3+x^4+x^5+x^6+x^7)$
(эту запись следует воспринимать аксиоматически).
В результате времена работы с числовым аргументом будут равны
3.44 2.75 1.21
т.е. реальный выигрыш по скорости в 2.5 раза.
Для функции с циклом следует определить еще и тип локальной перемен-
ной:
f(x):=block([s],mode_declare([function(f),x,s],float),
s:1, for i:1 thru 7 do s:s+x^i,s)$
Времена работы будут равны
9.68 5.47 1.26
т.е. реальный выигрыш по скорости в 6.5 раз.
70