Файл: А. Б. Шнейвайс основы программирования.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.12.2023

Просмотров: 331

Скачиваний: 6

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

9.13
Вариант 11 9.13.1
Задача №1
Написать программу, которая моделирует работу разностной машины
Чарльза Бэббиджа. Программа должна вводить:
a и b — начальный и конечный аргументы разностной таблицы;
m — число участков равномерной дискретизации [a,b];
n — показатель степени вычисляемого полинома;
p(0:n) — коэффициенты полинома P
n
(x) =
n
X
k=0
p k
x n−k
;
d(-1:n) — начальная строка схемы Бэббиджа. d(-1)=a — аргумент,
d(0)=P
n
(a). d(1:n) — первые n разностей (схема Б).
Программа должна использовать:
удобный переход на любую из возможных разновидностей типа real;
размещаемые массивы p(0:n) и d(-1:n);
внешнюю ФУНКЦИЮ rdrf_1(n) ввода коэф. полинома;
внешнюю подпрограмму wrt_1(p,n) вывода коэф. полинома;
внешнюю ФУНКЦИЮ initial_1(a,n) ввода первой строки;
внешнюю подпрограмму Babbage(d,n) расчёта очередной строки;
внешнюю функцию Horner(p,x,n) расчёта значения полинома.
9.13.2
Задача №2
Заменить процедуру initial_1 процедурой initial_2, которая должна вычислять (не используя матрицу) то, что вводит initial_1.
9.13.3
Задача №3
Заменить в задаче №2 вектора p(0:n) и d(-1:n) связными списками.
Описать соответствующие типы данных и, работающие со списками, про- цедуры: initial_2l, Babbage_l, Horner_l.
9.13.4
Задача №4
В задачах №2 и №3 обеспечить вызов (в пределах одной программы)
процедур с близкими по сути выполняемой работы именами по одно- му имени, например, rdr_1 и rdr_1l по имени rdr, wrt_1 и wrt_1l
— по имени wrt; initial_2 и initial_2l — по имени initial; Babbage и Babbage_l — по мени Babbage; Horner и Horner_l — по имени
Horner.
316

9.14
Вариант 12 9.14.1
Задача №1
Написать программу, которая моделирует работу разностной машины
Чарльза Бэббиджа. Программа должна вводить:
a и b — начальный и конечный аргументы разностной таблицы;
m — число участков равномерной дискретизации [a,b];
n — показатель степени вычисляемого полинома;
p(0:n) — коэффициенты полинома P
n
(x) =
n
X
k=0
p k
x n−k
;
d(-1:n) — начальная строка схемы Бэббиджа. d(-1)=a — аргумент,
d(0)=P
n
(a). d(1:n) — первые n разностей (схема Б).
Программа должна использовать:
удобный переход на любую из возможных разновидностей типа real;
размещаемые массивы p(0:n) и d(-1:n);
модульную ФУНКЦИЮ rdrf_1(n) ввода коэф. полинома;
модульную подпрограмму wrt_1(p,n) вывода коэф. полинома;
модульную ФУНКЦИЮ initial_1(a,n) ввода первой строки;
модульную подпрограмму Babbage(d,n) расчёта очередной строки;
модульную функцию Horner(p,x,n) расчёта значения полинома.
9.14.2
Задача №2
Заменить процедуру initial_1 процедурой initial_2, которая должна вычислять (не используя матрицу) то, что вводит initial_1.
9.14.3
Задача №3
Заменить в задаче №2 вектора p(0:n) и d(-1:n) связными списками.
Описать соответствующие типы данных и, работающие со списками, про- цедуры: initial_2l, Babbage_l, Horner_l.
9.14.4
Задача №4
В задачах №2 и №3 обеспечить вызов (в пределах одной программы)
процедур с близкими по сути выполняемой работы именами по одно- му имени, например, rdr_1 и rdr_1l по имени rdr, wrt_1 и wrt_1l
— по имени wrt; initial_2 и initial_2l — по имени initial; Babbage и Babbage_l — по мени Babbage; Horner и Horner_l — по имени
Horner.
317


10
Приложение I. Функция передачи типа transfer
Функция transfer(source, mold [, size] ) (функция передачи типа;
см. [2]) или (одна из функций преобразования типа; см. [3]), а точнее функция, позволяющая взглянуть на данное типа первого позиционно- го аргумента через очки типа данного второго позиционного аргумента,
не осуществляя формального преобразования типов. Другими словами,
взглянуть на данное source так, как если бы оно было типа mold, не изменяя внутреннего машинного битового представления source.
В некоторых ситуациях очень полезная функция, по сути обобща- ющая действие оператора equivalence, который в современном ФОР-
ТРАНе считается архаичным. Рассмотрим примеры.
1. Допустим, что у нас нет функций achar и iachar. Как в этом слу- чае по коду символа получить сам символ и наоборот по символу получить его код? В старом ФОРТРАНе была возможна программа:
program transf0
! Практически, ничего не делая,
implicit none
! смотрим на одну и туже область character(1) x
! памяти: то как на данное типа integer(1)
k
! character(1), то как на данное equivalence (x,k)
! integer(1). И функции achar и x=’z’; write(*,*) ’ x=’,x,’
k=’,k
! iachar остаются без работы,
k=48;
write(*,*) ’ x=’,x,’
k=’,k
! хотя в современном фортране это
! можно получить так:
x=’z’; write(*,*) ’ x=’,x,’
k=’,iachar(x)
k=48;
write(*,*) ’ x=’,achar(k),’
k=’,k
! Однако, есть x=’z’; k=transfer(x,k); write(*,*) ’ x=’,x,’
k=’,k
! и такой способ k=48;
x=transfer(k,x); write(*,*) ’ x=’,x,’
k=’,k end
Её результат:
x=z k=
122
x=0
k=
48
x=z k=
122
x=0
k=
48
x=z k=
122
x=0
k=
48
Кто теперь скажет, что equivalence — архаичен? Просто не нужно его использовать не по делу там, где легко ошибиться.
318

2. В ФОРТРАНе нет, как в CИ, беззнакового целого типа. Поэтому иногда результат работы на типе integer(4) выглядит как отри- цательное число из-за происшедшего якобы переполения. Так про- грамма random_seed начальной настройки random_number (ге- нератора равномерно распределённых по промежутку [0,1) чисел)
получает затравочные целые в массиве get типа integer(4). При их выводе могут встретиться и отрицательные целые. Однако мож- но взглянуть на них через очки типа integer(8).
program transf1
! Работая с transfer не следует implicit none
! забывать одну её пакостную integer(4), allocatable :: sget(:)
! особенность, за которую её
integer(8) i8
! можно критиковать с тем же integer(4) :: n, i, k(2)=(/0,0/)
! успехом, с каким раньше call random_seed(n)
! критиковали equivalence.
write(*,*) ’ n=’,n
! Именно, архиважно следить,
allocate(sget(n))
! чтобы первый аргумент занимал call random_seed()
! память того же объёма, что и call random_seed(get=sget)
! результат.
write(*,’("В первый раз крупно повезло!!!")’)
write(*,’(2x,"i",5x,"sget",9x,"i8")’)
do i=1,n
! САМЫЙ ОПАСНЫЙ СЛУЧАЙ:
i8=transfer(sget(i),i8)
! Результат верен. Но здесь write(*,’(i3,i12, i12)’) i,sget(i),i8
! нам просто "крупно повезло",
enddo
! так как,
write(*,’("Второе повторение того же даёт уже другой результат;")’)
write(*,’(2x,"i",5x,"sget",9x,"i8")’)
do i=1,n i8=transfer(sget(i),i8)
write(*,’(i3,i12, i12)’) i,sget(i),i8
enddo end
Её результат:
n=
8
!
В первый раз крупно повезло!!! !
Второе повтор --- результат иной i
sget i8
!
i sget i8 1
437395160 437395160
!
1 437395160 4732362456 2
1404128605 1404128605
!
2 1404128605 5699095901 3
572505362 572505362
!
3 572505362 4867472658 4 -1187264075 3107703221
!
4 -1187264075 7402670517 5
454383258 454383258
!
5 454383258 4749350554 6
525702629 525702629
!
6 525702629 4820669925 7
973594203 973594203
!
7 973594203 5268561499 8
1758310677 1758310677
!
8 1758310677 6053277973 319


Дело в том, что, собираясь смотреть на тип integer(4) глазами integer(8), мы должны подать типу integer(8) для обзора восемь байт, в то время как тип integer(4) предоставляет их только че- тыре. Поэтому, что может увидеть тип integer(8) на месте стар- ших четырёх байтов своего поля видимости, можно только гадать.
В первый раз нас, видимо, спас интеллект компилятора (или слу- чайность). Во второй — интеллект подвёл (может ошибка в старой версии компилятора). Разъяснение ситуации дано, например, в [4]:
если source — последовательность из n бит (b
1
b
2
· · · b n
), а данное mold — последовательность из m бит (s
1
s
2
· · · s m
), то при
• n=m : b
1
b
2
· · · b n
, что видели на примере программы transf0.
• n1
b
2
· · · b n
s
1
s
2
· · · s m−n
. Биты s
1
s
2
· · · s m−n не определены!
• n>m : b
1
b
2
· · · b m
3. Как следует поступить? — Подать в качестве source не 4 байта, а
8, например, целочисленный массив из двух элементов: в первый элемент записать наше данное из sget(i), а во второй — нули:
program transf2; implicit none; integer(4), allocatable :: sget(:)
integer(8) i8;
integer(4) :: n, i, k(2)
call random_seed(n)
write(*,*) ’ n=’,n allocate(sget(n))
call random_seed(); call random_seed(get=sget)
write(*,’("Теперь и в первый раз,")’)
write(*,’(2x,"i",5x,"sget",5x,"k(2)",5x,"k(1)",9x,"i8")’)
do i=1,n k(1)=sget(i); k(2)=0_4
i8=transfer(k,i8)
write(*,’(i3,i12, i5, i12, i12)’) i,sget(i), k(2), k(1), i8
enddo write(*,’("и во второй --- результаты одинаково верны!")’)
write(*,’(2x,"i",5x,"sget",5x,"k(2)",5x,"k(1)",9x,"i8")’)
do i=1,n k(1)=sget(i); k(2)=0_4
i8=transfer(k,i8)
write(*,’(i3,i12, i5, i12, i12)’) i,sget(i), k(2), k(1), i8
enddo end
Теперь результаты правильны в обоих случаях.
320

Заметим, что обычно элемент массива с более старшим индексом расположен по б´
ольшему адресу нежели элемент с более младшим индексом. Поэтому значение sget(i) важно помещать именно в k(1),
а не в k(2).
Заметим также, что при использовании оператора equivalence всё
выглядело бы до смешного проще: не потребовалось тратить время на вызов transfer вообще, и вряд ли бы совершили ляпу из програм- мы transf1.f95, поскольку бы сразу задумались, что надо поместить в старшие четыре байта результата:
program transf3; implicit none; integer(4), allocatable :: sget(:)
integer(8) i8;
integer(4) :: n, i, k(2)
equivalence (k(1),i8)
call random_seed(n); write(*,*) ’ n=’,n allocate(sget(n))
call random_seed(); call random_seed(get=sget)
write(*,’("Теперь и в первый раз,")’)
write(*,’(2x,"i",5x,"sget",5x,"k(2)",5x,"k(1)",9x,"i8")’)
do i=1,n k(1)=sget(i); k(2)=0_4
write(*,’(i3,i12, i5, i12, i12)’) i,sget(i), k(2), k(1), i8
enddo write(*,’("и во второй --- результаты одинаково верны!")’)
write(*,’(2x,"i",5x,"sget",5x,"k(2)",5x,"k(1)",9x,"i8")’)
do i=1,n k(1)=sget(i); k(2)=0_4
write(*,’(i3,i12, i5, i12, i12)’) i,sget(i), k(2), k(1), i8
enddo end n=
12
Теперь и в первый, и во второй раз --- результаты одинаково верны!
i sget k(2)
k(1)
i8
!
k(2)
k(1)
i8 1
287027030 0
287027030 287027030 !
0 287027030 287027030 2
-719361131 0
-719361131 3575606165 !
0
-719361131 3575606165 3
574274270 0
574274270 574274270 !
0 574274270 574274270 4
292048305 0
292048305 292048305 !
0 292048305 292048305 5
185733336 0
185733336 185733336 !
0 185733336 185733336 6 -1598963619 0 -1598963619 2696003677 !
0 -1598963619 2696003677 7
572469522 0
572469522 572469522 !
0 572469522 572469522 8
1446716853 0
1446716853 1446716853 !
0 1446716853 1446716853 9
437591706 0
437591706 437591706 !
0 437591706 437591706 10 1398099429 0
1398099429 1398099429 !
0 1398099429 1398099429 11 570932571 0
570932571 570932571 !
0 570932571 570932571 12 -1177695979 0 -1177695979 3117271317 !
0 -1177695979 3117271317 321


4. Приведём ещё пример, поясняющий суть дела при работе с transfer program transf4; implicit none; integer(4) :: i, k(2); integer(8) i8
i=-huge(1_4)-1; write(*,’(" 0)
i=",i12,"...",b32)’) i,i i8=transfer(i,i8); write(*,*) ’1) i8=’,i8
! Почему i8=transfer(i,i8); write(*,*) ’2) i8=’,i8
!
разные i8=transfer(i,i8); write(*,*) ’3) i8=’,i8
!

1   ...   13   14   15   16   17   18   19   20   ...   23

результаты?
i8=transfer(i,i8); write(*,*) ’4) i8=’,i8
!
k(1)=-huge(1_4)-1; k(2)=0_4
! <--= Безопасный i8=transfer(k,i8); write(*,*) ’ i8=’,i8
!
вариант.
write(*,’("
i",9x,"k(2)",8x,"k(1)",8x,"i8")’)
do i=-2,2; k(1)=i; i8=transfer(k,i8)
write(*,’(i3,i12,i12, i20)’) i,k(2),k(1),i8
enddo end
0)
i= -2147483648...10000000000000000000000000000000 1) i8=
139820512837632 2) i8=
2147483648 3) i8=
2147483648 4) i8=
140735340871680
i8=
2147483648
i k(2)
k(1)
i8
-2 0
-2 4294967294
-1 0
-1 4294967295 0
0 0
0 1
0 1
1 2
0 2
2
Опять странно, что, выполняя одно и то же, получаем разные ре- зультаты Либо это — накладка древнего компилятора, либо — намёк на необходимость более ответственно задавать аргумент source.
5. Результат функции transfer либо скаляр (если mold — скаляр),
либо вектор (если mold — массив любой формы и размера).
program transf5; implicit none; integer i, j, k integer :: a(4,4), b(16), d(6), e(18)
k=1; do i=1,4; do j=1,4; a(i,j)=k; k=k+1; enddo; enddo b=33; b=transfer(source=a,mold=b)
! SIZE не указан: число элементов write(*,’(" b:",16i3)’) b
!
в a и b одинаково (4*4=16).
d=55; d=transfer(source=a,mold=d)
!
Число элементов в a больше write(*,’(" d:",25i3)’) d
!
числа элементов в d (16>6).
e=77; e=transfer(source=a,mold=e)
!
Число элементов в a меньше write(*,’(" e:",25i3)’) e
!
числа элементов в e (16<18)
e=77; e(1:6)=transfer(source=a,mold=e,size=6) ! SIZE=6 УКАЗАН и задаёт write(*,’(" e(1:6):",25i3)’) e
! размер вектора результата end
322

Если source - массив любой формы и размера, то результат — все- гда одномерный массив, у которого заполняются либо все элементы,
либо столько, сколько их в mold — в зависимости от того, чей заяв- ленный размер больше и отсутствия при вызове transfer аргумента size.
b:
1 5
9 13 2
6 10 14 3
7 11 15 4
8 12 16
d:
1 5
9 13 2
6
e:
1 5
9 13 2
6 10 14 3
7 11 15 4
8 12 16 77 77
e(1:6):
1 5
9 13 2
6 77 77 77 77 77 77 77 77 77 77 77 77
Наличие же size-аргумента с необходимостью требует, чтобы заяв- ленный размер вектора-результата был равен size. Например, если бы в третьей строке снизу программы transf5 вместо e(1:6)= на- писали бы e=, то получили бы сообщение об ошибке:
transf5.f95:10.6:
e=77; e=transfer(source=a,mold=e,size=6)
1
ошибка: Different shape for array assignment at (1) on dimension 1
(18 and 6)
6. Ещё один пример работы transfer — получение из набора комплекс- ных данных набора вещественных чисел, в котором Re и Im ком- плексных чередуются, а затем наоборт из одномерного массива типа real получить одномерный массив типа complex:
program transf6;
implicit none complex(4) :: c(5)=(/(1.1,1.01),(1.2,1.02),(1.3,1.03),(1.4,1.04),&
& (1.5,1.05)/)
real(4)
r(10)
integer i write(*,’(4x,"i",8x,"Re(c)",8x,"Im(c)"/(i5,e15.7,e15.7))’) (i,c(i),i=1,5)
r=transfer(c,r)
write(*,’(4x,"i",8x,"r"/(i5,e15.7))’) (i,r(i),i=1,10)
r(2:10:2)=-r(2:10:2)
c=transfer(r,c)
write(*,’(4x,"i",8x,"Re(c)",8x,"Im(c)"/(i5,e15.7,e15.7))’) (i,c(i),i=1,5)
c(1:3)=transfer(r,c,3)
write(*,’(4x,"i",8x,"Re(c)",8x,"Im(c)"/(i5,e15.7,e15.7))’) (i,c(i),i=1,3)
end
323