
- •Глава 1. Разработка эффективных алгоритмов
- •Value значение переменной X, для которого рассчитывается Pn(X);
- •Value значение переменной X, для которого рассчитывается Pn(X);
- •Value значение переменной X, для которого рассчитывается Pn(X);
- •Void Insert_Element(char New_Unit, unsigned Free, unsigned Position, char *Name, unsigned Next)
- •Void Max_Min_Element(int array[], unsigned Size, unsigned max, unsigned min)
- •Алгоритм 4.2 нахождения наибольшего и наименьшего элементов множества
- •Алгоритм 6. Сортировка последовательности чисел слиянием
- •Void Merge(int *out1, int *out2, unsigned size, int *sorted)
- •Алгоритм 7. Динамического программирования для вычисления порядка, минимизирующего сложность умножения цепочки из n матриц m1m2…Mn
- •Void main()
Алгоритм 7. Динамического программирования для вычисления порядка, минимизирующего сложность умножения цепочки из n матриц m1m2…Mn
Вход. r0, r1, …, rn, где ri-1 и ri числа строк и столбцов матрицы Mi.
Выход. Минимальная сложность умножения матриц Mi в предположении, что для умножения (pq)-матрицы на (qr)-матрицу требуется pqr операций.
Метод. Алгоритм заполняет последовательно «верхнетреугольную» матрицу минимальных сложностей умножения двух, трех, четырех, … и т.д. матриц из предлагаемой последовательности. На первом шаге заполняется строка, соответствующая сложности умножения двух матриц, взятых последовательно из предлагаемого произведения. На втором шаге рассчитываются минимальные сложности умножения трёх последовательных матриц, основываясь на известных минимальных сложностях умножения двух матриц. Заполняется соответствующая строка, и т.д.
Программа 7.
// Программа рассчитывает матрицу минимальных сложностей умножения NUM матриц,
// размеры которых r0, r1, …, rn заданы в массиве Sizes
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#define NUM 6 // Число перемножаемых матриц
#define NUM1 NUM+1 // Размер массива Sizes
unsigned Sizes[NUM1]; // Массив, содержащий размеры перемножаемых матриц
unsigned long Time_comp[(NUM1*NUM)>>1]; // Матрица минимальных сложностей умножения
Void main()
{
unsigned long value, temp, *ptr, *current[NUM<<1];
unsigned ind, index, count, curr, max, min;
// Здесь должна стоять функция ввода массива Sizes
// Процедура заполнения матрицы Time_copm с помощью метода
// динамического программирования
ptr=Time_comp;
for(index=0; index<NUM; *ptr++=0, index++); // Первая строка заполняется нулями
for (ind=1; ind<NUM; ind++)
{
max=(ind<<1)-1;
// Предварительный расчёт указателей на элементы матрицы Time_copm, которые
// используются в дальнейшем при выборе минимальной сложности умножения.
// Указатели хранятся в массиве current.
for(index=0; index<ind; index++)
{
count=(index*((NUM<<1)-index+1))>>1;
curr=count+ind-index;
current[index<<1]=&Time_comp[count];
current[(index<<1)+1]=&Time_comp[curr];
}
for (index=1; index<NUM; index++)
{
count=ind+index;
if(count>NUM) break;
// Выбор минимальной сложности умножения заданного количества матриц
value=4294967294; // максимальное число типа long int
for(curr=index; curr<count; curr++)
{
min=(curr-index)<<1;
temp=*current[min]++ + *current[max-min]++ + Sizes[index-1]*Sizes[curr]*Sizes[count];
if (value > temp) value=temp;
}
*ptr++=value; // Заполнение текущего элемента матрицы Time_comp
}
}
} // Конец main
Рассмотрим характерный пример умножения некоторого количества матриц.
Пример 1.8. Предположим, что требуется найти произведение шести матриц
M = M1 M2 M3 M4 M5 M6,
(157) (78) (846) (462) (239) (3938)
соответствующие размеры которых указаны под матрицами. Здесь величины r0, r1, r2, r3, r4, r5, r6 соответственно равны 15, 7, 8, 46, 2, 39, 38. В результате работы алгоритма 7 получается матрица минимальных сложностей умножения, показанная в таблице 4. Из матрицы видно, что минимальная сложность умножения данных шести матриц равна 5162. Из этой же таблицы несложно получить и порядок умножения матриц. Для этого надо приписать к каждой клетке таблицы то значение k, на котором достигается минимум (1.11). Для удобства полученные значения k показаны в маленьких левых нижних клетках, а в верхних маленьких клетках показаны индексы i и j из (1.11).
Таблица 4. Сложности вычисления произведений MiMi+1…Mj
11 |
0 |
22 |
0 |
33 |
0 |
44 |
0 |
55 |
0 |
66 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
||||||
12 |
840 |
23 |
2576 |
34 |
736 |
45 |
3588 |
56 |
2964 |
|
|
1 |
2 |
3 |
4 |
5 |
|||||||
13 |
6360 |
24 |
848 |
35 |
1360 |
46 |
6224 |
|
|
|
|
2 |
2 |
4 |
5 |
||||||||
14 |
1058 |
25 |
1394 |
36 |
4308 |
|
|
|
|
|
|
1 |
4 |
4 |
|||||||||
15 |
2228 |
26 |
4344 |
|
|
|
|
|
|
|
|
4 |
4 |
||||||||||
16 |
5162 |
|
|
|
|
|
|
|
|
|
|
4 |
Теперь рассмотрим последнюю клетку таблицы
16 |
5162 |
4 |
Цифры в левом верхнем углу i=1, j=6 показывают, что данная клетка показывает минимальную сложность умножения матриц с первой по шестую. Эта сложность равна 5162. Цифра k=4 показывает оптимальную расстановку скобок, а именно нужно умножить сначала матрицы (M1M2…M4), далее матрицы (M5M6), после чего найти произведение полученных сомножителей. Чтобы найти оптимальный способ умножения (M1M2…M4) необходимо найти в таблице ячейку с параметрами i=1, j=4. Число k=1 в этой ячейке указывает на то, что сначала необходимо найти произведение матриц (M2M3M4), а далее умножить его на M1. И так далее. В результате получается следующий способ оптимального умножения матриц с точки зрения сложности вычислений (и далеко не очевидный при самостоятельном поиске варианта расстановки скобок):
Mопт = (M1 (M2 (M3 M4))) (M5 M6).
(157) (78) (846) (462) (239) (3938)
Если теперь сравнить полученный результат с вариантом, когда матрицы умножаются без расстановки скобок последовательно в порядке записи
M = M1 M2 M3 M4 M5 M6,
то получится сложность умножения, равная 31140. С другой стороны, если попробовать найти полученное оптимальное расположение скобок простым перебором всех возможных вариантов, то получится задача сложности зачастую превышающей сложность самой задачи умножения матриц.