Is there a way to make this more efficient (Matlab)? - matlab

I've figured out a method that works for my purposes, however I would like to make the solution generalizable to more numbers of trials without having to hard-code a new if-statement for every increase in trials. Right now trials=4 and if I wanted to make trials=5 I would need to write a new if-statement.
if new_A == 1:trials;
outer_matrices{:,1} = A;
end
if new_A == trials+1:trials*2;
outer_matrices{:,2} = A;
end
if new_A == (trials*2)+1:trials*3;
outer_matrices{:,3} = A;
end
if new_A == (trials*3)+1:trials*4;
outer_matrices{:,4} = A;
end
You see the pattern (with trials=5 I would need to multiply by 4 and 5 respectively and then change to outer_matrices{:,5})
I'm sure there's a fairly simple solution/structure that I'm just not thinking of. Thanks in advance!

Try this (no loops or conditionals approach) -
N = 5;%%// Number of cases
ind = find(all(bsxfun(#eq,reshape(1:trials*N,trials,[]),new_A'),1))
outer_matrices{:,ind} = A;

I take it new_A is a vector with trials number of elements. As (trials*n)+1:trials*(n+1) is a vector of the form [trials*n+1, trials*n+2, ... ] you can do:
if ~mod(new_A(1)-1,trials) && all(diff(new_A) == 1)
outerMatrices{(trials+new_A(1)-1)/trials} = A;
end
What it does is:
~mod(new_A(1)-1,trials) uses the modulus operation, to check if the first element in new_A is divisible by trials. If it is, the mod will return 0. The ~ in front of mod means "not", thus if mod returns zero, this will evaluate to "~0 == 1".
all(diff(new_A)==1) checks if all the elements in new_A is a exactly one element apart.

A basic for-loop approach to mirror the code in the question would be:
N=5;
for ii=1:N
if new_A == trials*(ii-1)+1:trials*ii
outer_matrices{:,ii} = A;
end
end

Related

MATLAB - Check matrix to see if two consecutive numbers = 0

I have a matrix being created in MATLAB but need to check if any two consecutive numbers (by row) = 0 and if it does output with a yes or no without showing the answers. Ive put my code below my final loop is returning errors and im not too sure how to go about this.
%%Input positive integer
n=input('Give a positive integer greater than 1: ');
%%Error for below 1
if n<=1
error('The value given is less than or equal to 1')
end
%%loop for vector v
for i=1:n
v(i)=2^i*3;
end
%display vector
v
A=randi([-5,5],n,n);
A
x = 1;
consecutive = false;
for i = 1:25
if (A(x) + A(x+1) = 0);
consecutive = true;
end
end
There's a lot wrong with the code of your final loop:
You set x to 1 and use it as an index into A, but never change it from 1.
As Amit points out, you are using = (used for assignment) when you should be using == (the equality operator).
As Gondrian points out, you are testing if a sum is equal to zero, but from your description it sounds like you should be testing if each value is zero.
Your loop iterates 25 times (i = 1:25), but it's not clear why, since your matrix A is of size n-by-n.
Instead of using a for loop, it's possible to do this with indexing instead. Since you are checking for consecutive zeroes by row, this is what it would look like:
zeroPairs = (A(:, 1:(n-1)) == 0) & (A(:, 2:n) == 0);
consecutive = any(zeroPairs(:));
The term A(:, 1:(n-1)) gets all of the "left" values in each pairwise comparison and the term A(:, 2:n) gets all of the "right" value. These are compared to 0, then combined. This creates an n-by-(n-1) matrix zeroPairs where a value of true indicates where a pair of consecutive zeroes occurs. This matrix is reshaped into a column vector and any is used to check if a value of true is present anywhere.
You will need to change your if block as follows.
if (A(x) + A(x+1) == 0);
consecutive = true;
end
Notice the == instead of just =. The first one is for comparison and the second one is for assignment. This will get rid of the error that you are currently getting. There may be other issues in the algorithm of your code but I did not examine or try to fix that.
btw:
'if any two consecutive numbers (by row) = 0'
If I understand you right, you should try 'if A(x) == 0 && A(x+1) == 0';
because '(A(x) + A(x+1) == 0)' would be true for -5 and 5, as it equals to 0. But -5 and 5 are not two consecutive zeros.
(or even look at the 'diff' function. It will return a 0 if two following numbers are the same)
To show a vectorized, more Matlab-like approach:
v = 0; % sought value
C = 2; % desired number of consecutive values in a row
consecutive = nnz(conv2(double(A==v), ones(1,C))==C)>0;
This compares each entry of A with the value v, and then applies 2D convolution with a row vector of C ones. Any C horizontally-consecutive entries of A with value v will produce an entry equal to C in the convolution result. So we check if the number of such entries is positive.

Matlab recursion. Sum off all odd numbers in a vector

I am trying to implement a recursive function to add the odd numbers in a vector v.
So far this is my attempt
function result = sumOdd(v)
%sum of odd numbers in a vector v
%sumOdd(v)
n = 1;
odds = [];
if length(v) > 0
if mod(v(n),2) == 1
odds(n) = v(n);
v(n) = [];
n = n + 1;
sumOdd(v)
elseif mod(v(n),2) == 0
v(n) = [];
n = n + 1;
sumOdd(v)
end
else
disp(sum(odds))
end
end
This does not work and returns a value of zero. I am new to programming and recursion and would like to know what I'm doing wrong.
Thank you.
There is a better way to solve this in MATLAB:
function result=summOdd(v)
odd_numbers=v(mod(v,2)); % Use logical indexing to get all odd numbers
result=sum(odd_numbers); % Summ all numbers.
end
To give a recursive solution:
When implementing a recursive function, there is a pattern you should always follow. First start with the trivial case, where the recursion stops. In this case, the sum of an empty list is 0:
function result = sumOdd(v)
%sum of odd numbers in a vector v
%sumOdd(v)
if length(v) == 0
result=0;
else
%TBD
end
end
I always start this way to avoid infinite recursions when trying my code. Where the %TBD is placed you have to put your actual recursion. In this case your idea was to process the first element and put all remaining into the recursion. First write a variable s which contains 0 if the first element is even and the first element itself when it is odd. This way you can calculate the result using result=s+sumOdd(v)
function result = sumOdd(v)
%sum of odd numbers in a vector v
%sumOdd(v)
if length(v) == 0
result=0;
else
if mod(v(1),2) == 1
s=v(1);
else
s=0;
end
v(1) = [];
result=s+sumOdd(v);
end
end
Now having your code finished, read the yellow warning the editor gives to you, it tells you to replace length(v) == 0 with isempty(v).
Instead of keeping 'n', you can always delete the first element of v. Also, you need to pass 'odds' as an argument, as you're initializing it as an empty array at each time you call the function (hence the zero output).
The following example seems to do the job:
function result = sumOdd(v,odds)
%sum of odd numbers in a vector v
%sumOdd(v)
if ~isempty(v)
if mod(v(1),2) == 1
odds = [odds;v(1)];
v(1) = [];
sumOdd(v,odds)
elseif mod(v(1),2) == 0
v(1) = [];
sumOdd(v,odds)
end
else
disp(sum(odds))
end
end

How to check for identical Vectors concurrently?

Trying to check for identical vectors. Currently using the isequal function to check for identical ones.
It runs like this.
if isequal (vectorA, vectorB) == 0
It will then run an instruction.
end
if isequal (vectorA, vectorB) == 1
It will run another instruction.
end
I now have a set of vectors more from A to F. Is there anyway to check all of them (B,C,D,F) against Vector A and do the same thing as mentioned?
Meaning
if vectorA matches any of B,C,D,F == 0
The same with the second case where
if vectorA matches any of B,C,D,F == 1
The vectors are constantly changing and this is running in a loop to check for identical vectors. Seems like the isequal function only works between 2 Vectors.
all the vectors are 1xi where i can be a number up to 50
ANY help in this would be greatly apreciated!
I suggest you keep your other vectors in a matrix rather so something like:
a = vectorA;
B = [vectorB, vectorC, vectorD, vectorE];
Then just use a simple for loop:
flag = false;
for k = 1:size(B,2)
flag = flag || isequal(a,B(:,k));
if flag
break
end;
end
or if you prefer a completely vecotrized one-liner over the loop (but in this case, I suspect the early exit clause in the loop might actually provide a performance benefit, depends on your data though):
flag = any(all(bsxfun(#eq,a,B)))
then
if flag
%// do stuff if any was equal
else
%// Do stuff if none were equal to a
BTW if you wanted to check if they ALL match instead of if ANY match then change to:
flag = all(all(bsxfun(#eq,a,B)))
or in the loop change to
flag = true;
for k = 1:size(B,2)
flag = flag && isequal(a,B(:,k));
if ~flag
break
end;
end
This can be done very easily with ismember:
To check whether it a vector matches any of the rows:
v = 1:3
M = [1:3;2:4;3:5]
ismember(v,M,'rows')
To check whether it matches all of the rows, you can extend it by also checking something like:
size(unique(M,'rows'),1)==1
This assumes that the vectors are stacked below each other, but of course it is easily adjusted to match the situation when they are next to each other.
To check if A equals any of B, C, D, F (with possibly different sizes), use cellfun on a cell array containing your vectors:
if any(cellfun(#(x) isequal(A,x), {B,C,D,F}))
If all vectors are row vectors with an equal number columns, it may be faster to use bsxfun on a matrix containing your vectors:
if any(all(bsxfun(#eq, A, [B; C; D; F]).'))

How can I shift array element using a for loop in Matlab?

I'm trying to shift all the elements of an array to the left, so that the first element would become the last element, the second becomes the first, the third the second, etc. I know about the circshift commands, but I would like to do this using a for loop.
Here's what I did.
old=[]
n=length(old)
for i=1;i<(n-1);i=i+1;
for j=2;j<n;j=j+1;
new(j)=old(i)
end
end
But it of course didn't work. I'm having trouble figuring out to make an array of n elements, without specifying n, which is why I used old=[], but I think that created an array of 0 elements.
How can I make this code work?
If you want to avoid specifying the n length of the array, you have to give it as an input argument in a function.
For example you can do something like this:
function new = shiftLeft(old)
n = length(old);
for i =1:n
new(i) = old(mod(i,n)+1);
end
return
So with this one, if you have an array for example old = [1 2 3 4]; you can will get something like new = [2 3 4 1];
mod(a,b) is the modulo operator, you can find more information if you type help mod.
So your irst step is to learn how to specify a for loop in Matlab, what you have is like C syntax. This is not Matlab syntax at all.
The following is how to do it using forloops but this is not good matlab programming. You could easily do it without loops too.
vec = 1:10;
temp = [];
shiftby = 2;
for ii = 1:shiftby %Each iteration shifts by one
temp = vec(end); %Store the last element of vec
for jj = size(vec, 2):-1:2; %inner loop must shift each element from the end to element 2
vec(jj) = vec(jj-1);
end
vec(1) = temp; %put the old end value at the beginning
end
but you could also just do this which is a much more Matlabesque way to code it:
vec = [vec(end - shiftby + 1: end), vec(1:end - shiftby)]

How to make random matrix whose rows must all sum <=11 and which cannot contain consecutive triplets of 0-0-0 in any row

I want to make an array called result, which has dimensions (14,12,10) and has values random[0 2], but it should not contain 0 more than three consecutive times in each row (dimension 2), and the sum of all values in each row must be <= 11.
This is my current approach:
jum_kel = 14;
jum_bag = 12;
uk_pop = 10;
for ii = 1:uk_pop,
libur(:,:,ii) = randint(jum_kel,jum_bag,[0 2]); %#initialis
sum_libur(1,:,ii) = sum(libur(:,:,ii),2); %#sum each row
end
for jj = 1:jum_kel
while sum_libur(1,jj,ii) > 11, %# first constraint : sum each row should be <=11,
libur(jj,:,ii) = randint(1,jum_bag,[0 2])
sum_libur(1,:,ii)= sum(libur(:,:,ii),2);
for kk = 1:jum_bag
if kk>2
o = libur(jj,kk,ii)+libur(jj,kk-1,ii)+libur(jj,kk-2)
while kk>2 && o==0 %# constraint 2: to make matrix will not contain consecutive triplets (0-0-0) in any row.
libur(jj,:,ii) = randint(1,jum_bag,[0 2]);
sum_libur(1,:,ii)= sum(libur(:,:,ii),2);
end
end
end
end
end
but this is extremely slow...Does anyone see a faster method?
(sidenote: A matrix does not have 3 dimensions: that would be a tensor, in which case the concept of "row" is not well-defined.)
The easiest way is to use Rejection Sampling. Normally this might be slow, and even though you don't mention it, I'd worry this code might occur in a performance-critical section of your program. Nevertheless, everything is okay, since the chance of a 14 3-sided coinflips in a row containing the substring 0-0-0 is fairly small. Even it's an issue, since the matrix is (supposedly) uniformly distributed, its elements must also be independently distributed, so you can sample each row separately, rejecting and recreating any row with 0-0-0 in a row or which has a sum <= 11.
As indicated by #ninjagecko, rejecting and recreating is the most obvious (if not the only) way to go here. In that light, I think this'll do nicely:
R = 14;
C = 12;
T = 10;
result = zeros(C,R*T);
for ii = 1:(R*T)
done = false;
while ~done
newRow = randi([0 2],C,1);
done = ...
(sum(newRow)<12) && ...
(~any(diff([0;find(newRow);C+1])>3));
end
result(:,ii) = newRow;
end
result = permute(reshape(result,C,R,T), [2 1 3]);
Note that <12 equals <=11 for integer math (and >3 equals >=4), but requires one less check and is thus faster. The variable newRow is created as a column vector, since such things are better organized in memory and can be checked faster. 3D arrays are generally slower to assign to than 2D matrices, therefore everything is kept 2D until after all operations. The most computationally intense check (~any(diff(find(newRow))>3)) is done last, so that short-circuiting (&&) may render its evaluation unnecessary. Both loops are small and fulfil all conditions to be compiled to machine code by Matlab's JIT.
So this is pretty close to optimized, and will be pretty fast. Unless someone comes up with an entirely different algorithm for this, I believe this is pretty close to the theoretical maximum speed in Matlab. If you need to go faster, you'll have to switch to C (which will allow optimization of the constraint checks).
For simply finding a series of numbers in an array, you can (ab)use strfind. When indexing into a multidimensional array, you can also combine subindices and linear indexing to the remaining dimensions (used in the assignment result(:,ii) = newRow):
result = NaN(12,14,10);
for ii = 1:14*10
while 1
newRow = randi([0 2],12,1);
if isempty(strfind(newRow',[0 0 0])) && sum(newRow)<=11
result(:,ii) = newRow;
break;
end
end
end
result = permute(result, [2 1 3]);