Пример двадцать один. В этой задаче находим значение экспоненты, довольно интересная задача не только в учебных целях, но и в практических, не смотря на то что в Форте можно получить это значение просто набрав следующий код:
1E FEXP F.
2.7182818 Ok
: FOR21 ( I: N>0 -> ) \ 1/1!+1/2!+1/3!+...+1/N!
1+ 1 \ пределы параметра цикла от 1 до N
1E 1E \ F: -> 1 1 – первая единица – это текущая сумма ряда, вторая текущий ее член
DO
I 0 D>F F/ \ F: S 1 –> S 1/I
FSWAP FOVER \ S 1/I -> 1/I S 1/I
F+ FSWAP \ 1/I S 1/I -> S+1/I 1/I
LOOP FDROP F. ; \ удаляем текущий член (1/I) печатаем сумму (S+1/I)
\ Тест словом TEST-FOR21 проверим не только работу решения задачи, но и сходимость данного метода вычисления экспоненты.
: TEST-FOR21 11 1 DO I DUP . FOR21 CR LOOP ;
\ В итоге на экране получим следующий результат:
TEST-FOR21
1 2.0000000
2 2.5000000
3 2.6666667
4 2.7083333
5 2.7166667
6 2.7180556
7 2.7182540
8 2.7182788
9 2.7182815
10 2.7182818
Ok
Для N=10 получаем результат с точностью до семи знаков после запятой.
Пример двадцать два. Используем переменную X, иначе манипуляции на стеке будут неоправданно сложными, и наглядности никакой.
VARIABLE X
: FOR22 ( I: N>0 F: X -> ) \ 1+X/1!+X^2/2!+X^3/3!+...+X^N/N!
X F! \ сохраняем значение X в одноименной переменной
1+ 1 \ пределы параметра цикла от 1 до N
1E 1E \ F: -> 1 1 – первая единица – это текущая сумма ряда, вторая текущий ее член
DO
X F@ F* \ F: S 1 –> S 1*X
I 0 D>F F/ \ F: S 1*X –> S 1*X /I
FSWAP FOVER \ S 1*X/I -> 1*X/I S 1*X/I
F+ FSWAP \ 1*X/I S 1*X/I -> S+1*X/I 1*X/I
LOOP FDROP F. ; \ удаляем текущий член (1*X/I) печатаем сумму (S+1*X/I)
Отличается от предыдущего примера только добавление строчки один (переменная X) и семь (текущий член ряда в цикле каждый раз умножаем на X, перед очередным суммированием).
Тест словом TEST-FOR22 проверим работу FOR22 с параметром X=1E (частный случай, сводим задачу к предыдущей, получаем те же данные, проверяйте самостоятельно).
\ Теперь проанализируем с параметром, например, два.
: TEST-FOR22 21 1 DO I DUP . 2E FOR22 CR LOOP ;
TEST-FOR22
1 3.0000000
2 5.0000000
3 6.3333333
4 7.0000000
5 7.2666667
6 7.3555556
7 7.3809524
8 7.3873016
9 7.3887125
10 7.3889947
11 7.3890460
12 7.3890546
13 7.3890559
14 7.3890561
15 7.3890561
16 7.3890561
17 7.3890561
18 7.3890561
19 7.3890561
20 7.3890561
Ok
Видим, что сходимость хуже, при I=14, мы получаем примерный результат (почему-то с округлением). С вещественными числами будьте аккуратнее (они могут «чудить»).
Пример двадцать три. Первая строчка объявление вспомогательной переменной. Вторая стандартное начало – описание слова. Третья задаем пределы цикла. Четвертая после тройного дублирования получаем: первый параметр – это начальное значение суммы, второй члена суммы, третий квадрат X сохраняем в одноименную переменную, он нам понадобится для вычисления очередного члена ряда. С пятого по одиннадцатый цикл в котором вычисляем член ряда и прибавляем к значению суммы ряда. В шестой считываем значение переменной X, где хранится его квадрат, умножаем на (An) и меняем знак на противоположенный, в седьмой параметр цикла I умножаем на два, дублируем и увеличиваем на единицу, в итоге на вещественном стеке получаем (2*I) и (2*I+1), которые в восьмой умножаем друг на друга, а результат – делитель, для (–An*X^2) (в результате этих манипуляций получаем факториал в знаменателе). В девятой и десятой полученный текущий член прибавляем к сумме ряда, и возвращаем исходный порядок. В итоге, в двенадцатой, у нас готовы сумма ряда и его последний член, сбрасывая который, за не надобностью, печатаем итоговый результат.
: FOR23 ( F: X I: N>0 -> ) \ 1.5E 5 FOR23=SIN[1.5] X-X^3/3!+X^6/6!-...+[-1]^N*X^[2*N+1]/[2*N+1]!
1+ 1 \ пределы параметра цикла
FDUP FDUP FDUP F* X F! \ X -> X X , первый X – сумма ряда (S), второй ее член (An), X=X^2
DO
X F@ F* FNEGATE \ S An -> S –An*X^2
I 2* DUP 1+ 0 D>F 0 D>F \ S –An*X^2 -> S –An*X^2 2*I 2*I+1
F* F/ \ S –An*X^2 2*I 2*[I+1] -> S –An*X^2/{2*I*2*[I+1]}
FSWAP FOVER \ S -An*X^2/{2*N+1}! -> -An*X^2/{2*N+1}! S -An*X^2/{2*N+1}!
F+ FSWAP \ -An S -An -> S-An -An
LOOP
FDROP F. ; \ удаляем текущий член (1*X/I) печатаем сумму ряда
Тест на углах 0, 1,5 и 3,14 (Пи) радиан при количестве итераций пять. Поэкспериментируйте на других значениях самостоятельно.
0E 5 FOR23
0.0000000 Ok
1.5E 5 FOR23
0.9974949 Ok
FPI 5 FOR23
>-0.0004451 Ok
Пример двадцать четыре. Брат близнец предыдущего. Первый член заменяем на единицу (вместо X), аналогично поступаем с суммой ряда. Используем раннее объявленную переменную, все остальное без изменений. Используя математику можно было не писать слово FOR24, а используя формулы приведения, вычислить COS через SIN, правда думаю цель учебы набраться опыта в программировании, а не в демонстрации познаний в математике.
: FOR24 ( F: X I: N>0 -> ) \ 1.5E 5 FOR23=COS[1.5] 1-X^2/2!+X^4/4!-...+[-1]^N*X^[2*N]/[2*N]!
1+ 1 \ пределы параметра цикла
FDUP F* X F! 1E 1E \ X -> 1 1 , первый 1 – сумма ряда (S), второй ее член (An), X=X^2
DO
X F@ F* FNEGATE \ S An -> S –An*X^2
I 2* DUP 1- 0 D>F 0 D>F \ S –An*X^2 -> S –An*X^2 2*I 2*I-1
F* F/ \ S –An*X^2 2*I 2*I-1 -> S –An*X^2/{2*I*[2*I-1]}
FSWAP FOVER \ S -An*X^2/{2*I}! -> -An*X^2/{2*I}! S -An*X^2/{2*I}!
F+ FSWAP \ -An S -An -> S-An -An
LOOP
FDROP F. ; \ удаляем текущий член (1*X/I) печатаем сумму ряда
\ Тесты на вычисление косинуса углов 0, 3,14 (Пи) и 1,5 радиан, для параметра итерации равной пяти.
0E 5 FOR24
1.0000000 Ok
FPI 5 FOR24
-1.0018291 Ok
FPI 10 FOR24
-1.0000000 Ok
1.5E 5 FOR24
0.0707369 Ok
Пример двадцать пять. Вторая строка – сохранение входного параметра в одноименной переменной. В третьей, добавляем в вещественный стек ноль (начальное значение суммы) и минус один (начальное значения заготовки для вычисления «n»-ого члена). Четвертая готовим цикл и с пятой по девятую сам цикл, в котором считываем «X» умножаем на заготовку и меняем знак на противоположенный (для этого мы его вначале сделали минус один, чтобы получить первый член положительным, как в условии задачи). Далее дублируем и вычисляем сам член ряда, поделив на «I» (параметр итерации цикла), добавим к предыдущей сумме, восстанавливаем исходный порядок параметров. Все это для того чтобы не испортить заготовку для вычисления очередного элемента ряда, ибо по предыдущему будет не просто вычислить последующий.
: FOR25 ( |X|<1 N>0 -> ) \ 0.5E 5 FOR25 = ln[1+X] , X-X^2/2+X^3/3-...+[-1]^[N-1]*X^N/N
X F! \ сохраняем значение X в переменную
0E -1E \ 0 – сумма, -1 – первый член
1+ 1 \ пределы цикла
DO
X F@ F* FNEGATE \ S An -> S -An*X
FDUP I 0 D>F F/ \ S -An*X -> S -An*X -An*X/I
FROT F+ FSWAP \ S -An*X -An*X/I -> S-An*X/I -An*X
LOOP FDROP F. ; \ удаляем текущий член печатаем сумму ряда
Парочка тестов:
0.1E 10 FOR25
0.0953101 Ok
0.2E 10 FOR25
0.1823215 Ok
0.3E 10 FOR25
0.2623641 Ok
\ А теперь проверим вычислив данные значения стандартными средствами Форта.
1.1E FLN F.
0.0953101 Ok
1.2E FLN F.
0.1823215 Ok
1.3E FLN F.
0.2623642 Ok
Если кому-то не понравится отличие в последней цифре при вычислении «0.3E 10 FOR25», то можете увеличить число элементов ряда так при N=20 получаем одинаковый результат. Поэкспериментируйте самостоятельно на различных данных насколько быстро сходится ряд (и какое значение N достаточно в разных случаях).
Пример двадцать шесть. Ряд очень быстро сходится. В строке два первые два «FDUP» дают начальное значение суммы и заготовки для «n»-ого члена ряда, который получаем умножением на квадрат «X» сохраненной в одноименной переменной, и делением на (2*I+1), последующие два «FDUP» позволяют получить квадрат «X», после умножения. Все остальной стандартно, как и ранее, подготовка пределов параметра цикла и сам цикл, в котором, заготовку «n»-ого члена умножаем на квадрат «X», дублируем и делим на (2*I+1), получив непосредственно член ряда (так как из предыдущего члена не получить следующий, вследствие того, что в знаменателях разные числа), затем прибавляем к предыдущему значению суммы и возвращаем исходный порядок параметров на стеке.
: FOR26 ( |X|<1 N>0 -> ) \ 0.5E 5 FOR25 = ln[1+X] , X-X^3/3+X^5/5-...+[-1]^N*X^(2*N+1)/ (2*N+1)
FDUP FDUP FDUP F* X F! \ X -> X X , 1-ый X сумма, второй - первый член, X=X^2
1+ 1 \ пределы цикла
DO
X F@ F* FNEGATE \ S An -> S -An*X^2
FDUP I 2* 1+ 0 D>F F/ \ S -An*X^2-> S -An*X^2 -An*X^2/(2*I+1)
FROT F+ FSWAP \ S -An*X^2 -An*X^2/(2*I+1) -> -An*X^2/(2*I+1)+S -An*X^2
LOOP FDROP F. ; \ удаляем текущий член печатаем сумму ряда
\ Напоследок парочка тестов примера 26:
0.1E 5 FOR26
0.0996686 Ok
0.2E 5 FOR26
0.1973955 Ok
0.3E 5 FOR26
0.2914567 Ok
Разумеется, эти значения можно вычислить непосредственно одной операцией (соответствующей встроенной функцией) ЯП Форт. Именно таким образом мы проверим корректность работы написанного слова.
0.1E FATAN F.
0.0996686 Ok
0.2E FATAN F.
0.1973955 Ok
0.3E FATAN F.
0.2914567 Ok
Пример двадцать семь. В третьей строке готовим начальное значение суммы ряда и заготовки для первого члена, также записали в переменную «X» значение его квадрата, так как каждый член ряда больше предыдущей на это число мы его используем. Далее идет стандартный цикл с установкой его пределов. В теле цикла умножаем заготовку для каждого элемента ряда на «X^2» (шестая строка), на «2*I-1» (седьмая строка), делим на «2*I» (восьмая строка), дублируем и делим на «2*I+1» (девятая строка), так как это действие для каждого члена своя мы не меняем заготовку (чтобы «не испортить» его), затем, как и ранее складываем с текущей суммой и восстанавливаем порядок параметров на стеке. Одиннадцатая строка выводит текущие значения элемента ряда и ее суммы, так можно увидеть, как быстро сходится ряд (если надоест поиграться ее можно удалить).
: FOR27 ( |X|<1 N>0 -> ) \ 0.5E 5 FOR25
\ arcsin[X]=X+1*X^3/(2*3)+1*3*X^5/(2*4*5)+...+1*3*…*(2*N-1)*X^(2*N+1)/ (2*4*…*(2*N)*(2*N+1))
FDUP FDUP FDUP F* X F! \ X -> X X , 1-ый X сумма, 2-ой – заготовка первого члена
1+ 1 \ пределы цикла
DO
X F@ F* \ S An -> S An*X^2
I 2* 1- 0 D>F F* \ S An*X^2 -> S An*X^2*(2*I-1)
I 2* 0 D>F F/ FDUP \ S An*X^2*(2*I-1) -> S An*X^2*(2*I-1)/(2*I) An*X^2*(2*I-1)/(2*I)
I 2* 1+ 0 D>F F/ \ -> S An*X^2*(2*I-1)/(2*I) An*X^2*(2*I-1)/(2*I*{2*I+1})
FROT F+ FSWAP \ -> An*X^2*(2*I-1)/(2*I*{2*I+1})+S An*X^2*(2*I-1)/(2*I)
FDUP F. FOVER F. CR \ выводим текущие значения «An» и суммы
LOOP FDROP F. ; \ удаляем текущий член печатаем сумму ряда
\ Для «Пи/4» мы получим следующее:
FPI 4E F/ 30 FOR27
0.2422365 0.8661436
0.1120677 0.8885572
0.0576075 0.8967868
0.0310933 0.9002416
0.0172619 0.9018109
0.0097606 0.9025617
0.0055908 0.9029345
0.0032331 0.9031246
0.0018835 0.9032238
0.0011037 0.9032763
0.0006499 0.9033046
0.0003842 0.9033200
0.0002278 0.9033284
0.0001355 0.9033331
0.0000808 0.9033357
0.0000482 0.9033371
0.0000289 0.9033380
0.0000173 0.9033384
0.0000104 0.9033387
0.0000062 0.9033389
0.0000037 0.9033389
0.0000022 0.9033390
0.0000013 0.9033390
0.0000008 0.9033390
0.0000005 0.9033391
0.0000003 0.9033391
0.0000001 0.9033391
0.0000001 0.9033391
0.0000000 0.9033391
0.0000000 0.9033391
0.9033391 Ok
Немного поисследовав, легко определяем где очередной элемент суммы становится нулем и не влияет на дальнейшею сумму. Теперь вычислим несколько значений arcsin для следующих углов:
0.1E 30 FOR27
0.1001674 Ok
0.2E 30 FOR27
0.2013579 Ok
0.3E 30 FOR27
0.3046926 Ok
Вычислим те же значения стандартными средствами Форта и докажем корректность работы нашего с вами (легко написанного слова) слова:
0.1E FASIN F.
0.1001674 Ok
0.2E FASIN F.
0.2013579 Ok
0.3E FASIN F.
0.3046926 Ok
Пример двадцать восемь. В третьей строке сохраняем X, заносим две единицы, в качестве суммы и первого члена. Далее уже знакомый цикл. Каждый раз член ряда умножаем на «X» и меняем поочередно знак на противоположенный, умножаем на «2*I-3» и делим на «2*I». Восстанавливаем порядок, после получения текущей суммы. В одиннадцатой строке закомментирован код, который выводил в цикле текущие значения суммы и элемента ряда (можете посмотреть самостоятельно как плохо сходится ряд и сколько итераций необходимо для приемлемой точности, когда X приближается к единице). Обычно не рекомендуется комментировать код, некоторые программисты могут считать это дурным тоном, но во время отладки – это очень удобный прием (включать и выключать отдельные функции).
: FOR28 ( |X|<1 N>0 -> ) \ 1E 10 FOR28
\ SQRT[1+X]=1+X/2-1*X^2/(2*4)+1*3*X^3/(2*4*6)-...+(-1)^(N-1)1*3*…*(2*N-3)*X^(N)/ (2*4*…*(2*N))
X F! 1E 1E \ X -> 1 1 , 1-ый сумма, 2-ой – заготовка первого члена
1+ 1 \ пределы цикла
DO
X F@ F* FNEGATE \ S An -> S -An*X
I 2* 3 – S>D D>F F* \ S -An*X -> S -An*X*(2*I-3)
I 2* S>D D>F F/ \ S -An*X*(2*I-3) -> S -An*X*(2*I-3)/(2*I)
FSWAP FOVER \ S -An*X*(2*I-3)/(2*I) -> -An*X*(2*I-3)/(2*I) S -An*X*(2*I-3)/(2*I)
F+ FSWAP \ -> S-An*X*(2*I-3)/(2*I) -An*X*(2*I-3)/(2*I)
\ FDUP F. FOVER F. CR \ выводим текущие значения «An» и суммы
LOOP FDROP F. ; \ удаляем текущий член печатаем сумму ряда
\ Стандартные тесты для корней из 1,1; 1,2; 1,5; 1,9 – чем ближе к единице, тем хуже точность при одних и тех же количествах итераций:
0.1E 10 FOR28
1.0488088 Ok
0.2E 10 FOR28
1.0954451 Ok
0.5E 10 FOR28
1.2247421 Ok
0.9E 10 FOR28
1.3769956 Ok
\ Те же корни, полученные стандартными средствами Форт:
1.1E FSQRT F.
1.0488088 Ok
1.2E FSQRT F.
1.0954451 Ok
1.5E FSQRT F.
1.2247449 Ok
1.9E FSQRT F.
1.3784049 Ok
\ Для корня из двух получаем следующие данные:
2E FSQRT F.
1.4142136 Ok
1E 10 FOR28
1.4099312 Ok
1E 50 FOR28
1.4138177 Ok
1E 100 FOR28
1.4140730 Ok
1E 500 FOR28
1.4142010 Ok
1E 1000 FOR28
1.4142091 Ok
1E 5000 FOR28
1.4142132 Ok
1E 10000 FOR28
1.4142134 Ok
1E 20000 FOR28
1.4142135 Ok
Чтобы получить шесть точных знаков после запятой, понадобилось порядка 10000 итераций.
Пример двадцать девять. В строке два вычисляем разницу (B-A) – длина отрезка. В третьей N переводим в вещественный стек. В четвертой вычисляя частное (B-A)/N, получаем длину каждого разбитого отрезка и в пятой выводим это на экран. В шестой выводим первую точку (координаты A), далее в цикле прибавляя длину разбитого отрезка, получаем очередную точку, и выводим это значение. После цикла удаляем два оставшихся значения (длина отрезка и последнюю точку).
: FOR29 ( I: N>1 F: A B [A<B] -> ) \ выводим: H A A+H ... A+N*H
FOVER F- \ F: A B -> A B-A
DUP S>D D>F \ I: N -> F: A B-A N
F/ FDUP \ F: A B-A N -> A (B-A)/N (B-A)/N
." Длина отрезка H = " F. CR \ F: A (B-A)/N (B-A)/N -> A (B-A)/N
FSWAP FDUP F. \ A (B-A)/N -> (B-A)/N A
1+ 1 \ I: N -> N+1 1
DO \ N+1 1 ->
FOVER F+ \ (B-A)/N A -> (B-A)/N A+(B-A)/N
FDUP F. \ дублируем и печатаем «A+(B-A)/N»
LOOP FDROP FDROP ; \ (B-A)/N A+(B-A)/N –>, очищаем вещественный стек после себя
\ Тест вышеописанного слова:
3 1.2E 1.5E FOR29
Длина отрезка H = 0.1000000
1.2000000 1.3000000 1.4000000 1.5000000 Ok
Пример тридцать. Пишем новое слово с названием «F(X)», вычисляющее значение функции «1-sin(X)» и выводящий результат на экран. Само слово «FOR30» близнец «FOR29», единственное отличие – это замена вывода «F.» (в строках шесть и десять на «F(X)»).
: F(X) ( X -> ) \ вывести значение «1-sin(X)»
FSIN FNEGATE 1E F+ F. ; \ X -> [1-sin(X)]
: FOR30 ( I: N>1 F: A B [A<B] -> )
FOVER F- \ F: A B -> A B-A
DUP S>D D>F \ I: N -> F: A B-A N
F/ FDUP \ F: A B-A N -> A (B-A)/N (B-A)/N
." Длина отрезка H= " F. CR \ F: A (B-A)/N (B-A)/N -> F: A (B-A)/N
FSWAP FDUP F(X) \ F: A (B-A)/N -> F: (B-A)/N A
1+ 1 \ I: N -> N+1 1
DO \ N+1 1 ->
FOVER F+ \ (B-A)/N A -> (B-A)/N A+(B-A)/N
FDUP F(X) \ дублируем и обрабатываем «A+(B-A)/N»
LOOP FDROP FDROP ; \ (B-A)/N A+(B-A)/N –>, очищаем вещественный стек после себя