real, dimension(1:5,1:5) :: A
A(1:2,1:3) ! вырезка из массива A
A(1,:) ! сечение (первая строка)
A(:,2) ! сечение (второй столбец)
1.5.2.
Задание массивов
Для задания значений массивов в Фортране предусмотрены различные
средства. Опишем несколько одинаковых массивов и зададим их значения
несколькими различными способами:
real, dimension(1:4) :: a, b, c, d
a = 2.0 ! Все элементы массива равны двум
! Использование конструктора массивов
b = (/1.0, 2.0, 3.0, 4.0/)
! Использование оператор data
data c/4.0, 3.0, 2.0, 1.0/
! Использование квадратных скобок
d = [ 1.0, 3.0, 4.0, 2.0 ]
Оператор
data
в общем случае может применяться для присваивания
значений не только массивам, но и обычным переменным. Он имеет сле-
дующий синтаксис:
data <список объектов>/<список значений>/
Конструктор массивов может применяться для задания значений эле-
ментов массива, зависящих от индекса. Например:
a(1:12) = (/ (2*i, i=1,10), 12.0, 15.0/)
Первые 10 элементов заданы с помощью выражения от индекса, а по-
следние два заданы явно. В результате мы получим массив c элементами
2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 12, 15. Хотя конструктор позволяет задавать
только одномерные массивы, но с помощью оператора
reshape
одномер-
ный массив легко можно преобразовать в многомерный:
Геворкян М. Н., Королькова А. В., Кулябов Д. С. Параллельное программирование
19
! Преобразуем одномерный массив из 12 элементов в
! двумерный массив 3 на 4
d = reshape( (/ (2*i, i=1,10), 12.0, 15.0/), (/3,4/) )
1.5.3.
Действия над массивами
К любому элементу массива можно обратиться по соответствующим
индексам массива, например,
v
=
a(2,1)
+
b(3,3)
. Рассмотрим ещё
несколько примеров, иллюстрирующих поэлементные действия с массива-
ми:
real, dimension(5) :: p, q
real, dimension(7) :: r
p = 2.0 ! Все элементы равны 2
r = 3.2 ! Все элементы равны 3,2
q = p + 0.89*r(3:7)
! Возможно применение стандартной функции.
! Значение функции будет вычислено для
! каждого элемента массива
p = exp(p)
Конструкция
where
позволяет запрограммировать действия с некото-
рыми элементами массива, отбираемые в соответствии с логической мас-
кой. Общий вид данной конструкции:
where (<логическое выражение массив>)
<действия над элементами массива>
[elsewhere
<действия над элементами массива>]
end where
! Обнулим все отрицательные элементы массива a
where(a < 0) a = 0
Более гибким в применении является оператор
forall
:
forall(<индексное выражение>,[<сколярная логическая маска>])
<действия над элементами массива>
end forall
! Сделаем замену в некоторой части массива всех
! положительных элементов на их квадратные корни
forall(i = 3:6, j =-3:3, M(i,j)>0) M(i,j)=sqrt(M(i,j))
В Фортран встроено много функций, позволяющих узнать характери-
стики массива, а также совершать матричные операции над одномерными
(вектор) и двухмерными (матрица) массивами. Наиболее полезные функ-
ции приведём в виде таблицы:
20
Глава 1. Fortran
Функция
Пояснение
lbound(a,[dim])
начальный индекс по измерению
dim
hbound(a,[dim])
конечный индекс по измерению
dim
shape(a)
вектор экстентов
size(a,[dim])
экстент по заданному измерению
dot_product(a,b)
скалярное произведение двух векторов
matmul(A,B)
перемножение матриц
maxval(a)
максимальный элемент массива
minval(a)
минимальный элемент массива
sum(a)
сумма элементов массива
transpose(a)
транспонирование (вектора, матрицы)
В функциях
lbound
и
hbound
второй аргумент не обязателен в случае
одномерного массива. В функции
size
второй аргумент также можно не
указывать, тогда функция вернёт суммарное число элементов по всем раз-
мерностям.
Заметим, что в Фортране нет встроенных функций для нахождения об-
ратной матрицы и определителя матрицы. Этот недостаток легко устраня-
ется сторонними библиотеками, об одной из которых (
LAPACK
) будет рас-
сказано далее.
Для иллюстрации применения некоторых из приведённых выше функ-
ций, рассмотрим простую программу.
! Простая программа определения
! максимального элемента массива
program min_max
implicit none ! не определять тип переменной по имени
real, dimension(1:100) :: a
real :: maximum
integer :: i
maximum = 0
i = 0
! заполняем массив случайными числами в пределах [0,1]
call random_number(a)
do i = lbound(a,1),ubound(a,1),1
! print *, a(i)
if (maximum <= a(i)) then
maximum = a(i)
end if
end do
print *, "Информация о массиве:"
print *, "
левая граница =", lbound(a,1)
print *, "
правая граница =", ubound(a,1)
print *, "Наш максимум = ", maximum
print *, "Встроенный максимум = ", maxval(a)
end program min_max
Геворкян М. Н., Королькова А. В., Кулябов Д. С. Параллельное программирование
21
1.5.4.
Динамические массивы
Для создания гибких программ часто необходимо создавать массивы,
размерности которых вычисляются в самой программе, вводятся пользо-
вателем или передаются как параметр при запуске программы. Для таких
случаев предусмотрено описание динамических массивов, граничные пары
которых при объявлении не указываются. Такие массивы называются дина-
мическими, и для их описания следует использовать атрибут
allocatable
:
! Опишем двумерный массив a
! и одномерный массив b
real, allocatable :: a(:,:), b(:)
После описания такого массива уже в области операторов следует вы-
делить под него память оператором
allocate(<список массивов>)
:
allocate(a(1:n,1:m), b(1:k))
Переменным
n, m, k
должны быть присвоены конкретные значения.
После завершения работы с массивами память можно освободить, исполь-
зуя оператор
deallocate(<список массивов>)
:
deallocate(a(1:n,1:m), b(1:k))
Хотя освобождение памяти, выделенной для динамических массивов,
при завершении программы должно осуществляться автоматически, пола-
гаться на это не рекомендуется, и следует всякий раз использовать оператор
deallocate
.
1.5.5.
Передача массива в качестве аргумента
процедуры
В некоторых случаях необходимо передать процедуре массив в каче-
стве аргумента. При этом размерность массива должна быть известна зара-
нее, но число элементов может вычисляться непосредственно в теле проце-
дуры. Следующая процедура вычисляет произведение всех элементов од-
номерного массива:
real function ArrayProd(A)
implicit none
! Следует указать только начальный индекс
real, intent(in), dimension(1:) :: A
integer :: m,i
m = size(A)
ArrayProd = 1
do i = 1,m,1
ArrayProd = ArrayProd*A(i)
end do
end function ArrayProd
22
Глава 1. Fortran
Для использования данной функции в основной программе следует вклю-
чить её в состав модуля (см. раздел 1.4.3) или же снабдить интерфейсным
блоком:
program prod
! Функция ArrayProd входит в состав модуля modprod
use modprod
implicit none
real, dimension(1:4) :: a
data a/1,2,3,4/
write(*,*), ArrayProd(a)
end program prod
1.5.6.
Функция, возвращающая массив
Имеется возможность создавать функции, возвращаемое значение ко-
торых является массивом:
! Функция, возвращающая массив
function fvector(x)
implicit none
! Массив переменных
real, intent(in) :: x
! Тип возвращаемых значений
real, dimension(1:3) :: fvector
fvector(1) = sin(x)
fvector(2) = cos(x)
fvector(3) = x**2
end function
Следует включить данную функцию в состав модуля, иначе потребует-
ся написать блок интерфейса. При вызове функции будет возвращён массив
значений:
real, parameter :: x = 2.0
print *, "fvector(x) = ", fvector(x)
или
real, dimension(1:3) :: fv
fv = fvector(x)
print *, "fvector(x) = ", fv(1), fv(2), fv(3)
1.6.
Ввод и вывод. Форматирование
1.6.1.
Ввод-вывод
Ввод и вывод по умолчанию происходит в консоль, в которой запущена
программа. Этой цели служат два оператора:
Геворкян М. Н., Королькова А. В., Кулябов Д. С. Параллельное программирование
23
! Ввод
read(*,*), "Список ввода"
read *, "Список ввода"
! Вывод
write(*,*), "Список вывода"
print *, "Список вывода"
Звёздочки означают стандартный формат и стандартное устройство вво-
да (клавиатура) и вывода (консоль). Общий синтаксис данных операторов
таков:
read(<устройство ввода>,<формат ввода>),
<список переменных>
write(<устройство вывода>,<формат вывода>),
<список переменных>
Следующий пример пояснит применение данных операторов для ввода
и вывода данных через консоль:
integer :: n, i
real, dimension(1:100) :: a
logical :: l
write(*,*), "Введите n"
read(*,*), n
write(*,*), "Введите ",n,"первых элементов:"
! Вводим значения через пробел
read(*,*), (a(i), i = 1,n)
! Распечатываем значения
write(*,*), (a(i), i = 1,n)
write(*,*), "Введите правду или ложь"
! Логические .true. и .false. Вводятся как T и F.
read(*,*), l
Для организации вывода в файл следует вначале открыть файл на запись
с помощью оператора
open
, который имеет два обязательных аргумента:
open(<номер устройства>,file=<путь к файлу>)
Для закрытия файла применяется оператор
close
:
close(<номер устройства>,status=)
Допустим, что в файле
in.txt
записаны три действительных числа,
расположенные в одну строку через пробел. Необходимо открыть файл на
чтение, считать данные и записать их в другой файл
out.txt
:
! Номер устройства может быть любым (больше 3)
! для разных файлов — разные номера
open(4,file= "in.txt")
open(5,file= "out.txt")
! Указываем номер устройства
read(4,*), x, y, z
write(5,*), x, y, z
! Закрываем файлы, сохраняя информацию
close(4, status = "keep")
close(5, status = "keep")
24
Глава 1. Fortran
1.6.2.
Форматирование ввода-вывода
При необходимости вывода в отличном от умолчания формате приме-
няются спецификации формата. Они задаются в виде текстовой строки в ар-
гументе операторов вывода или с помощью специального оператора
format
.
Спецификации формата лучше рассматривать на примерах:
–
i10
— целое число максимум из 10 позиций;
–
f10.5
— вещественное число максимум из 10 знаков всего и 5 знаков
после запятой;
–
e12.4
— вещественное с плавающей точкой из 12 знаков (например,
0.50200E+10
);
–
a10
— текст из 10 символов.
Продемонстрируем на примере использование оператора
format
:
integer :: k
real :: x
character :: s
! Перед оператором format указывается метка
! Меткой может быть произвольное целое число
! Метка позволяет обратиться к помеченному
! месту в программе
10
format(i10,f10.5,a10)
write(10,*), k, x, s
Вместо использования
format
можно непосредственно указать строку
спецификации формата в операторе
write
:
write(*, "(i10,f10.5,a10)"), k, x, s
Это удобно, когда нужно указать отдельную спецификацию формата
вывода для каждого случая.
Кроме настройки вывода стандартных типов данных, с помощью спе-
цификации формата можно управлять позицией в строке, вставкой пробе-
лов, табуляций, переводом на новую строку:
t
— отступ на
n
позиций
от начала строки,
x
— вставка
n
пробелов,
t
— вставка
n
символов
табуляции.
Следующий пример показывает, как распечатать в консоль несколько
элементов массива:
real, dimension(1:20) :: a
! Заполним массив случайными числами
call random_number(a)
write(*,"(10(2x,f5.2))"), a
Хотя массив
a
содержит 20 элементов, но при выводе в одной стро-
ке будет лишь 10 из них, следующие 10 будут перенесены на следующую
строку. Кроме того, между элементами массива будут вставлены 2 пробела
(
2x
).
Часто необходимо организовать распечатку элементов массива в виде
матрицы, но размерность массива заранее неизвестна, так как вычисляет-
ся уже во время работы программы или вводится пользователем. В этом
случае можно использовать следующий приём:
Геворкян М. Н., Королькова А. В., Кулябов Д. С. Параллельное программирование
25
character(len=20) :: fm
write(fm,*), m
write (*, "("//adjustl(fm)// "(f8.3), t1)") (a(i), i=1,m)
Вначале задаётся строковая переменная
fm
, затем в эту переменную за-
писывается некоторое целое число
m
. Дальше строковая переменная вклю-
чается в спецификацию формата с помощью конкатенации строк (оператор
//
) и оператора
adjustl
, который убирает лишние пробелы. Таким обра-
зом можно организовать форматированный вывод любых матриц и векто-
ров, что значительно упрощает восприятие результатов программы.
Ниже приведён код программы, иллюстрирующий как действия над мат-
рицами, так и разнообразное форматирование при записи результатов в
файл. Для лучшего понимания работы программы следует скомпилировать
её, запустить и посмотреть результат работы. Обратите внимание на поря-
док задания элементов матрицы и на то, в какой последовательности пере-
бираются коэффициенты массива при выводе в файл.
!-------------------------------------
! Действия с матрицами/массивами
! и форматированный вывод в файл
!-------------------------------------
program matrix
integer :: i, j, k
real :: s
real, dimension (1:5,1:4) :: a
real, dimension (1:4,1:7) :: b
real, dimension (1:5,1:7) :: c
real, dimension (1:4,1:5) :: aT
! открываем файл для записи
open(10,file = "matrix.out") ! Открыли файл
! заполняем массив случайными числами в пределах [0,1]
!call random_number(a)
! Заполняем матрицу. Заполнение идет по столбцам
! 1.000
0.000
0.000
0.000
! 0.000
0.000
1.000
0.000
! 0.000
1.000
0.000
0.000
! 0.000
0.000
0.000
1.000
! 0.000
0.000
0.000
0.000
data a/1,0,0,0,0, 0,0,1,0,0, 0,1,0,0,0, 0,0,0,1,0/
! 11.000
12.000
13.000
14.000
! 21.000
22.000
23.000
24.000
! 31.000
32.000
33.000
34.000
! 41.000
42.000
43.000
44.000
! 51.000
52.000
53.000
54.000
!data a/11,21,31,41,51,12,22,32,42, &
!
52,13,23,33,43,53,14,24,34,44,54/
call random_number(b)
call random_number(c)
26
Глава 1. Fortran
!---
write(10,*), "# Матрица A"
! Записываем в файл
write(10, '(4(f8.3), t1)') ((a(i,j), j = 1,4), i = 1,5)
write(10,*), "# Матрица B"
write(10, '(7(f8.3), t1)') ((b(i,j), j = 1,7), i = 1,4)
! Использование встроенной функции для перемножения
c = matmul(a,b)
!----
write(10,*), "# C — результат переумножения матриц A и B"
write(10, '(7(f8.3), t1)') ((c(i,j), j = 1,7), i = 1,5)
!-----
! Сложение вырезок
c(1:4,1:4) = a(1:4,1:4) + b(1:4,1:4)
write(10,*), "# C — результат сложения вырезок из A и B"
write(10, '(4(f8.3), t1)') ((c(i,j), j = 1,4), i = 1,4)
! Транспонирование матрицы А
aT = transpose(a)
write(10,*), "# AT — результат транспонирования матрицы А"
write(10, '(5(f8.3), t1)') ((aT(i,j), j = 1,5), i = 1,4)
!write (*,'(5(f8.3), t1)') ((c(i,j), j = 1,5), i = 1,4)
! Закрываем файл после того, как завершили с ним работу
close(10, status = "keep")
end program matrix
1.7.
Алгоритмы умножения матриц
1.7.1.
Стандартный алгоритм
Рассмотрим две матрицы A[m
× n] и B[ n × k], которые при умножении
дают матрицу C[m
× k]:
a
1
1
a
2
1
a
3
1
. . .
a
n
1
a
1
2
a
2
2
a
3
2
. . .
a
n
2
..
.
..
.
..
.
. ..
..
.
a
1
m
a
2
m
a
3
m
. . .
a
n
m
×
b
1
1
b
2
1
b
3
1
. . .
b
n
1
b
1
2
b
2
2
b
3
2
. . .
b
n
2
..
.
..
.
..
.
. ..
..
.
b
1
m
b
2
m
b
3
m
. . .
b
n
m
=
=
c
1
1
c
2
1
c
3
1
. . .
c
n
1
c
1
2
c
2
2
c
3
2
. . .
c
n
2
..
.
..
.
..
.
. ..
..
.
c
1
m
c
2
m
c
3
m
. . .
c
n
m
,
Геворкян М. Н., Королькова А. В., Кулябов Д. С. Параллельное программирование
27
где
c
l
i
=
m
∑
j=1
a
j
i
b
l
j
, i = 1, . . . , n, j = 1, . . . , m, l = 1, . . . , k.
Каждый элемент матрицы C является скалярным произведением стро-
ки матрицы A на столбец матрицы B. Стандартный алгоритм умножения
требует nmk операций умножения и n(m
− 1) k операций сложения и ал-
горитмически реализуется следующим образом:
Достарыңызбен бөлісу: |