Ответ 1
Одна проблема с кодом Штрассена очевидна - у меня нет точки отсечения, который переключается на обычный MM.
Справедливости ради следует сказать, что рекурсия до 1 пункта является основной проблемой (если не всей). Попытка угадать на других узких местах производительности, не обращаясь к этому, почти спорна из-за массивного удара производительности, который он приносит. (Другими словами, вы сравниваете Яблоки с апельсинами.)
Как обсуждалось в комментариях, выравнивание кеша может иметь эффект, но не этот масштаб. Furthemore, выравнивание кеша, скорее всего, повредит регулярному алгоритму больше, чем алгоритм Штрассена, поскольку последний не обращает внимания на кеш.
void strassen(int **a, int **b, int **c, int tam) {
// trivial case: when the matrix is 1 X 1:
if (tam == 1) {
c[0][0] = a[0][0] * b[0][0];
return;
}
Это слишком мало. Хотя алгоритм Штрассена имеет меньшую сложность, он имеет гораздо большую константу Big-O. Во-первых, у вас есть служебный вызов функции вплоть до 1 элемента.
Это аналогично использованию слияния или быстрой сортировки и рекурсии вплоть до одного элемента. Чтобы быть эффективными, вам нужно остановить рекурсию, когда размер станет небольшим и вернуться к классическому алгоритму.
В режиме быстрой сортировки/слияния вы вернетесь к сортировке вставки или выбора с низкой загрузкой O(n^2)
. Здесь вы возвращаетесь к нормальной матрице O(n^3)
.
Порог, который вы отбрасываете классическому алгоритму, должен быть настраиваемым порогом, который, вероятно, будет зависеть от аппаратного обеспечения и способности компилятора оптимизировать код.
Для чего-то вроде умножения Штрассена, где преимущество только O(2.8074)
над классическим O(n^3)
, не удивляйтесь, если этот порог окажется очень высоким. (тысячи элементов?)
В некоторых приложениях может быть много алгоритмов с уменьшением сложности, но увеличение Big-O. В результате несколько алгоритмов становятся оптимальными при разных размерах.
Большое целочисленное умножение - это печально известный пример:
- Умножение школы: O (N ^ 2), оптимальное для < ~ 100 цифр *
- Умножение Карацубы: O (N ^ 1.585) быстрее, чем указано выше, на ~ 100 цифр *
- Toom-Cook 3-way: O (N ^ 1.465) быстрее, чем Карацуба на 3000 знаков *
- FFT с плавающей запятой: O ( > N log (N)) быстрее, чем Karatsuba/Toom-3 на ~ 700 цифр *
- Алгоритм Schönhage-Strassen (SSA): O (N log (n) loglog (n)) быстрее, чем FFT при ~ a миллиард цифр *
- Теоретико-множественное преобразование с фиксированной шириной: O (N log (n) быстрее, чем SSA на ~ несколько миллиардов цифр? *
* Обратите внимание, что эти примерные пороговые значения являются приблизительными и могут сильно варьироваться - часто более чем в 10 раз.