I have two arrays of angles: a=[140 360 170 3]; b= [12 270 0 21]; I need to find combination that will give me minimum when subtracting(/adding) elements of arrays. I'm doing this right now:
c = [ones(1,4)*a(1)-b; ones(1,4)*a(2)-b; ones(1,4)*a(3)-b; ones(1,4)*a(4)-b];
cc= abs(c);
[minNumk, minIndkl] = min(cc(:));
[rowk, colk] = ind2sub(size(cc), minIndkl);
cc(rowk,:)=[];
cc(:,colk)=[];
[min2k,minInd2k]=min(cc(:));
[row2k, col2k] = ind2sub(size(cc), minInd2k);
cc(row2k,:)=[];
cc(:,col2k)=[];
[min3k,minInd3k]=min(cc(:));
[row3k, col3k] = ind2sub(size(cc), minInd3k);
cc(row3k,:)=[];
cc(:,col3k)=[];
min4k= cc;
total=minNumk+min2k+min3k+min4k
Question. Is there any way of doing it in a more concise way? Also I am thinking do I need to use mod(,360) here?
EDITED: if the element was used(subtracted) then it cannot be used anymore. (Thus,the whole row and column is deleted.)
Thanks in advance!!!
Any advice will be very much appreciated!
Happy 10 000 000! :)
If I understand correctly, you want to try all permutations of one of the vectors and minimize (over all permutations) the sum (over vector entries) of absolute values of the differences between the vectors:
result_total = min(sum(abs(bsxfun(#minus, a, perms(b))), 2));
To get the invididual differences that minimize the sum of absolute differences:
d = bsxfun(#minus, a, perms(b));
[result_total, ind] = min(sum(abs(d), 2));
result_indiv = d(ind,:);
If you want to consider the differences to be in the range 0-360, use mod (and then you don't need abs):
d = mod(bsxfun(#minus, a, perms(b)),360);
[result_total, ind] = min(sum(d, 2));
result_indiv = d(ind,:);
Related
I would like to compute the product of the next n adjacent elements of a matrix. The number n of elements to be multiplied should be given in function's input.
For example for this input I should compute the product of every 3 consecutive elements, starting from the first.
[p, ind] = max_product([1 2 2 1 3 1],3);
This gives [1*2*2, 2*2*1, 2*1*3, 1*3*1] = [4,4,6,3].
Is there any practical way to do it? Now I do this using:
for ii = 1:(length(v)-2)
p = prod(v(ii:ii+n-1));
end
where v is the input vector and n is the number of elements to be multiplied.
in this example n=3 but can take any positive integer value.
Depending whether n is odd or even or length(v) is odd or even, I get sometimes right answers but sometimes an error.
For example for arguments:
v = [1.35912281237829 -0.958120385352704 -0.553335935098461 1.44601450110386 1.43760259196739 0.0266423803393867 0.417039432979809 1.14033971399183 -0.418125096873537 -1.99362640306847 -0.589833539347417 -0.218969651537063 1.49863539349242 0.338844452879616 1.34169199365703 0.181185490389383 0.102817336496793 0.104835620599133 -2.70026800170358 1.46129128974515 0.64413523430416 0.921962619821458 0.568712984110933]
n = 7
I get the error:
Index exceeds matrix dimensions.
Error in max_product (line 6)
p = prod(v(ii:ii+n-1));
Is there any correct general way to do it?
Based on the solution in Fast numpy rolling_product, I'd like to suggest a MATLAB version of it, which leverages the movsum function introduced in R2016a.
The mathematical reasoning is that a product of numbers is equal to the exponent of the sum of their logarithms:
A possible MATLAB implementation of the above may look like this:
function P = movprod(vec,window_sz)
P = exp(movsum(log(vec),[0 window_sz-1],'Endpoints','discard'));
if isreal(vec) % Ensures correct outputs when the input contains negative and/or
P = real(P); % complex entries.
end
end
Several notes:
I haven't benchmarked this solution, and do not know how it compares in terms of performance to the other suggestions.
It should work correctly with vectors containing zero and/or negative and/or complex elements.
It can be easily expanded to accept a dimension to operate along (for array inputs), and any other customization afforded by movsum.
The 1st input is assumed to be either a double or a complex double row vector.
Outputs may require rounding.
Update
Inspired by the nicely thought answer of Dev-iL comes this handy solution, which does not require Matlab R2016a or above:
out = real( exp(conv(log(a),ones(1,n),'valid')) )
The basic idea is to transform the multiplication to a sum and a moving average can be used, which in turn can be realised by convolution.
Old answers
This is one way using gallery to get a circulant matrix and indexing the relevant part of the resulting matrix before multiplying the elements:
a = [1 2 2 1 3 1]
n = 3
%// circulant matrix
tmp = gallery('circul', a(:))
%// product of relevant parts of matrix
out = prod(tmp(end-n+1:-1:1, end-n+1:end), 2)
out =
4
4
6
3
More memory efficient alternative in case there are no zeros in the input:
a = [10 9 8 7 6 5 4 3 2 1]
n = 2
%// cumulative product
x = [1 cumprod(a)]
%// shifted by n and divided by itself
y = circshift( x,[0 -n] )./x
%// remove last elements
out = y(1:end-n)
out =
90 72 56 42 30 20 12 6 2
Your approach is correct. You should just change the for loop to for ii = 1:(length(v)-n+1) and then it will work fine.
If you are not going to deal with large inputs, another approach is using gallery as explained in #thewaywewalk's answer.
I think the problem may be based on your indexing. The line that states for ii = 1:(length(v)-2) does not provide the correct range of ii.
Try this:
function out = max_product(in,size)
size = size-1; % this is because we add size to i later
out = zeros(length(in),1) % assuming that this is a column vector
for i = 1:length(in)-size
out(i) = prod(in(i:i+size));
end
Your code works when restated like so:
for ii = 1:(length(v)-(n-1))
p = prod(v(ii:ii+(n-1)));
end
That should take care of the indexing problem.
using bsxfun you create a matrix each row of it contains consecutive 3 elements then take prod of 2nd dimension of the matrix. I think this is most efficient way:
max_product = #(v, n) prod(v(bsxfun(#plus, (1 : n), (0 : numel(v)-n)')), 2);
p = max_product([1 2 2 1 3 1],3)
Update:
some other solutions updated, and some such as #Dev-iL 's answer outperform others, I can suggest fftconv that in Octave outperforms conv
If you can upgrade to R2017a, you can use the new movprod function to compute a windowed product.
Please let me try to explain by an example
numel_last_a = 1;
numel_last_b = 2
a = rand(2,20,numel_last_a);
b = rand(2,20,numel_last_b);
size(squeeze(sum(a,1)))
size(squeeze(sum(b,1)))
in this case, the output will be
ans = 1 20
ans = 20 2
This means I have to catch the special case where numel_last_x == 1 to apply a transpose operation for consistency with later steps. I'm guessing that there must be a more elegant solution. Can you guys help me out?
Edit: sorry, code was wrong!
The following observations are key here:
The inconsistency you mention is buried deep into the Matlab language: all arrays are considered to be at least 2D. For example, ndims(pi) gives 2.
Another rule in Matlab is that all arrays are assumed to have infinitely many trailing singleton dimensions. For example, size(pi,5) gives 1.
According to observation 1, squeeze won't remove singleton dimensions if doing so would give less than two dimensions. This is mentioned in the documentation:
B = squeeze(A) returns an array B with the same elements as A, but with all singleton dimensions removed. A singleton dimension is any dimension for which size(A,dim) = 1. Two-dimensional arrays are unaffected by squeeze; if A is a row or column vector or a scalar (1-by-1) value, then B = A.
If you want to get rid of the first singleton, you can exploit observation 2 and use reshape:
numel_last_a = 1;
numel_last_b = 2;
a = rand(2,20,numel_last_a);
b = rand(2,20,numel_last_b);
as = reshape(sum(a,1), size(a,2), size(a,3));
bs = reshape(sum(b,1), size(b,2), size(b,3));
size(as)
size(bs)
gives
ans =
20 1
ans =
20 2
You could use shiftdim instead of squeeze
numel_last_a = 1;
numel_last_b = 2;
a = rand(2,20,numel_last_a);
b = rand(2,20,numel_last_b);
size(shiftdim(sum(a,1)))
size(shiftdim(sum(b,1)))
ans =
20 1
ans =
20 2
I have a dataset (Data) which is a vector of, let's say, 1000 real numbers. I would like to extract at random from Data 100 times 10 contiguous numbers. I don't know how to use Datasample for that purpose.
Thanks in advance for you help.
You can just pick 100 random numbers between 1 and 991:
I = randi(991, 100, 1)
Then use them as the starting points to index 10 contiguous elements:
cell2mat(arrayfun(#(x)(Data(x:x+9)), I, 'uni', false))
Here you have a snipet, but instead of using Datasample, I used randi to generate random indexes.
n_times = 100;
l_data = length(Data);
index_random = randi(l_data-9,n_times,1); % '- 9' to not to surpass the vector limit when you read the 10 items
for ind1 = 1:n_times
random_number(ind1,:) = Data(index_random(ind1):index_random(ind1)+9)
end
This is similar to Dan's answer, but avoids using cells and arrayfun, so it may be faster.
Let Ns denote the number of contiguous numbers you want (10 in your example), and Nt the number of times (100 in your example). Then:
result = Data(bsxfun(#plus, randi(numel(Data)-Ns+1, Nt, 1), 0:Ns-1)); %// Nt x Ns
Here is another solution, close to #Luis, but with cumsum instead of bsxfun:
A = rand(1,1000); % The vector to sample
sz = size(A,2);
N = 100; % no. of samples
B = 10; % size of one sample
first = randi(sz-B+1,N,1); % the starting point for all blocks
rand_blocks = A(cumsum([first ones(N,B-1)],2)); % the result
This results in an N-by-B matrix (rand_blocks), each row of it is one sample. Of course, this could be one-lined, but it won't make it faster, and I want to keep it clear. For small N or B this method is slightly faster. If N or B becomes very large then the bsxfun method is slightly faster. This ranking is not affected by the size of A.
I've done quite a bit of searching and haven't been able to find a satisfactory answer so far, so I'm sorry if this question has already been raised.
I'm stuck on how to sum over the dimensions of an array. I have array A(w0,lambda,2048,2048), and I would like to be able to define a second array U(w0, 2048, 2048) which is composed of the sum of A over dimension lambda.
So far I have been defining both A and U as follows:
A = zeros(length(w0),length(lambda),2048,2048);
U = zeros(length(w0),2048,2048);
for ii = 1:length(w0) % Scan through spot sizes
for aa = 1:length(lambda) % Scan through wavelengths
A(ii,aa,:,:) = ASM(w0(ii),lambda(aa),z1,z2);
end
U(ii,:,:) = sum(A,2);
end
Where ASM is just a function. z1 and z2 are defined earlier, and not relevant here.
I have been trying to come up with other possible ways of finding U(w0,2048,2048) as the sum over the second dimension of A (lambda), but haven't been successful...
Thanks for any pointers, and sorry again if this has already been resolved!
James.
From the sounds of it, you just want:
U = squeeze(sum(A, 2));
squeeze() eliminates singleton dimensions.
Here are two alternative solutions:
U = reshape(sum(A, 2), [length(w0) 2048 2048]);
or:
U = sum(A, 2);
U = U(:, 1, :, :);
Try using 'sum' function with a dimension argument, and collapse result on the desired dimensions.
z = rand(2,3,2,2);
q = sum(z,2); %sum all 3 matrices of size 2x2x2 to get 2x1x2x2 result
zz = q(:,1,:,:); %zz is now 2x2x2, by collapsing the dimension 2.
I have a 1974x1 vector, Upper, and I am trying to break the information up into individual arrays of 36 items each. So, I used length to find that there are 1974 items and then divided by 36 and used the floor function. I cannot figure out how to do it all with n.
Here is my logic: I am defining n in an attempt to find the number of subsets that need to be defined. Then, I am trying to have subsetn become subset1, subset2,...,subset36. However, MATLAB only definies the matrix subsetn as a 1x36 matrix. However, this matrix contains what subset1 is supposed to contain(1...36). Do you guys have any advice for a newbie? What am I doing wrong?
binSize = 36;
nData = length(Upper);
nBins = floor(nData/36);
nDiscarded = nData - binSize*nBins;
n=1:binSize;
subsetn= [(n-1)*binSize+1:n*binSize];
You can create a 54x36 array where the nth column is your nth subset.
subsetArray=reshape(x(1:binSize*nBins),[],nBins);
You can access the nth subset as subsetArray(:,n)
Sorry in advance if I misunderstood what you want to do.
I think the following little trick might do what you want (it's hacky, but I'm no Matlab expert):
[a, b] = meshgrid(0:nBins-1, 0:binSize-1)
inds = a*binSize + b + 1
Now inds is a nBins*binSize matrix of indices. You can index Upper with it like
Upper(inds)
which should give you the subsets as the columns in the resulting matrix.
Edit: on seeing Yoda's answer, his is better ;)