How to update the length of a vector in matlab if statement - matlab

I'm using MATLAB to write a function, which can find all elements in vector v that are equal to scalar a, and change each one of them to [b c], finally return the new vector w. If v = [1 2 5], a = 2, b = 4, c = 3, then the result is w = [1 4 3 5].
Below is my code
function w = change(v, a , b ,c )
for ii = 1:length(v)
if v(ii) == a
v_1 = v(1:ii-1);
v_2 = v(ii+1:length(v));
v_3 = [b c];
v = [v_1 v_3 v_2];
end
end
w = v;
end
However, the problem is: for statement will only read the length of the v before the first run of the loop, in this way, if the size of vector is increased, then length(v) in if statement will not be updated thus ii can not cover all the element indices. For example, if my input is ([1 2 2 2 3 2 4 5 6 2], 2, 4, 4), then with my code, for will stop at ii = 10, which is length of the old v, and give a wrong result [1 4 4 4 4 4 4 3 4 4 4 5 6 2], which does not change the last element (because ii doesn't cover 15).
My question is: how to update length(v) in if statement? or is there any other better way to finish this task?
Thank you very much.

I think this will be easier if you do it with cell arrays.
function w = change(v, a , b ,c )
cellV = num2cell(v);
cellW = cellfun(#(x)(subFunction(x,a,b,c)),cellV,'uni',0);
w = [cellW{:}];
end
function out = subFunction(val,a,b,c)
if (val == a)
out = [b c];
else
out = x;
end
end

A simpler method than checking each element in a loop is to find where the new elements will go, by using find(v==a) and just loop on those cases (taking care with the indexing!):
s = find(v==a);
v(end+1:end+numel(s))=0;
for ii=1:numel(s)
ind=s(ii)+ii;
v(ind-1:end)=[b c v(ind:end-1)];
end

Related

How to permutate over columns for each row in a matrix in MATLAB?

I have a matrix
A = [1,2;3,4];
I would like to generate a new matrix B, which contains all permutations over the columns for each row.
B = [1,2;2,1;3,4;4,3]
Is there a one-liner solution?
I could only think of a solution incorporating cell arrays, thus I'm not sure, if that is "efficient" at all. Also, have a look at the limitations of perms.
% Input.
A = [1, 2; 3, 4]
% Expected output.
B = [1, 2; 2, 1; 3, 4; 4, 3]
% Calculate output.
C = sortrows(cell2mat(cellfun(#(x) perms(x), mat2cell(A, ones(1, size(A, 1)), 2), 'UniformOutput', false)))
A =
1 2
3 4
B =
1 2
2 1
3 4
4 3
C =
1 2
2 1
3 4
4 3
I found a solution to my own question.
n = 2; % size of permutations
perm_index = perms(1:n); % index of the matrix to perm
perm_length = size(perm_index,1);
data = [3,4;5,6];
data_length = size(data,1);
output_length = perm_length* data_length;
output = reshape(data(:,perm_index), output_length,n);
%Final output
output = [4,3;6,5;3,4;5,6]
I couldn't find any one-liner solution. Hope this one is simpler enough:
A = [1, 2, 3; 4, 5, 6];
B = [];
for i=1:size(A,1)
B = [B ; perms(A(i, :))];
end
Read about the function nchoosek
A = [1 2 3 4] ;
B = nchoosek(A,2)

Summation in Matlab without for/while loop

How Do i solve this summation in MATLAB without using for/while loop?
Here C is a vector(1*N matrix), n=length(c) and x is scalar.
c(1)*x^1+c(2)*x^2+c()*x^3+....+c(n)*x^n.
Or can i Create a matrix with all element equal to x but with increasing power, like x, x^2,x^3....?
There are several ways:
result = polyval(fliplr([0 c]), x);
result = sum(c.*x.^(1:numel(c)));
result = sum(c.*cumprod(repmat(x, 1, numel(c))));
As an example, for
c = [3 4 -5 2 3];
x = 9;
any of the above gives
result =
186975
Check:
>> c(1)*x^1+c(2)*x^2+c(3)*x^3+c(4)*x^4+c(5)*x^5
ans =
186975

Find row-wise combinations of a 2 dimensional matrix

I have a matrix:
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
I want to find all row-wise combinations of X. i.e.
A(1) = [2, 6, 1; 3, 8, 1; 4, 7, 1]
A(2) = [2, 6, 1; 3, 8, 1; 6, 2, 1]
:
:
:
Here's what I've tried:
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
p = 3
[m, n] = size(X);
comb = combnk(1:m, p);
[s, t] = size(comb);
c = [X(comb(:,1), :, :) X(comb(:,2), :, :) X(comb(:,3), :, :)];
This gives me a matrix like:
c = 2 6 1 3 8 1 4 7 1
2 6 1 3 8 1 6 2 1
2 6 1 3 8 1 6 4 1
I want to apply the concatenate matrix option to obtain c to make it dynamic depending on value of p but I'm not sure how to use it. I don't want to use For loops. Please help me out.
This is fully vectorized, so it should be fast:
n = 3; %// number of rows to pick each time
ind = reshape(nchoosek(1:size(X,1), n).', [], 1); %'// indices of combinations
A = permute(reshape(X(ind,:).', size(X,2), n, []), [2 1 3]);
The result is
A(:,:,1)
ans =
2 6 1
3 8 1
4 7 1
A(:,:,2)
ans =
2 6 1
3 8 1
6 2 1
etc.
Should you need the result in the form of a cell array, you can convert A from 3D-array to cell array this way:
A = mat2cell(A, size(A,1), size(A,2), ones(1,size(A,3)));
Your thinking is pretty close. This code does the job. I put comments in code, which should be easy to read.
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
p = 3;
%// List all combinations choosing 3 out of 1:8.
v = nchoosek(1:size(X,1), p);
%// Use each row of v to create the matrices, and put the results in an cell array.
%// This is the A matrix in your question.
A = arrayfun(#(k)X(v(k,:), :), 1:size(v,1), 'UniformOutput', false);
%// And you can concatenate A vertically to get c.
flatA = cellfun(#(x)reshape(x, 1, []), A, 'UniformOutput', false);
c = vertcat(flatA{:});
PS: From my understanding I thought the result you wanted was A, which is an easy to use cell array. But I added an extra step to get c exactly as in your question just in case.
Disclaimer: arrayfun and cellfun are pretty much equivalent to for loop in terms of performance.
You can do it using reshape and a bunch of transposes since Matlab is column-major ordered:
c = reshape(X(comb',:)',9,[])'
or if you want a 3D matrix:
A = permute(reshape(X(comb',:)',3,3,[])', [2,1,3])

how to omit for loop when there is constrains

I have the following two arrays:
A = [1 2;3 4] and B = [1 5 4]
I want to do the following operation:
for each element of A(call it A(i))
for each element of B~=b do
( (A(i) - 1)/(b-1) ) * ( (A(i) - 5)/(b-5) ) * ( (A(i)- 4)/(b-4) )
end
end
It means that, sometimes the numerator equals to zero, so the product should be zeros. And I want to do the operation for the elements of B which are not equal to the b in denominator to not make it Inf.
How can I do this for the whole matrix A instead of using for loop?
Code
A = [1 2;3 4];
B = [1 5 4];
m1 = bsxfun(#minus,A,permute([1 5 4],[3 1 2]));
m2 = bsxfun(#minus,B,permute([1 5 4],[3 1 2]));
for k1=1:size(A,1)
for k2=1:size(A,2)
t2 = squeeze(bsxfun(#rdivide,m1(k1,k2,:),m2));
t2(1:size(t2,1)+1:end)=1;
A1(k1,k2) = prod(t2(:)); %%// Output
end
end
Output
A1 =
0 -0.2500
-0.1111 0
You can remove the nested loops, but at least two issues there -
You would be going to 4th and 5th dimension with it, using bsxfun. So, debugging would be tough.
bsxfun with higher dimensions to my knowledge seems to get slower.
You could just do the operation, and correct later:
C = (A-1)./(B-1) .* (A-5)./(B-5) .* (A-4)./(B-4)
C(isinf(C)) = 0;
or
C(B==b) = 0;
Possibly you'd need bsxfun, I'm not clear on the size of the output you want...

how to repeat element matrix in matlab

How to repeat
A = [ 1 2 ;
3 4 ]
repeated by
B = [ 1 2 ;
2 1 ]
So I want my answer like matrix C:
C = [ 1 2 2;
3 3 4 ]
Thanks for your help.
Just for the fun of it, another solution making use of arrayfun:
res = cell2mat(arrayfun(#(a,b) ones(b,1).*a, A', B', 'uniformoutput', false))'
This results in:
res =
1 2 2
3 3 4
To make this simple, I assume that you're only going to add more columns, and that you've checked that you have the same number of columns for each row.
Then it becomes a simple combination of repeating elements and reshaping.
EDIT I've modified the code so that it also works if A and B are 3D arrays.
%# get the number of rows from A, transpose both
%# A and B so that linear indexing works
[nRowsA,~,nValsA] = size(A);
A = permute(A,[2 1 3]);
B = permute(B,[2 1 3]);
%# create an index vector from B
%# so that we know what to repeat
nRep = sum(B(:));
repIdx = zeros(1,nRep);
repIdxIdx = cumsum([1 B(1:end-1)]);
repIdx(repIdxIdx) = 1;
repIdx = cumsum(repIdx);
%# assemble the array C
C = A(repIdx);
C = permute(reshape(C,[],nRowsA,nValsA),[2 1 3]);
C =
1 2 2
3 3 4