Assume that we have three vectors say a, b and c including increasing real-valued numbers as follows:
a=[3 4 19 22];
b=[1 10 15];
c=[3 5 11];
What is the most efficient way (without using loops) to find increasing sequence of numbers in such a problem in MATLAB?
For the above example the output should be like this:
[3 10 11]
[4 10 11]
Which both have their first element from a, their second one from b and their third one from c, so they have three increasing elements as it should be.
Note: The first number have to come from a, the second from b and the third from c.
Using loops is not a good choice for this problem because the vectors may have more length and the number of vectors would be increased in general cases, so the run time would take so long.
Any help would be gratefully appreciated…
Thanks in advance
There are several ways of approaching this problem, and one should always consider the time vs. space payoffs.
Approach 1 (Requires a lot of space (n^3 where n is the length of the arrays) but has no direct loops!):
[A,B,C] = meshgrid(a,b,c);
idxs = find((A<B)&(B<C));
[ib,ia,ic] = ind2sub(size(A),idxs);
answer = [a(1,ia);b(1,ib);c(1,ic)];
Approach 2 (Requires less space (n^2 where n is the length of the arrays), but does contain a loop, but it seems to be faster for big values of n. This approach is much faster if you only need to find how many solutions there are.):
ab = double(bsxfun(#lt,a',b));
bc = double(bsxfun(#lt,b',c));
abc = ab*bc;
numberOfAnswers = sum(abc(:));
[idx] = find(abc);
cumnum = [0;cumsum(abc(idx))];
[ia,ic] = ind2sub(size(abc),idx);
answer = zeros(3,numberOfAnswers);
for i = 1:numel(idx)
answer(1,(cumnum(i)+1):cumnum(i+1)) = a(ia(i));
answer(2,(cumnum(i)+1):cumnum(i+1)) = b((b>a(ia(i)))&(b<c(ic(i))));
answer(3,(cumnum(i)+1):cumnum(i+1)) = c(ic(i));
end
EDIT: Both methods will give a 3-by-m-matrix where m is the number of solutions, and the solutions will be the columns of the matrix.
Related
I need to find all possible combinations of numbers 1:8 such that sum of all elements is equal to 8
The combinations need to be arranged in an ascending order.
Eg
1 7
2 2 4
1 3 5
1 2 2 3
1 1 1 1 1 1 1 1
A number can repeat itself. But a combination must not..
i.e 1 2 2 3 and 2 1 2 3
I need the the solution in ascending order So there will be only one possibility of every combination
I tried a few codes online suggested on Find vector elements that sum up to specific number in MATLAB
VEC = [1:8];
NUM = 8;
n = length(VEC);
finans = zeros(2^n-1,NUM);
for i = 1:(2^n - 1)
ndx = dec2bin(i,n) == '1';
if sum(VEC(ndx)) == NUM
l = length(VEC(ndx));
VEC(ndx)
end
end
but they dont include the possibilities where the numbers repeat.
I found a better approach through recursion and it's more elegant (I like elegant) and faster than my previous attempt (0.00399705213 seconds on my computer).
EDIT: You will need my custom function stretchmat.m that stretches a vector to fit the size of another matrix. Kinda like repmat but stretching the first parameter (see help for details). Very useful!
script.m
% Define funciton to prepend a cell x with a variable i
cellprepend = #(x,i) {[i x]};
% Execute and time function
tic;
a = allcomb(cellprepend,1,8); % Solution in a
toc;
allcomb.m
function a = allcomb( cellprepend, m, n )
% Add entire block as a combination
a{1} = n;
% Exit recursion if block size 1
if n == 1
return;
end
% Recurse cutting blocks at different segments
for i = m:n/2
b = allcomb(cellprepend,i,n-i);
a = [a cellfun( cellprepend, b, num2cell( stretchmat( i, b ) ) )];
end
end
So the idea is simple, for solutions that add to 8 is exhaustive. If you look for only valid answers, you can do a depth first search by breaking up the problem into 2 blocks. This can be written recursively as I did above and is kinda similar to Merge Sort. The allcomb call takes the block size (n) and finds all the ways of breaking it up into smaller pieces.
We want non-zero pieces so we loop it from 1:n-1. It then prepends the first block to all the combinations of the second block. By only doing all comb on one of the blocks, we can ensure that all solutions are unique.
As for the sorting, I'm not quite sure what you mean by ascending. From what I see, you appear to be sorting from the last number in ascending order. Can you confirm? Any sort can be appended to the end of script.m.
EDIT 2/3 Notes
For the permutatively unique case, the code can be found here
Thanks to #Simon for helping me QA the code multiple times
EDIT: Look at my second more efficient answer!
The Naive approach! Where the cartprod.m function can be found here.
% Create all permutations
p(1:8) = {0:8};
M = fliplr( cartprod( p{:} ) );
% Check sums
r = sum( M, 2 ) == 8;
M = M(sum( M, 2 ) == 8,:); % Solution here
There are definitely more efficient solutions than this but if you just need a quick and dirty solution for small permutations, this will work. Please note that this made Matlab take 3.5 GB of RAM to temporarily store the permutations.
First save all combinations with repetitions in a cell array. In order to do that, just use nmultichoosek.
v = 1 : 8;
combs = cell(length(v),0);
for i = v
combs{i} = nmultichoosek(v,i);
end
In this way, each element of combs contains a matrix where each row is a combination. For instance, the i-th row of combs{4} is a combination of four numbers.
Now you need to check the sum. In order to do that to all the combinations, use cellfun
sums = cellfun(#(x)sum(x,2),combs,'UniformOutput',false);
sums contains the vectors with the sum of all combinations. For
instance, sums{4} has the sum of the number in combination combs{4}.
The next step is check for the fixed sum.
fixed_sum = 10;
indices = cellfun(#(x)x==fixed_sum,sums,'UniformOutput',false);
indices contains arrays of logical values, telling if the combination satisfies the fixed sum. For instance, indices{4}(1) tells you if the first combination with 4 numbers sums to fixed_sum.
Finally, retrieve all valid combinations in a new cell array, sorting them at the same time.
valid_combs = cell(length(v),0);
for i = v
idx = indices{i};
c = combs{i};
valid_combs{i} = sortrows(c(idx,:));
end
valid_combs is a cell similar to combs, but with only combinations that sum up to your desired value, and sorted by the number of numbers used: valid_combs{1} has all valid combinations with 1 number, valid_combs{2} with 2 numbers, and so on. Also, thanks to sortrows, combinations with the same amount of numbers are also sorted. For instance, if fixed_sum = 10 then valid_combs{8} is
1 1 1 1 1 1 1 3
1 1 1 1 1 1 2 2
This code is quite efficient, on my very old laptop I am able to run it in 0.016947 seconds.
How can I construct a scrambled matrix with 128 rows and 32 columns in vb.net or Matlab?
Entries of the matrix are numbers between 1 and 32 with the condition that each row mustn't contain duplicate elements and rows mustn't be duplicates.
This is similar to #thewaywewalk's answer, but makes sure that the matrix has no repeated rows by testing if it does and in that case generating a new matrix:
done = 0;
while ~done
[~, matrix] = sort(rand(128,32),2);
%// generate each row as a random permutation, independently of other rows.
%// This line was inspired by randperm code
done = size(unique(matrix,'rows'),1) == 128;
%// in the event that there are repeated rows: generate matrix again
end
If my computations are correct, the probability that the matrix has repteated rows (and thus has to be generated again) is less than
>> 128*127/factorial(32)
ans =
6.1779e-032
Hey, it's more likely that a cosmic ray will spoil a given run of the program! So I guess you can safely remove the while loop :-)
With randperm you can generate one row:
row = randperm(32)
if this vector wouldn't be that long you could just use perms to find all permutations:
B = perms(randperm(32))
but it's memory-wise too much! ( 32! = 2.6313e+35 rows )
so you can use a little loop:
N = 200;
A = zeros(N,32);
for ii = 1:N
A(ii,:) = randperm(32);
end
B = unique(A, 'rows');
B = B(1:128,:);
For my tests it was sufficient to use N = 128 directly and skip the last two lines, because with 2.6313e+35 possibly permutations the probability that you get a correct matrix with the first try is very high. But to be sure that there are no row-duplicates choose a higher number and select the first 128 rows finally. In case the input vector is relatively short and the number of desired rows close to the total number of possible permutations use the proposed perms(randperm( n )).
small example for intergers from 1 to 4 and a selection of 10 out of 24 possible permutations:
N = 20;
A = zeros(N,4);
for ii = 1:N
A(ii,:) = randperm(4);
end
B = unique(A, 'rows');
B = B(1:10,:);
returns:
B =
1 2 3 4
1 2 4 3
1 3 4 2
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
some additional remarks for the choice of N:
I made some test runs, where I used the loop above to find all permutations like perms does. For vector lengths of n=4 to n=7 and in each case N = factorial(n): 60-80% of the rows are unique.
So for small n I would recommend to choose N as follows to be absolutely on the safe side:
N = min( [Q factorial(n)] )*2;
where Q is the number of permutations you want. For bigger n you either run out of memory while searching for all permutations, or the desired subset is so small compared to the number of all possible permutations that repetition is very unlikely! (Cosmic Ray theory linked by Luis Mendo)
Your requirements are very loose and allow many different possibilities. The most efficient solution I can think off that meets these requirements is as follows:
p = perms(1:6);
[p(1:128,:) repmat(7:32,128,1)]
I am reading 5 columns from a .txt file to 5 vectors.
Sometimes some vectors are one element larger than others, so I need to check if they are all of equal length, and if not, I have to find which ones are the largest and delete their last element. I think I should be able to do this without loops. I was originally thinking of using find in combination with isequal but isequal only returns a logical, and does not provide any information on which vectors are the largest.
[Seconds,Sensor1VStatic,Sensor2VPulsed,TemperatureC,RelativeHumidity] = importfile(path);
Then depending on what vectors are longer by one element, I will do, for example
Seconds(end) = [];
Sensor1VStatic(end) = [];
If Seconds and Sensor1VStatic are longer than the other vectors by one element
Assume your vectors are in a cell array A:
A = {[1 2 3], [1 2 3 4], [1 2 3 4 5]};
You can get the size of each vector with
sz = cellfun(#(x)size(x,2), A);
Which will return (for the above example)
sz = [ 3 4 5]
Now you can find the shortest vector:
minLength = min(sz);
And finally, make all vectors this length:
B = cell2mat(cellfun(#(x)x(1:minLength), A, 'uniformoutput', false))';
There may be more elegant ways (and note that cellfun really is doing "implicit looping")
Applying this to your (now expanded) example, you could probably assign the output of importfile directly to the cell array - or you can do it as a separate line:
A = {Seconds,Sensor1VStatic,Sensor2VPulsed,TemperatureC,RelativeHumidity};
But it all becomes a lot of work. Instead you could do:
minLength = min(size(Seconds,1), size(Sensor1VStatic,1), size(Sensor2VPulsed,1), ...
Seconds = Seconds(1:minLength);
...
There is scope for some cleverness but it won't make things more readable, and it won't save time in the long run...
I have two vectors of values and I want to compare them statistically. For simplicity assume A = [2 3 5 10 15] and B = [2.5 3.1 4.8 10 18]. I want to compute the standard deviation, the root mean square error (RMSE), the mean, and present conveniently, maybe as histogram. Can you please help me how to do it so that I understand? I know question is probably simple, but I am new into this. Many thanks!
edited:
This is how I wanted to implement RMSE.
dt = 1;
for k=1:numel(A)
err(k)=sqrt(sum(A(1,1:k)-B(1,1:k))^2/k);
t(k) = dt*k;
end
However it gives me bigger values than I expect, since e.g. 3 and 3.1 differ only in 0.1.
This is how I calculate error between reference value of each cycle with corresponding estimated in that cycle.
Can you tell me, am I doing right, or what's wrong?
abs_err = A-B;
The way you are looping through the vectors is not element by element but rather by increasing the vector length, that is, you are comparing the following at each iteration:
A(1,1:k) B(1,1:k)
-------- --------
k=1 [2] [2.5]
=2 [2 3] [2.5 3.1]
=3 [2 3 5] [2.5 3.1 4.8]
....
At no point do you compare only 2 and 2.1!
Assuming A and B are vectors of identical length (and both are either column or row vectors), then you want functions std(A-B), mean(A-B), and if you look in matlab exchange, you will find a user-contributed rmse(A-B) but you can also compute the RMSE as sqrt(mean((A-B).^2)). As for displaying a histogram, try hist(A-B).
In your case:
dt = 1;
for k=1:numel(A)
stdab(k) = std(A(1,1:k)-B(1,1:k));
meanab(k) = mean(A(1,1:k)-B(1,1:k));
err(k)=sqrt(mean((A(1,1:k)-B(1,1:k)).^2));
t(k) = dt*k;
end
You can also include hist(A(1,1:k)-B(1,1:k)) in the loop if you want to compute histograms for every vector pair difference A(1,1:k)-B(1,1:k).
I'm desperately trying to avoid a for loop in Matlab, but I cannot figure out how to do it. Here's the situation:
I have two m x n matrices A and B and two vectors v and w of length d. I want to outer multiply A and v so that I get an m x n x d matrix where the (i,j,k) entry is A_(i,j) * v_k, and similarly for B and w.
Afterward, I want to add the resulting m x n x d matrices, and then take the mean along the last dimension to get back an m x n matrix.
I'm pretty sure I could handle the latter part, but the first part has me completely stuck. I tried using bsxfun to no avail. Anyone know an efficient way to do this? Thanks very much!
EDIT: This revision comes after the three great answers below. gnovice has the best answer to the question I asked without a doubt. However,the question that I meant to ask involves squaring each entry before taking the mean. I forgot to mention this part originally. Given this annoyance, both of the other answers work well, but the clever trick of doing algebra before coding doesn't help this time. Thanks for the help, everyone!
EDIT:
Even though the problem in the question has been updated, an algebraic approach can still be used to simplify matters. You still don't have to bother with 3-D matrices. Your result is just going to be this:
output = mean(v.^2).*A.^2 + 2.*mean(v.*w).*A.*B + mean(w.^2).*B.^2;
If your matrices and vectors are large, this solution will give you much better performance due to the reduced amount of memory required as compared to solutions using BSXFUN or REPMAT.
Explanation:
Assuming M is the m-by-n-by-d matrix that you get as a result before taking the mean along the third dimension, this is what a span along the third dimension will contain:
M(i,j,:) = A(i,j).*v + B(i,j).*w;
In other words, the vector v scaled by A(i,j) plus the vector w scaled by B(i,j). And this is what you get when you apply an element-wise squaring:
M(i,j,:).^2 = (A(i,j).*v + B(i,j).*w).^2;
= (A(i,j).*v).^2 + ...
2.*A(i,j).*B(i,j).*v.*w + ...
(B(i,j).*w).^2;
Now, when you take the mean across the third dimension, the result for each element output(i,j) will be the following:
output(i,j) = mean(M(i,j,:).^2);
= mean((A(i,j).*v).^2 + ...
2.*A(i,j).*B(i,j).*v.*w + ...
(B(i,j).*w).^2);
= sum((A(i,j).*v).^2 + ...
2.*A(i,j).*B(i,j).*v.*w + ...
(B(i,j).*w).^2)/d;
= sum((A(i,j).*v).^2)/d + ...
sum(2.*A(i,j).*B(i,j).*v.*w)/d + ...
sum((B(i,j).*w).^2)/d;
= A(i,j).^2.*mean(v.^2) + ...
2.*A(i,j).*B(i,j).*mean(v.*w) + ...
B(i,j).^2.*mean(w.^2);
Try reshaping the vectors v and w to be 1 x 1 x d:
mean (bsxfun(#times, A, reshape(v, 1, 1, [])) ...
+ bsxfun(#times, B, reshape(w, 1, 1, [])), 3)
Here I am using [] in the argument to reshape to tell it to fill that dimension in based on the product of all the other dimensions and the total number of elements in the vector.
Use repmat to tile the matrix in the third dimension.
A =
1 2 3
4 5 6
>> repmat(A, [1 1 10])
ans(:,:,1) =
1 2 3
4 5 6
ans(:,:,2) =
1 2 3
4 5 6
etc.
You still don't have to resort to any explicit loops or indirect looping using bsxfun et al. for your updated requirements. You can achieve what you want by a simple vectorized solution as follows
output = reshape(mean((v(:)*A(:)'+w(:)*B(:)').^2),size(A));
Since OP only says that v and w are vectors of length d, the above solution should work for both row and column vectors. If they are known to be column vectors, v(:) can be replaced by v and likewise for w.
You can check if this matches Lambdageek's answer (modified to square the terms) as follows
outputLG = mean ((bsxfun(#times, A, reshape(v, 1, 1, [])) ...
+ bsxfun(#times, B, reshape(w, 1, 1, []))).^2, 3);
isequal(output,outputLG)
ans =
1