AXT> А, в общем, я ступил, надо было сразу выложить. Умножаем столбец на матрицу.
Для первой.
29 | 55 | 74 | 84 |
74 | 74 | 0 | -74 |
84 | -29 | -74 | 55 |
55 | -84 | 74 | -29 |
Реализация в лоб (не считая расходов на циклы)
R0 = 29*V0+55*V1+74*V2+84*V3;
R1 = 74*V0+74*V1+ 0*V2-74*V3;
R2 = 84*V0-29*V1-74*V2+55*V3;
R3 = 55*V0-84*V1+74*V2-29*V3;
16 умножений, 12 сложений.
Введём промежуточные переменные и немного соптимизируем
I0 = 29*V0; I1 = 55*V0;
J0 = 29*V0; J1 = 55*V1;
K0 = 74*V2;
L0 = 29*V3; L1 = 55*V3;
R0 = I0 + I1 + K0 + L0 + L1;
R1 = 74*(V0+V1-V3);
R2 = I0 + I1 - J0 - K0 + L1;
R3 = I1 - J0 - J1 + K0 - L0;
8 умножений, 14 сложений.
64 | 64 | 64 | 64 |
83 | 36 | -36 | -83 |
64 | -64 | -64 | 64 |
36 | -83 | 83 | -36 |
Реализация в лоб (не считая расходов на циклы)
R0 = 64*V0+64*V1+64*V2+64*V3;
R1 = 83*V0+36*V1-36*V2-83*V3;
R2 = 64*V0-64*V1-64*V2+64*V3;
R3 = 36*V0-83*V1+83*V2-36*V3;
16 умножений, 12 сложений.
Очевидные упрощения:
R0 = 64*(V0+V1+V2+V3);
R1 = 83*(V0-V3)+36*(V1-V2);
R2 = 64*(V0-V1-V2+V3);
R3 = 36*(V0-V3)-83*(V1-V2);
Можно ввести две промежуточные:
I0 = V0 - V3; I1 = V1 - V2;
R0 = 64*(V0+V1+V2+V3);
R1 = 83*I0+36*I1;
R2 = 64*(V0-V1-V2+V3);
R3 = 36*I0-83*I1;
6 умножений, 10 сложений
В принципе, если компилятор поддерживает преобразование дерева вычислений, то он это найдёт. В режиме C++, но в C стиле это будет выглядеть как:
code text
inline void mul1( const int* v, int*& r )
{
// no code would be generated, we just introduce aliases
const int& V0 = v[ 0 ];
const int& V1 = v[ 1 ];
const int& V2 = v[ 2 ];
const int& V3 = v[ 3 ];
const int I0 = 29 * V0;
const int I1 = 55 * V0;
const int J0 = 29 * V0;
const int J1 = 55 * V1;
const int K0 = 74 * V2;
const int L0 = 29 * V3;
const int L1 = 55 * V3;
r[ 0 ] = I0 + I1 + K0 + L0 + L1;
r[ 1 ] = 74 * ( V0 + V1 - V3 );
r[ 2 ] = I0 + I1 - J0 - K0 + L1;
r[ 3 ] = I1 - J0 - J1 + K0 - L0;
}
inline void mul2( const int* v, int*& r )
{
// no code would be generated, we just introduce aliases
const int& V0 = v[ 0 ];
const int& V1 = v[ 1 ];
const int& V2 = v[ 2 ];
const int& V3 = v[ 3 ];
const int I0 = V0 - V3;
const int I1 = V1 - V2;
r[ 0 ] = 64 * ( V0 + V1 + V2 + V3 );
r[ 1 ] = 83 * I0 + 36 * I1;
r[ 2 ] = 64 * ( V0 - V1 - V2 + V3 );
r[ 3 ] = 36 * I0 - 83 * I1;
}
В общем, это примерно то же самое, что и в указанном Татариным алгоритме Штрассена (уменьшение количества умножений), только там про матрицу ничего не известно, а здесь всё известно. Ну и малые размеры вектора и матрицы позволяют рекурсию заменить развёрткой. А inline вместо вызова ф-ции положит эти инструкции прямо в нужное место. Кода получится больше, но работать будет быстрее. Можно попросить компилятор не оформлять стек, тогда inline будет ещё быстрее.
PS Для мат преоброзований матрицы надо почитать стандарт и немного теории, т.к. это всё похоже на фильтры. А там нельзя просто так переставлять строки и столбцы, т.к. последствия могут быть весьма интересными.