ind2sub for nonzero elements of triangular matrix - matlab

I just wanted to simply find the index of (row, col) that is a minimum point of a matrix A. I can use
[minval, imin] = min( A(:) )
and MATLAB built in function
[irow, icol] = ind2sub(imin);
But for efficiency reason, where matrix A is trigonal, i wanted to implement the following function
function [i1, i2] = myind2ind(ii, N);
k = 1;
for i = 1:N
for j = i+1:N
I(k, 1) = i; I(k, 2) = j;
k = k + 1;
end
end
i1 = I(ii, 1);
i2 = I(ii, 2);
this function returns 8 and 31 for the following input
[irow, icol] = myind2ind(212, 31); % irow=8, icol = 31
How can I implement myind2ind function more efficient way without using the internal "I"?

The I matrix can be generated by nchoosek.
For example if N = 5 we have:
N =5
I= nchoosek(1:N,2)
ans =
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
so that
4 repeated 1 times
3 repeated 2 times
2 repeated 3 times
1 repeated 4 times
We can get the number of rows of I with the Gauss formula for triangular number
(N-1) * (N-1+1) /2 =
N * (N -1) / 2 =
10
Given jj = size(I,1) + 1 - ii as a row index I that begins from the end of I and using N * (N -1) / 2 we can formulate a quadratic equation:
N * (N -1) / 2 = jj
(N^2 -N)/2 =jj
So
N^2 -N - 2*jj = 0
Its root is:
r = (1+sqrt(8*jj))/2
r can be rounded and subtracted from N to get the first element (row number of triangular matrix) of the desired output.
R = N + 1 -floor(r);
For the column number we find the index of the first element idx_first of the current row R:
idx_first=(floor(r+1) .* floor(r)) /2;
The column number can be found by subtracting current linear index from the linear index of the first element of the current row and adding R to it.
Here is the implemented function:
function [R , C] = myind2ind(ii, N)
jj = N * (N - 1) / 2 + 1 - ii;
r = (1 + sqrt(8 * jj)) / 2;
R = N -floor(r);
idx_first = (floor(r + 1) .* floor(r)) / 2;
C = idx_first-jj + R + 1;
end

Related

Vectorize the sum of outer products of coresponding columns of two matrices using Matlab/Octave

Suppose I have two matrices A and B which are made up of column vectors as follows.
A = [a_1,a_2,...,a_N];
B = [b_1,b_2,...,b_N];
Is there any way to vectorize the calculation of the sum of outer products for every column in A with the corresponding column in B. Here is my non-vectorized solution.
S = zeros(size(A,1), size(B,1));
for n=1:N
S = S + A(:,n)*B(:,n)'; % S = S + a_n * b_n'
end
Any help would be greatly appreciated.
you are not clear on what N is, but I assume that N = number of column vectors - which means you are simply doing A * B'
A = rand(3,4);
B = rand(3,4);
N = size(A,2);
S = zeros(size(A,1), size(B,1));
for n=1:N
S = S + A(:,n)*B(:,n)'; % S = S + a_n * b_n'
end
%Check that you are doing A*B'
S == A*B'
>> ans =
1 1 1
1 1 1
1 1 1

How to oppositely order two vectors in Matlab?

I have the code below for oppositely ordering two vectors. It works, but I want to specify the line
B_diff(i) = B(i) - B(i+1);
to hold true not just for only
B_diff(i) = B(i) - B(i+1); but for
B_diff(i) = B(i) - B(i+k); where k can be any integer less than or equal to n. The same applies to "A". Any clues as to how I can achieve this in the program?
For example, I want to rearrange the first column of the matrix
A =
1 4
6 9
3 8
4 2
such that, the condition should hold true not only for
(a11-a12)(a21-a22)<=0;
but also for all
(a11-a13)(a21-a23)<=0;
(a11-a14)(a21-a24)<=0;
(a12-a13)(a22-a23)<=0;
(a12-a14)(a22-a24)<=0; and
(a13-a14)(a23-a24)<=0;
## MATLAB CODE ##
A = xlsread('column 1');
B = xlsread('column 2');
n = numel(A);
B_diff = zeros(n-1,1); %Vector to contain the differences between the elements of B
count_pos = 0; %To count the number of positive entries in B_diff
for i = 1:n-1
B_diff(i) = B(i) - B(i+1);
if B_diff(i) > 0
count_pos = count_pos + 1;
end
end
A_desc = sort(A,'descend'); %Sort the vector A in descending order
if count_pos > 0 %If B_diff contains positive entries, divide A_desc into two vectors
A_less = A_desc(count_pos+1:n);
A_great = sort(A_desc(1:count_pos),'ascend');
A_new = zeros(n,1); %To contain the sorted elements of A
else
A_new = A_desc; %This is then the sorted elements of A
end
if count_pos > 0
A_new(1) = A_less(1);
j = 2; %To keep track of the index for A_less
k = 1; %To keep track of the index for A_great
for i = 1:n-1
if B_diff(i) <= 0
A_new(i+1) = A_less(j);
j = j + 1;
else
A_new(i+1) = A_great(k);
k = k + 1;
end
end
end
A_diff = zeros(n-1,1);
for i = 1:n-1
A_diff(i) = A_new(i) - A_new(i+1);
end
diff = [A_diff B_diff]
prod = A_diff.*B_diff
The following code orders the first column of A opposite to the order of the second column.
A= [1 4; 6 9; 3 8; 4 2]; % sample matrix
[~,ix]=sort(A(:,2)); % ix is the sorting permutation of A(:,2)
inverse=zeros(size(ix));
inverse(ix) = numel(ix):-1:1; % the un-sorting permutation, reversed
B = sort(A(:,1)); % sort the first column
A(:,1)=B(inverse); % permute the first column according to inverse
Result:
A =
4 4
1 9
3 8
6 2

Gauss-Seidel code not converging on solution

I am unable to get converging values using a Gauss-Seidel algorithm
Here is the code:
A = [12 3 -5 2
1 6 3 1
3 7 13 -1
-1 2 -1 7];
b = [2
-3
10
-11];
ep = 1e-8;
[m, n] = size(A);
[n, p] = size(b);
x = zeros(n, 1001);
x(:, 1) = []
for k=0:1000
ka = k + 1;
if ka == 1001
break;
end
xnew = zeros(n,1);
for i=1:n
sum = 0;
j = 1;
while j < i
s1 = s1 + A(i,j) * x(j, ka + 1);
j = j + 1;
end
j = i + 1;
while j <= n
sum = sum + A(i,j) * x(j, ka);
j = j + 1;
end
xnew(i) = (b(i) - sum) / A(i, i);
% if result is within error bounds exit loop
if norm(b - A * xnew, 2) < ep * norm(b, 2)
'ending'
break
end
end
x(:,ka + 1) = xnew;
end
I cannot get the A * xnew to converge on b what am I doing wrong?
I have tried running this changing the syntax several times, but I keep getting values that are way off.
Thanks!
Gabe
You have basically two problems with your code:
(1) You are using two different variables "sum" and "s1". I replaced it by mySum. By the way, dont use "sum", since there is a matlab function with this name.
(2) I think there is also a problem in the update of x;
I solved this problem and I also tried to improve your code:
(1) You dont need to save all "x"s;
(2) It is better to use a "while" than a for when you dont know how many iterations you need.
(3) It is good to use "clear all" and "close all" in general in order to keep your workspace. Sometimes old computations may generate errors. For instance, when you use matrices with different sizes and the same name.
(4) It is better to use dot/comma to separate the lines of the matrices
You still can improve this code:
(1) You can test if A is square and if it satisfies the conditions necessary to use this numerical method: to be positive definite or to be diagonally dominant.
clear all
close all
A = [12 3 -5 2;
1 6 3 1;
3 7 13 -1;
-1 2 -1 7];
b = [2;
-3;
10;
-11];
ep = 1e-8;
n = length(b); % Note this method only works for A(n,n)
xNew=zeros(n,1);
xOld=zeros(n,1);
leave=false;
while(~leave)
xOld=xNew;
for i=1:n
mySum = 0;
j = i + 1;
while j <= n
mySum = mySum + A(i,j) * xOld(j,1);
j = j + 1;
end
j = 1;
while j < i
mySum = mySum + A(i,j) * xNew(j,1);
j = j + 1;
end
mySum=b(i,1)-mySum;
xNew(i,1) = mySum / A(i, i);
end
if (norm(b - A * xNew, 2) < ep * norm(b, 2))
disp('ending');
leave=true;
end
xOld = xNew;
end
xNew

How to vectorize double loop in Matlab?

y = 0;
for m = 0:variable
for n = 0:m
y = y + f(n,m);
end
end
I vectorized the inner loop this way,
y = 0;
for m = 0:variable
n = 0:m
y = y + f(n,m);
end
This resulted in around 60% speed increase for my code. How do I also vectorize the outer loop?
You are probably looking for the meshgrid function. It is designed to fill in the sort of m by n combinations that it looks like you need. For example:
>> m = 1:4;
>> n = 1:3;
>> [mGridValues, nGridValues] = meshgrid(m,n)
mGridValues =
1 2 3 4
1 2 3 4
1 2 3 4
nGridValues =
1 1 1 1
2 2 2 2
3 3 3 3
This is a little more complicated since your inner loop depends on the value of your outer loop. So you will need to mask out the undesired [n, m] pairs (see below).
Modifying the prototype code that you have provided, you would end up with something like this:
[mValues, nValues] = meshgrid(0:variable, 0:variable); %Start with a full combination of values
mask = mValues >= nValues; %Identify all values where m >= n
mValues = mValues(mask); % And then remove pairs which do not
nValues = nValues(mask); % meet this criteria
y = f(nValues, mValues ); %Perform whatever work you are performing here

Matlab: Applying threshold to one dimension in a matrix

I have a matrix M(x,y). I want to apply a threshold in all values in x, such that if x
Example:
M = 1, 2;
3, 4;
5, 6;
If t = 5 is applied on the 1st dimension, the result will be
R = 0, 2;
0, 4;
5, 6;
One way (use M(:,1) to select the first column; M(:,1)<5 returns row indices for items in the first column that are lest than 5))-
> R = M;
> R(M(:,1)<5,1) = 0
R =
0 2
0 4
5 6
Another -
R = M;
[i,j]=find(M(:,1)<5); % locate rows (i) and cols (j) where M(:,1) < 5
% so j is just going to be all 1
% and i has corresponding rows
R(i,1)=0;
To do it in a matrix of arbitrary dimensions:
thresh_min = 5;
M(M < thresh_min) = 0;
The statement M < thresh_min returns indices of M that are less than thresh_min. Then, reindexing into M with these indices, you can set all of these valuse fitting your desired criterion to 0 (or whatever else).