I'm having trouble avoiding loops in Matlab. I'm told loops cause poor performances so I'm reworking a code that's already working with loops.
I have a vector big vector x containing values, and a smaller X, also containing value. For each value x, I have to know in which interval i it is. I define the ith interval as the values between X_i-1 and X_i. For now, I'm doing this:
len = length(x);
is = zeros(len, 1); % Interval for each x
for j=1:len
i=1; % Start interval
while(x(j)<X(i-1) || x(j)>X(i)) % Please consider accessing X(0) won't crash it's a simplification to make the code clearer for you.
i = i + 1;
end
is(j) = i;
end
What's the way to do it without those loops ?
EDIT: To help you understand the situation, here's a real example of what I'm trying to do here. With these inputs
X = [1 3 4 5]
x = [1 1.5 3.6 4.7 2.25]
I'd like is to be
% The 2 first and the 5th are in the first interval [1, 3]
% The 3rd is in [3, 4] and the 4th is in [4, 5]
is = [1 1 2 3 1]
Obvious homework, so I'll just point you to two functions that might help you:
If your list of intervals has a constant spacing, have a look at floor and figure out how you can compute the index directly.
If the intervals are irregularly spaced, have a lookt at histc, especially look at the form with 2 output arguments.
One more issue with your example code: try to understand what happens when x(j) is outside of any interval.
I am using masks, then I shift the second mask, and then I use find to return the index of ones:
ranges = [1,2,3,4]; %<br>
a = 1.5; %<br>
m1 = (a >= ranges); % will be [1, 0, 0, 0] <br>
m2 = (a <= ranges); % will be [0, 1, 1, 1] <br>
m2(1:end-1) = m2(2:end); % will be [1, 1, 1, 1], I am trying to shift this mask <br>
m2(end) = 0; % will be [1, 1, 1, 0], the mask shift is completed <br>
b = find( m1 & m2); % this will return 1 so your value is between 1 and 2 <br>
Related
I have a matrix as follows:
id value
=============
1 0.5
2 0.5
3 0.8
4 0.3
5 0.2
From this array, I wish to find all the possible combinations that have a sum less than or equal to 1. That is,
result
======
1 2
1 4 5
2 4 5
3 5
1 5
1 4
2 4
2 5
...
In order to get the above result, my idea has been to initially compute all the possibilities of finding sum of elements in the array, like so:
for ii = 1 : length(a) % compute number of possibilities
no_of_possibilities = no_of_possibilities + nchoosek(length(a),ii);
end
Once this is done, then loop through all possible combinations.
I would like to know if there's an easier way of doing this.
data = [0.5, 0.5, 0.8, 0.3, 0.2];
required = cell(1, length(data));
subsets = cell(1, length(data));
for k = 2:length(data)-1 % removes trivial cases (all numbers or one number at a time)
% generate all possible k-pairs (if k = 3, then all possible triplets
% will be generated)
combination = nchoosek(1:length(data), k);
% for every triplet generated, this function sums the corresponding
% values and then decides whether then sum is less than equal to 1 or
% not
findRequired = #(x) sum(data(1, combination(x, :))) <= 1;
% generate a logical vector for all possible combinations like [0 1 0]
% which denotes that the 2nd combination satisfies the condition while
% the others do not
required{k} = arrayfun(findRequired, 1:size(combination, 1));
% access the corresponding combinations from the entire set
subsets{k} = combination(required{k}, :);
end
This produces the following subsets:
1 2
1 4
1 5
2 4
2 5
3 5
4 5
1 4 5
2 4 5
It is not in easy way, however is a faster way, as I removed the combination which its subsets are not passed the condition.
bitNo = length(A); % number of bits
setNo = 2 ^ bitNo - 1; % number of sets
subsets = logical(dec2bin(0:setNo, bitNo) - '0'); % all subsets
subsets = subsets(2:end,:); % all subsets minus empty set!
subsetCounter = 1;
resultCounter = 1;
result = {};
while(1)
if( subsetCounter >= size(subsets,1))
break;
end
if(sum(A(subsets(subsetCounter,:).',2)) <= 1)
result{resultCounter} = A(subsets(subsetCounter,:).',1).';
resultCounter = resultCounter + 1;
subsetCounter = subsetCounter + 1;
else
% remove all bad cases related to the current subset
subsets = subsets(sum((subsets & subsets(subsetCounter,:)) - subsets(subsetCounter,:),2) ~= 0,:);
end
end
Generate the subsets using this method. After that, check the condition for each subset. If the subset does not pass the condition, all its supersets are removed from the subsets. To do this, using sum((subsets & subsets(i,:)) - subsets(i,:),2) ~= 0 which mean get some rows from subsets which has not the same elements of the not passed subset. By doing this, we able to not to consider some bad cases anymore. Although, theoretically, this code is Θ(2^n).
Here is potential solution, using inefficient steps, but borrowing efficient code from various SO answers. Credit goes to those original peeps.
data = [0.5, 0.5, 0.8, 0.3, 0.2];
First get all combinations of indices, not necessarily using all values.
combs = bsxfun(#minus, nchoosek(1:numel(data)+numel(data)-1,numel(data)), 0:numel(data)-1);
Then get rid of repeated indices in each combination, regardless of index order
[ii, ~, vv] = find(sort(combs,2));
uniq = accumarray(ii(:), vv(:), [], #(x){unique(x.')});
Next get unique combinations, regardless of index order... NOTE: You can do this step much more efficiently by restructuring the steps, but it'll do.
B = cellfun(#mat2str,uniq,'uniformoutput',false);
[~,ia] = unique(B);
uniq=uniq(ia);
Now sum all values in data based on cell array (uniq) of index combinations
idx = cumsum(cellfun('length', uniq));
x = diff(bsxfun(#ge, [0; idx(:)], 1:max(idx)));
x = sum(bsxfun(#times, x', 1:numel(uniq)), 2); %'// Produce subscripts
y = data([uniq{:}]); % // Obtain values
sums_data = accumarray(x, y);
And finally only keep the index combinations that sum to <= 1
allCombLessThanVal = uniq(sums_data<=1)
Here is what I want, a 3-D matrix:
K = 2:2.5:10;
den = zeros(1,4,4);
for i = 1:1:4
den(:,:,i) = [1, 5, K(i)-6, K(i)];
end
Or, a cell array is also acceptable:
K = 2:2.5:10;
for i = 1:1:4
den{i} = [1, 5, K(i)-6, K(i)];
end
But I want to know if there is a more efficient way of doing this using vectorized code like:
K = 2:2.5:10;
den = [1, 5, K-6, K];
I know the last code will not get what I wanted. But, like I can use:
v = [1 2 3];
v2 = v.^2;
instead of:
v = [1 2 3];
for i = 1:length(v)
v(i) = v(i)^2;
end
to get the matrix I want. Is there a similar way of doing this so that I can get the 3-D matrix or cell array I mentioned at the beginning more efficiently?
You need to "broadcast" the scalar values in columns so they are of the same length as your K vector. MATLAB does not do this broadcasting automatically, so you need to repeat the scalars and create vectors of the appropriate size. You can use repmat() for this.
K = 2:2.5:10;
%% // transpose K to a column vector:
K = transpose(K);
%% // helper function that calls repmat:
f = #(v) repmat(v, length(K), 1);
%% // your matrix:
den = [f(1) f(5) K-6 K];
This should be more optimized for speed but requires a bit more intermediary memory than the loop does.
Just use reshape with a 1*3 size:
den = reshape([ones(1,length(K));ones(1,length(K))*5; K-6; K],[1 4 length(K)]);
I think the used extra memory by reshape should be low and constant (dependent only on the length of the vector of new sizes).
You can use the classic line equation y=a*x+b, extended to the matrix form:
k = 2:2.5:10 ;
fa = [0 0 1 1].' ; %' // "a" coefficients
fb = [1 5 -6 0].' ; %' // "b" coefficients
d(1,:,:) = fa*k + fb*ones(1,4) ;
The above is better for clarity, but if you're not bothered you can also pack everything in one line:
d(1,:,:) = [0 0 1 1].' * (2:2.5:10) + [1 5 -6 0].' * ones(1,4) ;
If you need to re-use the principle for many different values of k, then you can use an anonymous function to help:
fden = #(k) [0 0 1 1].' * k + [1 5 -6 0].' * ones(1,4) ; %// define anonymous function
k = 2:2.5:10 ;
d(1,:,:) = fden(k) ; %// use it for any value of "k"
I want data to be processed as follows.
Eg.
say data x(i)=[1 2 3 5 2 1].
Comparisons should be elements INDEX [1 to 2, 1 to 3, 1 to 4, 1 to 5, 1 to 6, 2 to 3,2 to 4,2 to 5,2 to 6,3 to 4....]
following the above logic
hence elements values of distance = [1, 2 , 3 , 4 , 5 , 1 , 2, 3, 4, 1, 2, 3, 1, 2, 1].
hence elements values of difference = [1, 2 , 4 , 1 , 0 , 1 , 3, 0, 1, 2, 1, 2, 3, 4, 1].
I have written the following code for the same but i notice that the final matrix 'b' that i want is always changing size when it should be constant. I welcome any suggestions
clc;
close all;
clear all;
% read data set
I= imread('img2.bmp');
G=double(rgb2gray(I));
%choose 2 random numbers
n = 1;
s = [1 960];
k = round(rand(n,1)*range(s)+min(s));
for i = 1:length(k)
% choose a particular row from a matrix
row_no=k(i);
%G=R(row_no,:);
% compare every element with its neigbour to create distance and difference matrix
x1=row_no;
x2=row_no;
for y1 = 1:length(G)%size(G,2)
for y2 =1:length(G) %1:size(G,2)
distance_Mat(y1,y2) = round(sqrt((y2-y1)^2 + (x2-x1)^2));
difference_Mat(y1,y2) = 1*(G(x1,y1) - G(x2,y2))^2;
end
end
%% now remove repeating comparisons
b=horzcat(distance_Mat(:),(difference_Mat(:)));
[UniXY,Index]=unique(b,'rows');
DupIndex=setdiff(1:size(b,1),Index);
b(DupIndex,:)=[];
%calculate the cumulative sums and store it in different colums of data matrix
A1 = cumsum(b);
data(:,1)=A1;
end
If you have the stats toolbox then
distance_Mat = squareform(pdist(x'));
only does each comparison once and then mirrors the data. You can get just the lower half by
tril(distance_Mat,-1);
If you don't have the toolbox then try this:
I = tril(ones(numel(x)),-1);
[r,c] = find(I);
distance_Mat = zeros(numel(x));
distance_Mat(logical(I)) = round(sqrt((x(r)-x(c)).^2)
I would like to write a matlab function to find an equation of a linear classifier for 2 separable sets of points using one single-layer perceptron. I have got 2 files:
script file - run.m:
x_1 = [3, 3, 2, 4, 5];
y_1 = [3, 4, 5, 2, 2];
x_2 = [6, 7, 5, 9, 8];
y_2 = [3, 3, 4, 2, 5];
target_array = [0 0 0 0 0 1 1 1 1 1];
[ func ] = classify_perceptron([x_1 x_2; y_1 y_2], target_array);
x = -2:10;
y = arrayfun(func, x);
plot(x_1, y_1, 'o', x_2, y_2, 'X', x, y);
axis([-2, 10, -2, 10]);
classify_perceptron.m
function [ func ] = classify_perceptron( points, target )
% points - matrix of x,y coordinates
% target - array of expected results
% func - function handler which appropriately classifies a point
% given by x, y arguments supplied to this function
target_arr = target;
weights = rand(1, 2);
translation = rand();
for i=1:size(points, 2)
flag = true;
while flag
result = weights * points(:, i) + translation;
y = result > 0;
e = target_arr(1, i) - y;
if e ~= 0
weights = weights + (e * points(:, i))';
translation = translation + e;
else
flag = false;
end
end
end
func = #(x)(-(translation + (weights(1, 1) * x)) / weights(1, 2));
return
end
The problem is that I don't know where I am making the mistake that leads to incorrect result. It looks like the slope of the line is right, however translation should be a bit bigger. I would be really thankful for pointing me in the right direction. The result I get is presented in the picture below:
Ok, so I have made a significant progress. In case someone runs into the same problem I present to you the solution. The problem has been solved by adding a variable learning_rate = 0.1 and packing the loop iterating over points into another loop iterating as many times as specified in the variable epochs (e.g. 300) .
I want to vectorize the following MATLAB code. I think it must be simple but I'm finding it confusing nevertheless.
r = some constant less than m or n
[m,n] = size(C);
S = zeros(m-r,n-r);
for i=1:m-r+1
for j=1:n-r+1
S(i,j) = sum(diag(C(i:i+r-1,j:j+r-1)));
end
end
The code calculates a table of scores, S, for a dynamic programming algorithm, from another score table, C.
The diagonal summing is to generate scores for individual pieces of the data used to generate C, for all possible pieces (of size r).
Thanks in advance for any answers! Sorry if this one should be obvious...
Note
The built-in conv2 turned out to be faster than convnfft, because my eye(r) is quite small ( 5 <= r <= 20 ). convnfft.m states that r should be > 20 for any benefit to manifest.
If I understand correctly, you're trying to calculate the diagonal sum of every subarray of C, where you have removed the last row and column of C (if you should not remove the row/col, you need to loop to m-r+1, and you need to pass the entire array C to the function in my solution below).
You can do this operation via a convolution, like so:
S = conv2(C(1:end-1,1:end-1),eye(r),'valid');
If C and r are large, you may want to have a look at CONVNFFT from the Matlab File Exchange to speed up calculations.
Based on the idea of JS, and as Jonas pointed out in the comments, this can be done in two lines using IM2COL with some array manipulation:
B = im2col(C, [r r], 'sliding');
S = reshape( sum(B(1:r+1:end,:)), size(C)-r+1 );
Basically B contains the elements of all sliding blocks of size r-by-r over the matrix C. Then we take the elements on the diagonal of each of these blocks B(1:r+1:end,:), compute their sum, and reshape the result to the expected size.
Comparing this to the convolution-based solution by Jonas, this does not perform any matrix multiplication, only indexing...
I would think you might need to rearrange C into a 3D matrix before summing it along one of the dimensions. I'll post with an answer shortly.
EDIT
I didn't manage to find a way to vectorise it cleanly, but I did find the function accumarray, which might be of some help. I'll look at it in more detail when I am home.
EDIT#2
Found a simpler solution by using linear indexing, but this could be memory-intensive.
At C(1,1), the indexes we want to sum are 1+[0, m+1, 2*m+2, 3*m+3, 4*m+4, ... ], or (0:r-1)+(0:m:(r-1)*m)
sum_ind = (0:r-1)+(0:m:(r-1)*m);
create S_offset, an (m-r) by (n-r) by r matrix, such that S_offset(:,:,1) = 0, S_offset(:,:,2) = m+1, S_offset(:,:,3) = 2*m+2, and so on.
S_offset = permute(repmat( sum_ind, [m-r, 1, n-r] ), [1, 3, 2]);
create S_base, a matrix of base array addresses from which the offset will be calculated.
S_base = reshape(1:m*n,[m n]);
S_base = repmat(S_base(1:m-r,1:n-r), [1, 1, r]);
Finally, use S_base+S_offset to address the values of C.
S = sum(C(S_base+S_offset), 3);
You can, of course, use bsxfun and other methods to make it more efficient; here I chose to lay it out for clarity. I have yet to benchmark this to see how it compares with the double-loop method though; I need to head home for dinner first!
Is this what you're looking for? This function adds the diagonals and puts them into a vector similar to how the function 'sum' adds up all of the columns in a matrix and puts them into a vector.
function [diagSum] = diagSumCalc(squareMatrix, LLUR0_ULLR1)
%
% Input: squareMatrix: A square matrix.
% LLUR0_ULLR1: LowerLeft to UpperRight addition = 0
% UpperLeft to LowerRight addition = 1
%
% Output: diagSum: A vector of the sum of the diagnols of the matrix.
%
% Example:
%
% >> squareMatrix = [1 2 3;
% 4 5 6;
% 7 8 9];
%
% >> diagSum = diagSumCalc(squareMatrix, 0);
%
% diagSum =
%
% 1 6 15 14 9
%
% >> diagSum = diagSumCalc(squareMatrix, 1);
%
% diagSum =
%
% 7 12 15 8 3
%
% Written by M. Phillips
% Oct. 16th, 2013
% MIT Open Source Copywrite
% Contact mphillips#hmc.edu fmi.
%
if (nargin < 2)
disp('Error on input. Needs two inputs.');
return;
end
if (LLUR0_ULLR1 ~= 0 && LLUR0_ULLR1~= 1)
disp('Error on input. Only accepts 0 or 1 as input for second condition.');
return;
end
[M, N] = size(squareMatrix);
if (M ~= N)
disp('Error on input. Only accepts a square matrix as input.');
return;
end
diagSum = zeros(1, M+N-1);
if LLUR0_ULLR1 == 1
squareMatrix = rot90(squareMatrix, -1);
end
for i = 1:length(diagSum)
if i <= M
countUp = 1;
countDown = i;
while countDown ~= 0
diagSum(i) = squareMatrix(countUp, countDown) + diagSum(i);
countUp = countUp+1;
countDown = countDown-1;
end
end
if i > M
countUp = i-M+1;
countDown = M;
while countUp ~= M+1
diagSum(i) = squareMatrix(countUp, countDown) + diagSum(i);
countUp = countUp+1;
countDown = countDown-1;
end
end
end
Cheers