问题
Given a matrix A
that represents polynomials in each column. How can the roots of each polynomial be calculated efficiently without loops?
回答1:
Here's a comparison between 3 methods:
- A simple loop through all the rows, using
roots
on each row. - A completely loopless approach, based on YBE's idea of using a block-diagonal matrix,
using
sparse
as an intermediate - A simple loop through all the rows, but this time using "inlined" code from
roots
.
The code:
%// The polynomials
m = 15;
n = 8;
N = 1e3;
X = rand(m,n);
%// Simplest approach
tic
for mm = 1:N
R = zeros(n-1,m);
for ii = 1:m
R(:,ii) = roots(X(ii,:));
end
end
toc
%// Completely loopless approach
tic
for mm = 1:N
%// Indices for the scaled coefficients
ii = repmat(1:n-1:m*(n-1), n-1,1);
jj = 1:m*(n-1);
%// Indices for the ones
kk = bsxfun(@plus, repmat(2:n-1, m,1), (n-1)*(0:m-1).'); %'
ll = kk-1;
%// The block diagonal matrix
coefs = -bsxfun(@rdivide, X(:,2:end), X(:,1)).'; %'
one = ones(n-2,m);
C = full(sparse([ii(:); kk(:)], [jj(:); ll(:)],...
[coefs(:); one(:)]));
%// The roots
R = reshape(eig(C), n-1,m);
end
toc
%// Simple loop, roots() "inlined"
tic
R = zeros(n-1,m);
for mm = 1:N
for ii = 1:m
A = zeros(n-1);
A(1,:) = -X(ii,2:end)/X(ii,1);
A(2:n:end) = 1;
R(:,ii) = eig(A);
end
end
toc
The results:
%// m=15, n=8, N=1e3:
Elapsed time is 0.780930 seconds. %// loop using roots()
Elapsed time is 1.959419 seconds. %// Loopless
Elapsed time is 0.326140 seconds. %// loop over inlined roots()
%// m=150, n=18, N=1e2:
Elapsed time is 1.785438 seconds. %// loop using roots()
Elapsed time is 110.1645 seconds. %// Loopless
Elapsed time is 1.326355 seconds. %// loop over inlined roots()
Of course, your mileage may vary, but the general message should be clear: The old advice of avoiding loops in MATLAB is just that: OLD. It just no longer applies to MATLAB versions R2009 and up.
Vectorization can still be a good thing though, but certainly not always. As in this case: profiling will tell you that most time is spent on computing the eigenvalues for the block-diagonal matrix. The algorithm underlying eig
scales as N³
(yes, that is a three), plus it cannot take advantage of sparse matrices in any way (like this block-diagonal one), making the approach a poor choice in this particular context.
Loops are your friend here ^_^
Now, this is of course based on eig()
of the companion matrix, which is a nice and simple method to compute all roots in one go. There are of course many more methods to compute roots of polynomials, each with their own advantages/disadvantages. Some are a lot faster, but aren't so good when a few of the roots are complex. Others are a lot faster, but need a fairly good initial estimate for every root, etc. Most other rootfinding methods are usually a lot more complicated, which is why I left these out here.
Here is a nice overview, and here is a more in-depth overview, along with some MATLAB code examples.
If you're smart, you should only dive into this material if you need to do this computation millions of times on a daily basis for at least the next few weeks, otherwise, it's just not worth the investment.
If you're smarter, you'll recognize that this will undoubtedly come back to you at some point, so it's worthwhile to do anyway.
And if you're an academic, you master all the root-finding methods so you'll have a giant toolbox, so you can pick the best tool for the job whenever a new job comes along. Or even dream up your own method :)
回答2:
You can use arrayfun in combination with roots, which will give you the results in terms of cell arrays.
n = size(A,2);
t = arrayfun(@(x)roots(A(:,x)), 1:n, 'UniformOutput', 0);
You can then use cell2mat to convert it to a matrix. Either: r = cell2mat(t)
, or
r = cell2mat(arrayfun(@(x)roots(A(:,x)), 1:n, 'UniformOutput', 0));
回答3:
Practically what roots
does is to find the eigenvalues of the companion matrix.
roots(p) = eig(compan(p))
So here is my example that constructs a block-diagonal matrix out of the companion matrices of each polynomial, than finds the eigenvalues of the block-diagonal matrix.
>> p1=[2 3 5 7];
>> roots(p1)
ans =
-0.0272 + 1.5558i
-0.0272 - 1.5558i
-1.4455
>> eig(compan(p1))
ans =
-0.0272 + 1.5558i
-0.0272 - 1.5558i
-1.4455
>> p2=[1 2 9 5];
>> roots(p2)
ans =
-0.6932 + 2.7693i
-0.6932 - 2.7693i
-0.6135
>> p3=[5 1 4 7];
>> roots(p3)
ans =
0.3690 + 1.1646i
0.3690 - 1.1646i
-0.9381
>> A=blkdiag(compan(p1),compan(p2),compan(p3))
A =
-1.5000 -2.5000 -3.5000 0 0 0 0 0 0
1.0000 0 0 0 0 0 0 0 0
0 1.0000 0 0 0 0 0 0 0
0 0 0 -2.0000 -9.0000 -5.0000 0 0 0
0 0 0 1.0000 0 0 0 0 0
0 0 0 0 1.0000 0 0 0 0
0 0 0 0 0 0 -0.2000 -0.8000 -1.4000
0 0 0 0 0 0 1.0000 0 0
0 0 0 0 0 0 0 1.0000 0
>> eig(A)
ans =
-0.0272 + 1.5558i
-0.0272 - 1.5558i
-1.4455
-0.6932 + 2.7693i
-0.6932 - 2.7693i
-0.6135
0.3690 + 1.1646i
0.3690 - 1.1646i
-0.9381
来源:https://stackoverflow.com/questions/20163940/calculate-roots-of-multiple-polynomials