Изменения
Опечатка
{{Задача|definition = Дана последовательность из <tex>n<//Не окончательный вариант --[[Участник:GosuGDR|GosuGDR]] 07:02tex> матриц, 10 декабря 2011 (MSK)требуется найти самый эффективный способ их перемножения.}}
[[Правильные скобочные последовательности | Расстановок скобок]] достаточно много и их количество очень быстро растет. Точное количество всевозможных вариантов равно <tex>n</tex>–ому [[Числа Каталана | числу Каталана]].
Однако, порядок в котором расставляются скобки между матрицами повлияет на количество арифметических операций, которые потребуются на вычисление ответа, или, другими словами, на ''эффективность''.
Например, предположим, что <tex>\dim{A}= 10 \times 30</tex>, <tex>\dim{B} = 30 \times 5</tex>, <tex>\dim{C} = 5 \times 60</tex>. Тогда:
: Для <tex> (A \times B)\times C</tex> будет <tex>(10\times30\times5) + (10\times5\times60) = 1500 + 3000 = 4500</tex> операций
: Для <tex> A \times(B \times C)</tex> будет <tex>(30\times5\times60) + (10\times30\times60) = 9000 + 18000 = 27000</tex> операций.
Как мы видим, первый способ гораздо эффективней.
== Решение задачи ==
== Решение динамическим программированием = Перебор всех вариантов ===
Или другими словами, давайте обозначим через <tex>f(i, j)</tex> минимальное количество скалярных умножений для вычисления матрицы <tex>M_{i..j}</tex>, то получаем следующее рекуррентное соотношение:<tex> f(''AB''i,j)''C'' = (10\left \{ \begin{array}{ll} 0, ×30×5) + (10×5×60) = 1500 + 3000 i= 4500 операцийj \\:''A'' \min\limits_{i \leqslant k < j}{(''BC'') = f(30×5×60i,k) + f(10×30×60k+1,j) = 9000 + 18000 = 27000 операцийp_{i-1}p_kp_j)} & i < j \end{array} \right.</tex>
'''int''' dp[][] <font color="green">// dp[i][j] — ответ на отрезке [i, j)</font> '''int''' v[] <font color== Динамическое программирование ==="green">// Массив v[] — хранит все размеры матриц по порядку // Так как у нас размеры соседних матриц по вертикали и горизонтали совпадают, то они занесены в этот массив однократноОдно // l — включая в отрезок, r — исключая из простых решений — это меморизацияотрезка. Каждый разИзначально l = 0, когда мы считаем минимальную стоимость перемножения определенной подпоследовательностиr = n, давайте мы будем запоминать ответ. Если мы когда либо ещё раз захотим посчитать это ещё разгде n {{---}} длина последовательности</font> '''int''' matrixChainMultiplication('''int''' l, то мы уже будет иметь ответ и не будем пересчитывать. Поскольку существует всего около '''int''' r) '''if''' dp[l][r] == -1 <mathfont color="green">n^2/2/ Если значение динамики не посчитано</mathfont>, где '''if'n'' — это количество матрицl == r - 1 dp[l][r] = 0 <font color="green"> // Если у нас подотрезок длины 1, то память занимаемая программой будет не так велика. Можно сказать, что с помощью этого простого трюка мы уменьшили асимптотику алгоритма с O(<math>2nколичество операций для перемножения равно нулю</mathfont>) до O( '''else''' dp[l][r] = <mathtex>n^3\infty</mathtex> '''for''' i = l + 1 '''to''' r - 1 dp[l][r] = min(dp[l][r], v[l] * v[i] * v[r] + matrixChainMultiplication(l, i)+ matrixChainMultiplication(i, что является достаточно эффективным для реальных приложений.r)) '''return''' dp[l][r]
[[Категория: Дискретная математика и алгоритмы]]
[[Категория:Динамическое_программирование]]