Origin | Русская версия

Calc - Java калькулятор для мобильных телефонов и MIDP устройств

Примеры программ

В листингах программ для наиболее простых команд пишется только их обозначение, например "ENTER", "*", "0,5". Для команд, расположенных на более глубоких уровнях меню, дополнительно пишется короткая подсказка поясняющая, где искать эту команду. При этом в подсказке содержится не весь путь к команде, а только короткое напоминание, например "stack/RCL st# 1" соответствует последовательным нажатиям "main/special/stack/more/RCL st#/<0-3>/<1>".

Если программа во время своей работы использует данные из стека, то перед началом ввода такой программы полезно поместить в стек какие-либо значения, чтобы уже во время записи иметь возможность отследить результаты ее работы.

Т.к. калькулятор позволяет независимо задавать масштаб осей x и y, то на некоторых телефонах может наблюдаться вытягивание графиков вдоль той или иной оси, даже при задании одинаковых пределов построения вдоль обеих осей, если при этом область построения на экране телефона отличается от квадратной.

Список примеров

  1. Построение графиков
  2. Решение квадратного уравнения
  3. Решение уравнений относительно разных переменных
  4. Преобразования углов
  5. Биоритмы
  6. Аппроксимация нестандартной функцией
  7. Построение сплайнов
  8. Построение окружности
  9. Построение сферы
  10. Фрактал Мандельброта
  11. Линейное программирование
  12. Еще о построение кривых

Построение графиков

Многие просили меня объяснить подробнее, как выполняется такая простая вещь как построение графика функции. Если говорить коротко, то функция – это такая программа, которая использует нижний регистр стека (x) в качестве аргумента, затем выполняет над этим аргументом какие-либо преобразования и возвращает результат этих преобразований опять же в нижний регистр стека.

Сейчас мы запрограммируем и построим графики двух разных по степени сложности функций. Вначале в качестве примера выберем простейшую функцию
График функции f(θ) = θ
Изображение функции f(z) = z
График функции
f(x) = 2x2 - 3x - 1

f(x) = x

Давайте подумаем, как должна выглядеть программа для вычисления такой функции. Когда мы вызываем команду построения графика, эта команда последовательно помещает в регистр x различные случайные числа, и каждый раз вызывает нашу программу, чтобы та выполнила необходимые действия над этими числами. Т.к. в первом примере функция просто равна самому аргументу, то нам вообще не требуется выполнять никаких действий над x. Поэтому, чтобы запрограммировать такую функцию нам потребуется выполнить следующие команды:
prog/new "f1"
prog/finish
Перед тем как строить график необходимо еще задать область его построения (максимальные и минимальные значения осей координат). Мы будем использовать интервал значений -10<x<10 и -10<y<10. Для этого введем в стек:

-10
10
-10
10

После ввода пределов вызовем, наконец, уже саму команду построения графика:

prog/draw/y=f(x)/f1

Ну что ж, как и следовало ожидать, получилась обычная прямая. Тем не менее, из этой же программы можно извлечь кое-что более интересное, используя другие режимы построения графиков. Оставив те же самые пределы построения, вызовите следующие команды:

prog/draw/r=f(θ)/f1 (углы в радианах)
и
prog/draw/z=f(z)/f1

и полюбуйтесь на результат.

В качестве второго примера выберем более сложный случай. Давайте попробуем построить график функции

f(x) = 2x2 - 3x - 1

Первая проблема, которую нам предстоит преодолеть – это то, что по ходу вычисления функции нам нужно дважды использовать значение x, которое изначально содержится в стеке только в одном экземпляре. Что тут можно предложить? Ну во-первых можно использовать ячейку памяти для временного хранения x (команда "mem/STO") и когда это значение нам потребуется вызвать его командой "mem/RCL". Но мы сделаем проще: продублируем регистр x командой ENTER. Ниже приведен один из вариантов программы для вычисления нашей функции. Перед началом записи программы очистим стек и введем в x какое-нибудь формальное входное значение, например 1, чтобы иметь возможность отслеживать промежуточные результаты вычислений (текст после точки с запятой – это просто комментарии):

stack/clear             ; очистка стека
1                       ; ввод формального аргумента (если ты реально крут, можешь ввести 2)
prog/new "f2"           ; теперь непосредственно начинаем ввод нашей новой программы
ENTER                   ; копирование x. Теперь у нас два одинаковых числа в стеке
math/simple/x2          ; вычисление x2, израсходовалась одна из копий числа
2                       ; ввели цифру 2
*                       ; посчитали 2x2
stack/x<=>y             ; спустили вниз вторую копию числа
3                       ; ввели цифру 3
*                       ; теперь в стеке есть 2x2 и 3x
-                       ; вычли 3x из 2x2, в стеке теперь 2x2-3x
1                       ; ввели цифру 1
-                       ; вычли 1 из 2x2-3x, теперь в регистре x значение нужной нам функции 2x2 - 3x - 1
prog/finish             ; заканчиваем ввод программы
Теперь давайте еще раз проверим, правильность работы нашей программы. Очищаем стек и вводим число 3, затем нажимаем "prog/run/f2". На дисплее 3-ка должна смениться на 8-ку, т.е. мы посчитали, что f(3)=8.

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

Решение квадратного уравнения

Составим программу для решения уравнений второй степени

ax2 + bx + c = 0

При составлении программы используется несколько необычный алгоритм который, однако, позволяет увеличить точность нахождения одного из корней при b2 >> 4ac, более подробно здесь. Перед запуском программы поместите в стек значения коэффициентов a, b, и c. На выходе получим те же коэффициенты плюс корни уравнения (действительные или комплексные).

Листинг программы:
prog/new "Quad"         ; начало ввода программы, присваиваем ей имя
stack/RCL st# 1         ; вызываем b в регистр x
math/simple/x2          ; вычисляем b2
stack/RCL st# 3         ; вызываем a в регистр x
stack/RCL st# 2         ; вызываем c в регистр x (a сдвигается в y, b2 – в z)
*                       ; вычисляем a*c
4                       ; вводим число 4
*                       ; вычисляем 4ac
-                       ; вычисляем b2-4ac
math/simple/√x          ; извлекаем корень из b2-4ac
stack/RCL st# 2         ; вызываем b в регистр x (т.к. стек увеличился, индекс изменился)
prog/util/sgn           ; вычисляем знак b
*                       ; вычисляем sgn(b)*√b²-4ac
stack/RCL st# 2         ; еще раз вызываем b в регистр x
+                       ; складываем b с произведением знака на корень
-2                      ; вводим число -2
/                       ; вычисляем промежуточное число q = -(b+sgn(b)*√b²-4ac )/2
stack/RCL st# 1         ; вызываем c в регистр x
stack/RCL st# 1         ; вызываем q в регистр x
/                       ; находим первый корень уравнения, c/q
stack/x<=>y             ; опять помещаем q в регистр x
stack/RCL st# 4         ; вызываем a в регистр x
/                       ; находим второй корень уравнения, q/a
prog/finish             ; окончание ввода программы

Решение уравнений относительно разных переменных

Допустим, у нас есть уравнение с несколькими переменными, например уравнение равноускоренного движения (падения) тела из начальной точки с координатой (высотой) h0, с ускорением a и начальной скоростью v0. И нам нужно узнать высоту h в момент времени t. Как известно из физики, это уравнение выглядит так:

h = h0 + v0t + ½at2

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

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

0 = h0 + v0t + ½at2 - h

Выделим для хранения каждой переменной отдельную ячейку памяти, просто занесем значения переменных в память в том порядке, в котором они встречаются в нашем уравнении:

M1=h0, M2=v0, M3=t, M4=a, M5=h

Для ячейки M0 отведена особая роль, она будет определять относительно какой переменной мы хотим решить наше уравнение, так если M0=3, то это будет означать, что мы хотим решить уравнение относительно третьей переменной, т.е. относительно t.

Далее, переключитесь в режим мониторинга памяти ("mode/monitor/mem/<4-7>/<6>") и введите исходные данные, например:

M0=3, M1=100, M2=10, M4= -9,80665, M5=0

(Величину ускорения свободного падения 9,80665 можно взять из встроенных констант "special/conv/const/astro/gn", знак минус перед g означает, что ускорение направлено в сторону уменьшения координаты, т.е.вниз)

Теперь введем наше уравнение в виде программы:
prog/new "Speed"        ; вводим имя программы
mem/RCL 0               ; определяемся относительно какой переменной будем решать уравнение
prog/mem/STO[x]         ; помещаем значение величины из стека в соответствующую ячейку памяти
clear                   ; удаляем из стека номер переменной...
clear                   ; ... и ее значение
mem/RCL 1               ; начало решения, вызываем h0
mem/RCL 2               ; вызываем v0
mem/RCL 3               ; вызываем t
*                       ; вычисляем v0*t
+                       ; вычисляем h0 + v0*t
mem/RCL 4               ; вызываем a
mem/RCL 3               ; вызываем t
math/simple/x2          ; вычисляем t2
*                       ; вычисляем a*t2
2                       ; вводим число 2
/                       ; вычисляем a*t2/2
+                       ; вычисляем h0 + v0*t + a*t2/2
mem/RCL 5               ; вызываем h
-                       ; вычисляем h0 + v0*t + a*t2/2 - h
prog/finish             ; конец программы
Теперь, используя модуль решения уравнений, можно найти какое значение времени t соответствует другим введенным переменным. Перед началом решения нам нужно определиться с диапазоном поиска. Введем, например
0                       ; нижняя граница поиска решения
100                     ; верхняя граница поиска решения
prog/more/solve/Speed   ; вызов модуля решения уравнений
Результат показывает, что после 5,65 секунд свободного падения нас ждет довольно жесткая посадка :)

Если мы вдруг захотим узнать какая начальная скорость необходима чтобы мяч, находившийся на высоте 100 метров через 1 секунду оказался на высоте 50 м, нам потребуется изменить соответствующие значения в ячейках памяти:

M0=2 (теперь решаем относительно начальной скорости скорости M2=v0); M1=100; M3=1; M4= -9,80665; M5=50
0                       ; нижняя граница поиска решения
100                     ; верхняя граница поиска решения
prog/more/solve/Speed   ; вызов модуля решения уравнений
То, что в результате мы получаем предупреждающую надпись и ответ nan наводит на мысль, что в этот раз мы ошиблись с выбором интервала поиска решения. Чтобы определиться с границами поиска, построим график:

0
100
0
100
prog/draw/y=f(x)/Speed

Взглянув на него можно понять, что решение следует искать среди отрицательных значений v0. Попробуем так:
-100                    ; нижняя граница поиска решения
0                       ; верхняя граница поиска решения
prog/more/solve/Speed   ; вызов модуля решения уравнений
Получаем решение что-то около -45,1. Т.е. нам нужно изначально придать мячу довольно большую скорость, направленную вниз.

Как вы уже догадались, самым трудным в этом примере было запомнить в какой ячейке памяти что хранится. Режим мониторинга памяти делает вычисления нагляднее, но все равно возможно вам потребуется записать что-нибудь на бумаге.

Пример прислал Goetz Schwandtner

Преобразования углов

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

Листинги:
prog/new "→DMS"         ; как обычно, начинаем с ввода имени программы
ENTER                   ; делаем копию входного значения
math/misc/int/trunk     ; выделяем целую часть
stack/x<=>y             ; помещаем в регистр x вторую копию входных данных
math/misc/int/frac      ; выделяем дробную часть
conv/time/→DH.MS        ; конвертируем десятые доли градусов в градусы, минуты и секунды
+                       ; восстанавливаем целую часть
prog/finish             ; конец программы

 
prog/new "→D"           ; новая программа
ENTER                   ; делаем копию входного значения
math/misc/int/trunk     ; выделяем целую часть
stack/x<=>y             ; помещаем в регистр x вторую копию входных данных
math/misc/int/frac      ; выделяем дробную часть
conv/time/→H            ; конвертируем минуты и секунды в десятые доли градуса
+                       ; опять прибавляем целые градусы
prog/finish             ; конец программы


prog/new "DMS+"         ; новая программа
ENTER                   ; делаем копию первого аргумента
math/misc/int/frac      ; выделяем дробную часть
stack/x<=>y             ; помещаем в регистр x копию первого аргумента
math/misc/int/trunk     ; выделяем целую часть
stack/x<=>st# 2         ; помещаем в регистр x второй аргумент
ENTER                   ; делаем копию второго аргумента
math/misc/int/frac      ; выделяем дробную часть
stack/x<=>y             ; помещаем в регистр x копию второго аргумента
math/misc/int/trunk     ; выделяем целую часть
stack/x<=>st# 2         ; помещаем в регистр x дробную часть первого аргумента
conv/time/DH.MS+        ; суммируем дробные части, как минуты и секунды
+                       ; и прибавляем к ним ...
+                       ; ...оставшиеся целые части
prog/finish             ; конец программы
Примечание: если в вашем телефоне можно ввести знак "»" в имени программы, то впоследствии он превратится в знак "→". Соответственно программам можно давать имена "»D.MS" и "»D" вместо "->D.MS" и "->D".

Биоритмы

Одно время наряду с гороскопами популярностью пользовалась теория биоритмов. Давайте составим программы для вычисления этого дела. При написании этой программы будем использовать свойство калькулятора отображать на графике действительную часть комплексного числа линией розового цвета, мнимую часть – линией желтого цвета, а точки в которых действительная и мнимая части совпадают – белым цветом. А также тот факт, что если выходные значения вычисляются по чередующимся функциям, то можно построить несколько графиков одновременно. Эта программа предназначается только для построения графика, если запускать ее в обычном режиме, будут просто выдаваться различные числа, не имеющие особого смысла.

Листинг программы:
prog/new "BioRt"        ; вводим имя программы
conv/time/now           ; получаем текущее время
mem/RCL 0               ; вызываем день рождения в регистр x
+/-                     ; меняем знак
conv/time/DH.MS+        ; вычитаем день рождения из текущей даты
conv/time/→H            ; вычисляем число часов, прошедших со дня рождения
24                      ; вводим число 24 (что бы оно могло значить?)
/                       ; вычисляем число дней, прошедших со дня рождения
+                       ; прибавляем временное смещение по оси x (автоматически помещается в стек модулем построения графиков)
trig/π                  ; вводим число пи
ENTER                   ; копируем число пи
+                       ; вычисляем 2*π
*                       ; умножаем разность дат на 2*π для перевода в радианы
ENTER                   ; делаем копию
ENTER                   ; теперь 3 копии в стеке
23                      ; вводим число 23 (продолжительность физического цикла)
/                       ; вычисляем число циклов, прошедших со дня рождения
trig/sin                ; строим синусоиду 23-х дневного цикла
stack/x<=>y             ; помещаем в регистр x вторую копию подготовленной даты в радианах
28                      ; вводим число 28 (продолжительность эмоционального цикла)
/                       ; вычисляем число циклов, прошедших со дня рождения
trig/sin                ; строим синусоиду 28-ми дневного цикла
trig/coord/r->cplx      ; слепляем комплексное число, re=эмоц., im=физ.
stack/x<=>y             ; помещаем в регистр x третью копию даты
33                      ; вводим число 33 (продолжительность интеллектуального цикла)
/                       ; вычисляем число циклов, прошедших со дня рождения
trig/sin                ; строим синусоиду 33-х дневного цикла
ENTER                   ; копируем регистр x
trig/coord/r→cplx       ; делаем комплексное число, такое что re=im=инт.
mem/RCL 1               ; вызываем содержимое 1-й ячейки памяти (в ней при запусках чередуются 0 и 1)
+/-                     ; меняем знак
1                       ; вводим число 1
+                       ; инвертируем содержимое ячейки
mem/STO 1               ; сохраняем инвертированное значение в ячейке памяти
prog/util/select        ; выбираем значение регистра y или z в зависимости от содержимого x
prog/finish             ; конец программы
Биоритмы
Чтобы запустить программу, поместите дату своего рождения в ячейку памяти #0, поместите число 0 или 1 в ячейку памяти #1, переведите калькулятор в режим RAD и задайте подходящий диапазон построения, например -14; 14; -1,2; 1,2. Затем выполните команду prog/draw/y=f(x)/BioRt. Программа построит график, включающий в себя 14 дней до и после текущей даты. Физический цикл показан желтым цветом, эмоциональный – розовым, а интеллектуальный соответственно белым. Начало координат соответствует текущему времени. Напомню, что в соответствии с форматом даты, принятым в программы, день рождения следует вводить как ггггммддчч.ммсс. Если вы не знаете час минуту и секунду своего рождения можете вводить там что хотите, например нули, но помните, что нули стоящие перед десятичной точкой отбрасывать нельзя. Допустим, если вы хотите ввести 15 февраля 1989 года, то в ячейку #0 следует поместить число 1989021500.

Аппроксимация нестандартной функцией

Допустим, у нас есть набор точек, полученных в результате каких-либо измерений и нам нужно найти формулу кривой, которая наилучшим образом соответствовала бы этим точкам
   x  |   y
=============
   0  |   0
   5  |   6
  10  |  10
  20  |  17
  30  |  21
  50  |  27
Если построить эти точки то можно увидеть, что значения вначале увеличиваются практически линейно, но мало-помалу скорость роста начинает замедляться. Такую зависимость можно описать логарифмической функцией, например

y = a ln(b1x + 1)

Остается только найти коэффициенты, наиболее подходящие к имеющимся данным. На наше несчастье статистический модуль может подобрать коэффициенты лишь для функции вида y = a ln(x) + b. Поэтому пойдем на хитрость: перед вводом данных в статистику прибавим к x дополнительный поправочный коэффициент и получим кривую вида y = a ln(x + c) + b. Если при этом подобрать коэффициент c таким образом, чтобы c=e-b/a, то после преобразования получим, что y = a ln(x + c) + b = a ln(xeb/a + 1), т.е. фактически то, что нам и требовалось. Ну и потом останется только найти коэффициент b1 = eb/a. Следовательно, осмыслив вышесказанное, можно прийти к выводу, что нам нужно составить программу которая, используя экспериментальные данные и поправочный коэффициент c, будет находить значение ln(c) + b/a, используя которое в модуле решения уравнений мы сможем найти нужное нам ln(c) = -b/a (что тоже самое что и c=e-b/a).

Итак:
prog/new "fit"          ; вводим имя программы
mem/STO 0               ; записываем значение параметра c из регистра x в ячейку памяти #0
stat/clear              ; очищаем старые статистические данные
0                       ; вводим значение y1
ENTER                   ; -//-
0                       ; вводим значение x1
mem/RCL 0               ; вызываем параметр c в стек
+                       ; вычисляем x1+c
stat/Σ+                 ; записываем в статистику значения x1+c и y1
6                       ; вводим значение y2
ENTER                   ; -//-
5                       ; вводим значение x2
mem/RCL 0               ; вызываем параметр c в стек
+                       ; вычисляем x2+c
stat/Σ+                 ; записываем в статистику значения x2+c и y2
10                      ; вводим значение y3
ENTER                   ; -//-
10                      ; вводим значение x3
mem/RCL 0               ; вызываем параметр c в стек
+                       ; вычисляем x3+c
stat/Σ+                 ; записываем в статистику значения x3+c и y3
17                      ; вводим значение y4
ENTER                   ; -//-
20                      ; вводим значение x4
mem/RCL 0               ; вызываем параметр c в стек
+                       ; вычисляем x4+c
stat/Σ+                 ; записываем в статистику значения x4+c и y4
21                      ; вводим значение y5
ENTER                   ; -//-
30                      ; вводим значение x5
mem/RCL 0               ; вызываем параметр c в стек
+                       ; вычисляем x5+c
stat/Σ+                 ; записываем в статистику значения x5+c и y5
27                      ; вводим значение y6
ENTER                   ; -//-
50                      ; вводим значение x6
mem/RCL 0               ; вызываем параметр c в стек
+                       ; вычисляем x6+c
stat/Σ+                 ; записываем в статистику значения x6+c и y6
mem/RCL 0               ; вызываем параметр c в стек
math/pow/ln             ; вычисляем ln(c)
stat/result/alnx+b/a,b  ; вычисляем коэффициенты a и b
/                       ; вычисляем b/a
+                       ; и наконец, находим ln(c)+b/a
prog/finish             ; коней программы
Результат аппроксимации
Запустив эту программу с несколькими пробными значениями c, находим, что c=1 и c=20 находятся по разные стороны решения уравнения ln(c) + b/a = 0. Следовательно, водим в стек 1 и 20 и запускаем модуль решения уравнений "prog/solve/fit". На выходе имеем c=10,76295. Взглянув на полученную с помощью команды "stat/result/alnx+b/draw" кривую видим, что она действительно неплохо описывает наши экспериментальные данные (хотя и смещена вдоль оси x на значение поправочного коэффициента c = 10,76), неплохой результат аппроксимации подтверждает также значение коэффициента корреляции ("stat/result/alnx+b/r") 0,9996. Нужный нам изначально коэффициент b1, как уже говорилось, вычисляется на основе найденных a и b по формуле eb/a, коэффициент a уже и так найден в нужном нам виде (напомню, вычисленные коэффициенты вызываются в стек командой stat/result/alnx+b/a,b).

Таким образом, позволив программе модифицировать статистические данные, мы смогли аппроксимировать наши точки нестандартной кривой. И в итоге получаем нужную нам функцию:

y = 15,724 ln(0,092911x + 1)

Построение сплайнов

Дан двумерный массив точек. Через эти точки можно провести параметрическую кривую, состоящую из сегментов полиномов третьей степени x=f(t) и y=g(t). Эту кривую называют сплайном. Использование матриц может существенно упростить процесс его построения.

Листинг программы:
prog/new "Splin"        ; вводим имя новой программы
mem/RCL 0               ; вызываем из памяти матрицу контрольных точек
stack/x<=>y             ; помещаем параметр t в регистр x (t автоматически задается графическим модулем)
math/matrix/size        ; узнаем размер матрицы
clear                   ; теперь в регистре x - число строк (rows) матрицы, оно равно числу контрольных точек
3                       ; вводим число 3
-                       ; rows-3 = число сегментов кривой
*                       ; t*(rows-3) = текущий сегмент кривой
ENTER                   ; дублируем значение
math/misc/int/frac      ; t внутри текущего сегмента
mem/STO 2               ; сохраняем значение в ячейку #2
clear                   ; в регистре x опять t*(rows-3)
math/matrix/split       ; извлекаем узловые точки для текущего сегмента
stack/x<=>y             ; ненужную часть ...
clear                   ; ... удаляем
4                       ; теперь ...
math/matrix/split       ; ... выделяем ...
clear                   ; ... G = [ G0  G1  G2  G3 ]^T (узловые точки)
mem/RCL 2               ; извлекаем значение t
3                       ; возводим его ...
math/pow/yx             ; ... в куб
mem/RCL 2               ; опять извлекаем t
math/simple/x2          ; возводим в квадрат
mem/RCL 2               ; и еще раз извлекаем t
1                       ; единичка
math/matrix/concat      ; собираем...
math/matrix/concat      ; ... матрицу-строку ...
math/matrix/concat      ; ... T = [ t3  t2  t  1 ]
mem/RCL 1               ; извлекаем базовую матрицу сплайна M
*                       ; перемножаем ...
stack/x<=>y             ; ... и получаем ...
*                       ; [ x  y ] = T*M*G
-1                      ; извлекаем ...
math/matrix/split       ; ... значения x и y
stack/x<=>y             ; меняем их местами
trig/coord/r→cplx       ; конструируем комплексное число z = x+iy
prog/finish             ; заканчиваем ввод программы
Сплайн Катмулла-Рома
В-сплайн
Далее, перед запуском программы необходимо задать матрицу Nx2, содержащую узловые точки, и поместить ее в ячейку памяти #0. А также создать базовую матрицу сплайна размером 4x4 и сохранить ее в ячейке #1. Введите в стек подходящие пределы построения и запустите построение сплайна командой "prog/draw/z=f(t)/Splin".

Для примера возьмем матрицу узловых точек размером 34х2 (34 строки и 2 столбца)
/  2 22 \      |  ...  |
|  7 19 |      | 21  6 |
| 14 18 |      | 23  0 |
| 13 22 |      | 26  7 |
|  2 11 |      | 30 18 |
|  4  2 |      | 29 22 |
| 11  2 |      | 25  9 |
| 18  9 |      | 27  0 |
| 21  9 |      | 31  5 |
| 22  9 |      | 34  9 |
| 19  9 |      | 37  9 |
| 16  6 |      | 38  9 |
| 16  2 |      | 35  9 |
| 20  2 |      | 32  6 |
| 22  5 |      | 32  2 |
| 22  7 |      | 36  2 |
| 23 10 |      | 38  3 |
|  ...  |      \ 39  4 /
и одну из базовых матриц:
для сплайна Катмулла-Рома           для B-сплайна
 
     | -1  3 -3  1 |               | -1  3 -3  1 |
1/2* |  2 -5  4 -1 |          1/6* |  3 -6  3  0 |
     | -1  0  1  0 |               | -3  0  3  0 |
     |  0  2  0  0 |               |  1  4  1  0 |
Сплайн Катмулла-Рома проходит через узловые точки, но у него непрерывна только первая производная. B-сплайн часто проходит мимо узловых точек, но у него непрерывны первая и вторая производные, поэтому он выглядит более гладким.

Построение окружности

Существует несколько способов построения окружности или эллимса

1. Построение в полярных координатах окружности с центром в точке (0, 0):
prog/new/"a"            ; начало программы
clear                   ; удаляем входное значение угла, т.к. R=const
1                       ; величина радиуса
prog/finish             ; конец
Вводим в стек границы построения:
-1
1
-1
1
Запускаем построение командой prog/draw/r=f(phi)/"a"

2. Построение эллипса с центром в точке (5, 5) с помощью параметрической кривой:
prog/new/"b"            ; присваиваем программе имя
2                       ; умножаем входной параметр t на 2...
trig/π                  ; ...и на пи ...
*                       ; ... и получаем ...
*                       ; ... 2*π*t
ENTER                   ; копируем
trig/sin                ; вычисляем синус
3                       ; вводим значение радиуса по y
*                       ; умножаем на этот радиус
5                       ; координата центра по y
+                       ; добавляем к значению
stack/x<=>y             ; сейчас будем работать со второй координатой
trig/cos                ; вычисляем косинус
4                       ; вводим радиус по x
*                       ; умножаем на него
5                       ; вводим координату центра по x
+                       ; добавляем ее к значению
trig/coord/r→cplx       ; из координат текущей точки составляем комплексное число
prog/finish             ; конец программы
Опять вводим пределы построения:
0
10
0
10
На этот раз запускаем построение командой prog/draw/z=f(t)/"b"

3. Построение верхней и нижней полуокружности, объединенных в комплексное число:
На сей раз используем тот факт, что для комплексных чисел функция prog/draw/y=f(x) строит два отдельных графика разных цветов, один для действительной части, второй – для мнимой.
prog/new/"c"            ; начинаем ввод
math/x2                 ; окружность будет иметь радиус равный 1 ...
+/-                     ; ... поэтому в этих строчках программы ...
1                       ; ... делаем все вычисления ...
+                       ; ... в соответствии с формулой ...
math/√x                 ; ... y = √1-x²
ENTER                   ; копируем вычисленное значение
+/-                     ; для одной из копий меняем знак
trig/cord/r->cplx       ; из положительной и отрицательной половинок составляем комплексное число
prog/finish             ; конец программы
Задаем область построения
-1
1
-1
1
Запускаем построение графика prog/draw/y=f(x)/"c".

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

Вначале введем 0 в нулевую ячейку памяти:
0
mem/STO 0
prog/new/"d"            ; начало программы
math/x2                 ; опять окружность единичного радиуса ...
+/-                     ; ... и опять все вычисления ...
1                       ; ... производятся здесь ...
+                       ; ... в соответствии с формулой ...
math/√x                 ; ... y = √1-x²
ENTER                   ; копируем координату точки
+/-                     ; для одной из копий меняем знак
mem/RCL 0               ; вызываем содержимое ячейки памяти #0
+/-                     ; результатом действий ...
1                       ; ... выполняемых здесь будет ...
+                       ; ... инверсия содержимого памяти 0 <=> 1
mem/STO 0               ; возвращаем в память инвертированное значение
prog/util/select        ; теперь на основе 0 или 1 выбиратся содержимое регистра y или z
prog/finish             ; конец программы
Не забываем ввести пределы построения
-1
1
-1
1
Строим окружность prog/draw/y=f(x)/"d".

Дополнительный пример построения эллипса с использованием z = f(z).

Построение сферы

Изображение сферы
Построение сферы демонстрирует возможность создания 3D графика с использованием z = f(z). Для этого нам понадобится программа, реализующая комплексную функцию комплексного аргумента. При построении z = f(z) аргумент (угол) комплексного числа, возвращаемого программой, определяет цвет точки графика, а его модуль определяет яркость этой точки. Если f(z) имеет только действительную часть, тогда положительные значения отображаются на экране зеленым цветом, а отрицательные – фиолетовым. Имеет смысл ограничить модуль f(z) интервалом [0,1], поскольку на каждом из интервалов [j, j+1] для любого натурального числа j яркость точек циклически повторяется, и если не ввести ограничение, то на таком графике будет трудно определить закономерность распределения яркости точек. Поэтому в зависимости от конкретной задачи всегда следует выбирать подходящий масштаб.

Мы будем использовать функцию f(z) = √max{0, [1-abs²(z)]}, которая выдаст нам вполне симпатичную сферу:

Листинг программы:
prog/new "Spher"        ; начинаем ввод
                        ; проверьте, чтобы в стеке находилось комплексное число
                        ; иначе следующая команда будет недоступна
trig/cplx/abs           ; вычисляем модуль комплексного числа
math/simple/x2          ; находим квадрат модуля
1                       ; вычитаем …
-                       ; …квадрат модуля…
+/-                     ; … из единицы
0                       ; вводим ноль для сравнения
prog/util/max           ; определяем что больше: разность или ноль
math/simple/√x          ; из большего извлекаем корень
prog/finish             ; заканчиваем программу
Теперь вводим пределы построения
-1
1
-1
1
и запускаем команду prog/draw/z=f(z)/"Spher"

Фрактал Мандельброта

Еще один интересный пример применения z = f(z) – построение фрактала Мандельброта. Этот пример также демонстрирует использование цикла DSE. Желательно переключиться в режим мониторинга текста программы mode/monitor/prog, это поможет легче находить и исправлять возможные ошибки. (Более подробно о режиме мониторинга см. в руководстве).

Листинг программы:
prog/new/"mbrot"        ; начало программы
mem/STO 0               ; на самом деле в английском варианте
16                      ; нет ни одного комментария
mem/STO 1               ; к данной программе
clear                   ; так что не буду никого вводить в заблуждение
prog/flow/LBL 0         ; сами смотрите и разбирайтесь
x2                      ; предварительно можете
mem/RCL 0               ; почитать где-нибудь
+                       ; о множествах Мандельброта
prog/flow/DSE 1         ; в общем-то не составляет труда
prog/flow/GTO 0         ; восстановить по программе
prog/util/abs           ; используемую формулу
math/pow/log2           ; но самое главное – это понять смысл вычислений
math/pow/log2           ; как из простых итераций
ENTER                   ; появляются такие
16                      ; сложные объекты
/
trig/tanh               ; здесь гиперболический тангенс
trig/coord/p→r
trig/coord/r→cplx
prog/finish             ; конец программы
Фрактал Мандельброта
В конце программы происходит аппроксимация числа итераций из log2(log2(|z|)), затем на основе этого вычисляется новое комплексное число, чтобы придать изображению цветовую окраску. Единственной проблемой данного алгоритма является специфический способ вычисления цветов, ориентированный на выполнение 16 итерационных циклов, поэтому если вы решите увеличить число итераций, фрактал потеряет свою точность, станет менее детальным. Ну и наконец, поскольку мы научились управлять переходами, мы можем делать еще более изощренные вещи, такие как непосредственная установка черного цвета (0) для областей в которых множество сходится. Оставлю это вам в качестве упражнения. Построить фрактал можно используя границы типа:
-2
1
-1.5
1.5
и команду prog/draw/z=f(z)/"mbrot".

Линейное программирование

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

Цель: максимизировать x + y
Условия: x + 2y ≤ 70, 2x + y ≤ 50, x ≥ 0, y ≥ 0

Для решения задачи сначала приведем все неравенства к виду a1*x + a2*y ≤ b, где знаки ≥ и ≤ можно менять местами, умножая неравенства на ‑1. Теперь неравенства можно представить как матрицу A, каждая строка которой содержит неравенство A*(x,y) ≤ b. Что в свою очередь даст следующую стандартную формулировку задачи: max c*(x,y), при условии, что A*(x,y) ≤ b, где c и b – векторы и A – матрица с действительными компонентами.
    | 1  2 |            | 70 |
A = | 2  1 | ;      b = | 50 | ;      c = ( 1, 1 )
    |-1  0 |            |  0 |
    | 0 -1 |            |  0 |
А сейчас давайте построим множество решений этих неравенств, используя отображение функции z = f(z), где яркость точек пропорциональна величине x + y. Но сначала сохраните матрицу A в ячейку памяти #4, b в ячейку #5 и c (строка 1х2) в ячейку #6.

Листинг программы:
prog/new/"Simpl"        ; даем имя новой программе (сокращение от Simplex)
trig/complex/cplx→r     ; разделяем комплексное число x+yi на x и y (для доступа к команде в x должно быть комплексное число)
matrix/stack            ; преобразуем в вектор [x,y]
ENTER                   ; копируем для дальнейшего использования
mem/RCL 4               ; вызываем матрицу A
stack/x<=>y             ; вычисляем ...
*                       ; ... произведение A*[x,y]
mem/RCL 5               ; вызываем b
-                       ; приводим к виду A*[x,y] - b ≤ 0
matrix/a_max            ; находим максимальный компонент
stack/x<=>y             ; теперь ...
clear                   ; ... удаляем из стека A*[x,y] - b
prog/flow/x≤0           ; проверяем компонент на ≤ 0
prog/flow/GTO 1         ; если да, переходим к c*x
clear                   ; иначе: некоторые неравенства не прошли проверку
clear                   ; поэтому функция ...
0                       ; ... выдает 0
prog/flow/STOP          ; и завершает работу
prog/flow/LBL 1         ; сюда переходим если проверка прошла
clear                   ; удаляем элемент после проверки
mem/RCL 6               ; вызываем c
stack/x<=>y             ; вычисляем ...
*                       ; ... произведение c*x
0.025                   ; вводим масштабный коэффициент
*                       ; получаем 0,025*c*x
prog/finish             ; конец программы
Решение задачи линейного
программирования
Теперь вводим в стек
-5
40
-5
40
и смотрим графическое отображение результата решения задачи линейного программирования симплекс-методом (команда prog/plot/z=f(z)/"Simpl").

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

Примечание: масштабный коэффициент был введен для того, чтобы отображаемые значения находились в пределах [0, 1], т.к. на каждом интервале [j, j+1] для любого натурального числа j яркость точек циклически повторяется и если не ввести ограничение, то на таком графике не получится определить точку с максимальной яркостью. Поэтому в зависимости от конкретной задачи всегда следует выбирать подходящий масштаб для модуля f(z).

Еще о построение кривых

Построение кривой
с помощью z = f(z)
Кривые часто задаются в неявном виде, т.е. как геометрическое место точек с нулевым значением функции. Возьмем для примера функцию двух переменных x2+3y2-2x+3y-1=0. Сейчас мы увидим, как можно строить такие графики, используя z = f(z). Вот только вместо непосредственного построения f(x,y)=0, будем строить функцию в чуть-чуть измененном виде:
| x2 + 3y2- 2x + 3y - 1 | < ε,
где присвоим величине ε (эпсилон) какое-нибудь небольшое значение. В нашем примере возьмем ε=0,1. Перед началом ввода программы поместите в стек комплексное число, например 3+4i, иначе вы не сможете получить доступ к команде cplx→r, ну и к тому же это позволит вам более наглядно наблюдать за процессом вычислений.

Листинг программы:
prog/new "curve"        ; новая программа
monitor/prog/4          ; включение режима мониторинга (необязательно, но удобно)
trig/complex/cplx→r     ; разбиение комплексного x+iy на две координаты x и y
ENTER                   ; далее вычисляем x2+3y2-2x+3y-1
2
*
stack/x<=>y 
x2
+                       ; сейчас имеем x2+2x в регистре x
stack/x<=>y             ; теперь начинаем вычислять члены, содержащие y
ENTER
3
*
stack/x<=>y
x2
3
*
+                       ; теперь в стеке 3y2+3y 
+                       ; складываем это с членами, содержащими x
-1                      ; и ...
+                       ; ... прибавив -1 имеем всю функцию x2+2x+3y2+3y-1
prog/util/abs           ; вычисляем ее модуль
0.1                     ; вводим ε
prog/flow/x>y?          ; и сравниваем модуль функции с ε
prog/flow/GTO 1         ; если модуль функции < ε идем к LBL
clear                   ; иначе все зачищаем ...
clear                   ; ... и функция выдает ...
0                       ; ... ноль (черный цвет на графике)
prog/flow/STOP          ; программа завершает работу, на выходе 0
prog/flow/LBL 1         ; сюда попадаем, если модуль функции < ε
clear                   ; опять все ...
clear                   ; ... зачищаем
1                       ; функция возвращает 1 (зеленый цвет на графике)
prog/finish             ; конец программы
Ну и теперь можно построить наш эллипс, введя пределы
-3
1
-2
1
и запустив построение командой prog/draw/z=f(z)/"curve".

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

Больше примеров

Если Вы считаете, что придумали достаточно интересную программу, можете написать автору проекта (roarl at users sourceforge net) и может быть Ваша программа тоже появится здесь.



















Hosted by uCoz