Шаг 105.
Рекурсия на Python. Множественная рекурсия I: "разделяй и властвуй". Умножение матриц. Алгоритм Штрассена умножения матриц

    На этом шаге мы рассмотрим особенности этого алгоритма.

    Самая затратная по времени арифметическая операция в предыдущих алгоритмах - это скалярное умножение в двух начальных условиях. Оба они, подобно простой итерационной версии с тройным циклом, требуют p*q*r таких умножений. Однако небезынтересно оценить временную сложность для исходных матриц размерности n*n. В этом случае время выполнения можно определить следующей функцией:

            1,                если n ≤ 1,
    T(n) =                                            (6.6)
            8T(n/2) + 4Θ(n2), если n > 1, 
поскольку методы вызывают сами себя восемь раз и должны выполнить четыре сложения матриц стоимостью порядка n2. Поэтому согласно основной теореме (3.28) T(n) ∈ Θ(nlog28) = Θ(n3). Теперь рассмотрим алгоритм Штрассена - известный метод, который сокращает временную сложность до Θ(nlog27) = Θ(n2,807...).

    Метод, как и стандартный алгоритм, разбивает каждую из входных матриц на четыре блока. Таким образом, AB = C можно записать как:

⌈ ⌉ ⌈ ⌉ ⌈ ⌉ A1,1 A1,2 B1,1 B1,2 С1,1 С1,2 = A2,1 A2,2 B2,1 B2,2 С2,1 С2,2 ⌊ ⌋ ⌊ ⌋ ⌊ ⌋

    Цель метода - определить следующие новые матрицы, которые используют всего одну операцию умножения матриц:

    М1 = (A1,1 + A2,2)(B1,1 + B2,2)
    М2 = (A2,1 + A2,2) B1,1
    M3 = A1,1 (B1,2 - B2,2)
    M4 = A2,2 (B2,1 - B1,1)                           (6.7)
    M5 = (A1,1 + A1,2) B2,2
    M6 = (A2,1 - A1,1)(B1,1 + B1,2)
    M7 = (A1,2 - A2,2)(B2,1 + B2,2).

    После чего эти матрицы могут быть сгруппированы так, чтобы сформировать выходные блочные матрицы:

    C1,1 = M1 + M4 - M5 + M7
    C1,2 = M3 + M5                                  (6.8)
    C2,1 = M2 + M4 
    C2,2 = M1 - M2 + M3 + M6.

    Таким образом, в каждом рекурсивном вызове алгоритм выполняет 7 произведений и 18 сложений (или вычитаний). Поэтому оценка времени его выполнения:

            1,                 если n ≤ 1,
    T(n) =                                         (6.9)
            7T(n/2) + 18Θ(n2), если n > 1, 
где T(n) = Θ(nlog27) = Θ(n2,807...). Этот алгоритм для больших значений n может быть быстрее стандартного со временем выполнения Θ(n3). Однако для малых или средних матриц он может быть медленнее из-за больших постоянных множителей, имеющих значение на практике.

    Наконец, теоретически входы этого алгоритма должны быть квадратными матрицами n*n, где n - степень двух. На практике эффективные реализации разбивают матрицы на множество квадратных подматриц и неоднократно применяют этот алгоритм. Более простая, но медленная альтернатива - дополнить (расширить) входные матрицы нулями до размерности 2k*2k.

    На следующем шаге мы рассмотрим задачу укладки тримино.




Предыдущий шаг Содержание Следующий шаг