是否有内置的matlab计算二次形式(x'*A*x)?

dan*_*ain 3 performance matlab linear-algebra quadratic

非常直截了当的问题:给定N×N对称矩阵A和N向量x,是否有内置的Matlab函数来计算x'*A*x?即,而不是y = x'*A*x,有一个功能quadraticformst y = quadraticform(A, x)

显然我可以做y = x'*A*x,但我需要表现,似乎应该有一种方法来利用

  1. A 是对称的
  2. 左右乘数是相同的向量

如果没有一个内置函数,是否有比这更快的方法x'*A*x?或者,Matlab解析器是否足够智能优化x'*A*x?如果是这样,你能指点我在文件中的一个地方验证这个事实吗?

kol*_*kol 6

我找不到这样的内置函数,我知道为什么.

y=x'*A*x可以写成的总和n^2而言A(i,j)*x(i)*x(j),在那里ij从运行1n(其中A是一个nxn矩阵).A是对称的:A(i,j) = A(j,i)为了所有ij.由于对称性,每个术语在总和中出现两次,除了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);
}
Run Code Online (Sandbox Code Playgroud)

我将此代码保存为"quadraticform.c",并使用Matlab编译:

mex -O quadraticform.c
Run Code Online (Sandbox Code Playgroud)

我写了一个简单的性能测试这一功能具有x"比一个 X:

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');
Run Code Online (Sandbox Code Playgroud)

结果非常有趣.两者的运行时间x'*A*xquadraticform(A,x)收敛O(n^2),但前者有一个较小的因素:

quadraticformtest.png