显然我可以做y = x’* A * x,但我需要表现,似乎应该有办法利用
> A是对称的
>左右乘数是相同的向量
如果没有单一的内置函数,是否有比x’* A * x更快的方法?或者,Matlab解析器是否足够智能以优化x’* A * x?如果是这样,你能指点我在文件中的一个地方验证这个事实吗?
我找不到这样的内置函数,我知道为什么.y = x’* A * x可以写成n ^ 2项A(i,j)* x(i)* x(j)之和,其中i和j从1到n(其中A是a) nxn矩阵). A是对称的:对于所有i和j,A(i,j)= A(j,i).由于对称性,每个项在总和中出现两次,除了那些i等于j的项.所以我们有n *(n 1)/ 2个不同的术语.每个都有两个浮点乘法,所以一个朴素的方法总共需要n *(n 1)次乘法.很容易看出,x’* A * x的朴素计算,即计算z = A * x,然后计算y = x’* z,也需要n *(n 1)次乘法.然而,有一种更快的方法来求和我们的n *(n 1)/ 2个不同的项:对于每个i,我们可以分解x(i),这意味着只有n *(n-1)/ 2 3 * n乘法就足够了.但这并没有真正帮助:y = x’* A * x的计算运行时间仍为O(n ^ 2).
因此,我认为二次形式的计算不能比O(n ^ 2)更快地完成,并且因为这也可以通过公式y = x’* A * x来实现,所以没有特殊的优势. “二次型”功能.
===更新===
我在C中写了函数“quadraticform”,作为Matlab扩展:
// y = quadraticform(A, x) #include "mex.h" /* Input Arguments */ #define A_in prhs[0] #define x_in prhs[1] /* Output Arguments */ #define y_out plhs[0] void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { mwSize mA, nA, n, mx, nx; double *A, *x; double z, y; int i, j, k; if (nrhs != 2) { mexErrMsgTxt("Two input arguments required."); } else if (nlhs > 1) { mexErrMsgTxt("Too many output arguments."); } mA = mxGetM(A_in); nA = mxGetN(A_in); if (mA != nA) mexErrMsgTxt("The first input argument must be a quadratic matrix."); n = mA; mx = mxGetM(x_in); nx = mxGetN(x_in); if (mx != n || nx != 1) mexErrMsgTxt("The second input argument must be a column vector of proper size."); A = mxGetPr(A_in); x = mxGetPr(x_in); y = 0.0; k = 0; for (i = 0; i < n; ++i) { z = 0.0; for (j = 0; j < i; ++j) z += A[k + j] * x[j]; z *= x[i]; y += A[k + i] * x[i] * x[i] + z + z; k += n; } y_out = mxCreateDoubleScalar(y); }
我将此代码保存为“quadraticform.c”,并使用Matlab编译:
mex -O quadraticform.c
我写了一个简单的性能测试来比较这个函数和x’Ax:
clear all; close all; clc; sizes = int32(logspace(2, 3, 25)); nsizes = length(sizes); etimes = zeros(nsizes, 2); % Matlab vs. C nrepeats = 100; h = waitbar(0, 'Please wait...'); for i = 1 : nrepeats for j = 1 : nsizes n = sizes(j); A = randn(n); A = (A + A') / 2; x = randn(n, 1); if randn > 0 start = tic; y1 = x' * A * x; etimes(j, 1) = etimes(j, 1) + toc(start); start = tic; y2 = quadraticform(A, x); etimes(j, 2) = etimes(j, 2) + toc(start); else start = tic; y2 = quadraticform(A, x); etimes(j, 2) = etimes(j, 2) + toc(start); start = tic; y1 = x' * A * x; etimes(j, 1) = etimes(j, 1) + toc(start); end; if abs((y1 - y2) / y2) > 1e-10 error('"x'' * A * x" is not equal to "quadraticform(A, x)"'); end; waitbar(((i - 1) * nsizes + j) / (nrepeats * nsizes), h); end; end; close(h); clear A x y; etimes = etimes / nrepeats; n = double(sizes); n2 = n .^ 2.0; i = nsizes - 2 : nsizes; n2_1 = mean(etimes(i, 1)) * n2 / mean(n2(i)); n2_2 = mean(etimes(i, 2)) * n2 / mean(n2(i)); figure; loglog(n, etimes(:, 1), 'r.-', 'LineSmoothing', 'on'); hold on; loglog(n, etimes(:, 2), 'g.-', 'LineSmoothing', 'on'); loglog(n, n2_1, 'k-', 'LineSmoothing', 'on'); loglog(n, n2_2, 'k-', 'LineSmoothing', 'on'); axis([n(1) n(end) 1e-4 1e-2]); xlabel('Matrix size, n'); ylabel('Running time (a.u.)'); legend('x'' * A * x', 'quadraticform(A, x)', 'O(n^2)', 'Location', 'NorthWest'); W = 16 / 2.54; H = 12 / 2.54; dpi = 100; set(gcf, 'PaperPosition', [0, 0, W, H]); set(gcf, 'PaperSize', [W, H]); print(gcf, sprintf('-r%d',dpi), '-dpng', 'quadraticformtest.png');
结果非常有趣. x’* A * x和二次型(A,x)的运行时间收敛到O(n ^ 2),但前者的因子较小: