quickselect code in matlab - matlab

Let us consider following pseudo code
QuickSelect(A, k)
let r be chosen uniformly at random in the range 1 to length(A)
let pivot = A[r]
let A1, A2 be new arrays
# split into a pile A1 of small elements and A2 of big elements
for i = 1 to n
if A[i] < pivot then
append A[i] to A1
else if A[i] > pivot then
append A[i] to A2
else
# do nothing
end for
if k <= length(A1):
# it's in the pile of small elements
return QuickSelect(A1, k)
else if k > length(A) - length(A2)
# it's in the pile of big elements
return QuickSelect(A2, k - (length(A) - length(A2))
else
# it's equal to the pivot
return pivot
I have wrote following code in matlab
function []=Quickselect(A,k);
% A-unsorted array
%k-we want to return kth largest element
% let r be chosen uniformly at random in the range 1 to length(A)
i=1; %initialize index
j=1; %initialize index
n=length(A);%length of array
r=floor(1+(n-1)*rand);%random index
pivot=A(r); % choose pivot as A(r);
A1=zeros(1,n); %create additional arrays
A2=zeros(1,n);%create additional arrays
for m=1:n
if A(m)<k
A1(i)=A(m);
i=i+1;
else if A(m)>k
A2(j)=A(m);
j=j+1;
end
end
end
if k <= numel(A1)
return Quickselect(A1,k);
else if k > (length(A) - length(A2))
return Quickselect(A2, k - (length(A) - length(A2)));
else
return pivot;
end
end
end
>> A=[2 1 4 3 6 5 7 9 8 10 11 13 21 12];
>> Quickselect(A,3)
Error: File: Quickselect.m Line: 23 Column: 10
Unexpected MATLAB expression.
I dont understand reason of error, can't i use recursive property in Matlab? thanks in advance

It is not a recursivity problem, it is just wrong Matlab syntax.
In Matlab, return does not force the return of a variable, but just makes the function stop and get out. If you want to return something your function should begin with:
function [outvar]=Quickselect(A,k);
And every time you have a
return pivot;
or
return Quickselect(A1,k);
You should modify it by
outvar=pivot; % or outvar=Quickselect(A1,k);
return;

Okay, here's what's wrong with your code. I've already commented about replacing pivot with k, but after looking at the code more closely I found a few more errors. #AnderBiguri already identified a couple of problems, and I'll mention those here as well for completeness.
function []=Quickselect(A,k);
As Ander mentioned, you need the return value. I'm going to directly use pivot. And function declarations don't need a ; after them. You're effectively creating a blank first line.
function pivot = Quickselect(A,k)
You had me confused for a second with the comments:
% A-unsorted array
%k-we want to return kth largest element
% let r be chosen uniformly at random in the range 1 to length(A)
The pseudocode you gave actually finds the kth smallest element. In the list [1, 2, 3, 4, 5], 1 would be the smallest element, 2 would be the second-smallest element, and 5 would be the fifth smallest-element.
i=1; %initialize index
j=1; %initialize index
n=length(A);%length of array
r=floor(1+(n-1)*rand);%random index
I don't know why you're not using randi here. Not actually wrong the way you're doing it, but it's much clearer, much more concise with randi.
r=randi(n); % random index
Note the % create additional arrays portion here:
pivot=A(r); % choose pivot as A(r);
A1=zeros(1,n); % create additional arrays
A2=zeros(1,n); % create additional arrays
These arrays are created with the same size as A and will always have the same size as A. This was addressed by Ander in his comment and will become very important later.
Now the for loop:
for m=1:n
if A(m)<k
Your pseudocode says:
if A[i] < pivot then
pivot is the value of the element chosen as the pivot and you're trying to split A into two lists: one with elements less than the pivot, and another with elements greater than the pivot. k is the index of an element whose value you're looking for. The pseudocode correctly uses pivot, so your code should be:
if A(m)<pivot
The same thing goes for A(m)>k. It should be A(m)>pivot. Also, in Matlab, the keyword is elseif, with no space. I'm deviating a bit from my method of commenting on individual lines as they occur, but I wanted to leave the next section uninterrupted to point out something else.
A1(i)=A(m);
i=i+1;
else if A(m)>k
A2(j)=A(m);
j=j+1;
end
end
end
See that first end? What does it end? It's better if you look at your whole code, but that last end ends the for loop. The second-to-last end ends the if-elsif. The first end is wrong and should be removed. Actually, and I'm very surprised Ander didn't mention this, but if you had properly indented your code you would never have created this bug. In fact, your entire code should be re-indented consistently for this reason as well as readability. The for loop section should look like this:
for m=1:n
if A(m)<pivot
A1(i)=A(m);
i=i+1;
elseif A(m)>pivot
A2(j)=A(m);
j=j+1;
end
end
Now it's quite obvious what blocks the ends go with and how many we need.
The next line in your code is:
if k <= numel(A1)
This is the bug I referred to above that's mentioned in Ander's comment. numel(A1) will always be equal to numel(A), because that's the way you created the array. Therefore, k will always be less than or equal to the size of the input array. (Well, it should be, but there's no explicit check for that in the pseudocode or your code, but oh well.)
So where do we get the number of elements actually added to A1? Well, we go back to that for loop where we were using i as the index of the next element to be added to A1 and j as the index of the next element to be added to A2. This means that the valid length of A1 is i-1 and the valid length of A2 is j-1. So your if statement should be:
if k <= (i-1)
Next we have:
return Quickselect(A1,k);
Ander Biguri also mentioned this one. Matlab does not return values this way; you simply set the value of the return variable that was defined in the function declaration. I used pivot, so we take the value returned from our recursive call to Quickselect and assign it to our copy of pivot. When the function ends, the value of this variable automatically becomes the return value.
Now for the parameters you're passing to the recursive call. I've already said that numel(A1) == numel(A). So if you recursively call Quickselect on A1, you're not actually reducing the size of the array. We only want to pass the valid portion of A1 and since we know that it has i-1 valid elements, we can just take the first i-1 elements, i.e. A1(1:i-1).
The correct line is:
pivot = Quickselect(A1(1:i-1),k);
Now we run into the length problem again, and you've switched from using numel to using length. Not actually wrong, as far as it goes, but bad style.
else if k > (length(A) - length(A2))
return Quickselect(A2, k - (length(A) - length(A2)));
This is always zero. The length of A minus the length of something that is the same length as A is zero. The valid length of A2 is j-1. Also, we already know the size of A, it's n from way up there at the beginning. The corrected lines:
elseif k > (n - (j-1))
pivot = Quickselect(A2(1:j-1), k - (n - (j-1)));
And so we come to the end of our code:
else
return pivot;
end
end
end
As mentioned, return doesn't set any return value, it just returns from the function. Since we're at the end of the function anyway, there's no need to return. Once again, proper indentation would show you that there is an extra end here as well. We need one for the if-elseif block, and one to end the function itself, but the third one is extraneous.
If you make these changes you will have a working quickselect function.

Related

How to calculate the sum of the output of the for loop for all iterations in Matlab?

I have two different row vectors P and L. The algorithm has different P and L for each iteration, my code is supposed to count how many corresponding ones both row vectors have at the same place for example if P and L are:
P=[1,2,2,1,1]
L=[2,2,1,1,1]
The answer should be 2. If I have another P and L with the answer 3 for example, I should be able to sum 2 and 3 to give me 5.
My code computes the correct answer for each iteration,but I don't know how to sum these answers to get the total number of corresponding ones the vectors P and L have in all the iterations.
Here is my code:
i = 1:numel(P);
j = 1:numel(L);
Valuecompared=(P(i) == 1) & (L(j)==1);
a=0;
for k = 1:numel(Valuecompared)
if Valuecompared(k) == 1
a=a+1;
end
end
I have tried sum(a), cumsum(a) inside and outside the loop with index k and without, but it just gives me the same answer "a" without adding the previous computed answers of "a". Could it be because it gets overwritten and doesn't get saved each time?
Also, I cannot seem to store the value of "a" while the algorithm is running I can display it, but if I have 100 iterations that will not be convenient, how can I compute the sum answer for all iterations and store the total sum in one variable like Atotal?
You have to store the a from each loop. Unfortunately you don't show the complete loop so I don't know over which variable you are iterating. But let's assume it is q. Then you have to define aoutside the loop, e.g. with
a = zeros(1,niter); % niter = number of times you run the loop
Then you would have to replace the last lines with with something like
if Valuecompared(k) == 1
a(q)=a(q)+1;
end
Once the loop is finished you can take the sum(a)
Alternatively you could move a=0;outside the loop, then you don't even have to take the sum anymore.
A bit offtopic, but you can replace all the code you have shown so far with
a = sum(P==1 & L==1)
Does exactly the same thing.

Matrix as input and output of Matlab function

For example I want to have a function which let me delete rows of my matrix where the highest value is 1. Thus I wrote:
% A is an input matrix
% pict2 suppose to be output cleared matrix
function pict2 = clear_(A)
B=A
n = size(A,1)
for i=1:n
if(max(B(i,:))==1)
B(i,:)=[]
end
end
But after I call:
pict2=clear_(pict) the Matlab resposes:
"warning: clear_: some elements in list of return values are undefined
warning: called from
clear_ at line 5 column 1 pict2 = "
I'm not sure which elements were left undefined?
The variable name of your output argument must match the variable that you want to be returned. So you'll want to change the first line to the following so that your modifications to B are saved and returned.
function B = clear_(A)
As far as your algorithm is concerned, it's not going to work because you are modifying B while trying to loop through it. Instead, you can replace your entire function with the following expression which computes the maximum value of each row, then determines if this value is equal to 1 and removes the rows where this is the case.
B(max(B, [], 2) == 1, :) == [];
I believe that, alternatively to the suggestions you already recieved, you might want to try the following. Using logicals is probably one of the best options for such a problem, since you don't need to use for-loops:
function out = clear_matr(A)
% ind is true for all the rows of A, where the highest value is not equal to 1
ind = ~(max(A, [], 2) == 1);
% filter A accordingly
out = A(ind, :);
end

find returns empty matrix

I have two vectors which are of length 200,000 approximately. They consist of dates in the datenum format.
%datenums
date_exp = datenum(data_exp(:,1:6));
date_sim = datenum(data_sim(:,1:6));
I want to find the dates in date_exp that exists in date_sim.
Then remove the values from date_exp
I have used the the ismember tool but ending up at i=38 find retunrs: Improper assignment with rectangular empty matrix.
Error in filter (line 18)
c(i)= find(ismember(date_sim(:),date_exp(i)),1);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
c = zeros(length(date_sim),1);
for i=1:length(date_sim)
c(i)= find(ismember(date_sim(:),date_exp(i)),1);
if isempty(c(i)) == 1
c(i) = 0;
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
I would be really helpful if anyone could help me out here.
The issue is because date_exp(38) must not be within date_sim. When there are no 1's in the input, find returns an empty array ([]).
Your code does not handle this as you would expect though because of this line.
c(i) = find(...)
In this case, if there are no matches (find() == []), then you are essentially calling
c(i) = [];
This deletes the ith element of c element
Therefore the following line is never true!
if isempty(c(i)) == 1
Instead, you should probably do something to handle the empty value.
index = find(ismember(date_sim(:), date_exp(i)), 1);
%// Only assign the index if it isn't an empty array
if ~isempty(index)
c(i) = index;
end
You don't have to worry about assigning zeros because your initial matrix is already full of zeros.
A Better Option
All of that aside, it would likely be a much better approach to not loop at all and to instead use the second output of ismember (on the arrays before you pass them to datenum) to give you the same result in a much more efficient way.
[~, c] = ismember(date_exp(:,1:6), date_sim(:,1:6), 'rows');

Function output contradicts function constraints

I wrote a function that takes as an imput a natural number 'n', and outputs a random walk, starting at 0, where each subsequent entry in the vector is obtained by adding or subtracting 1 to the previous entry. The random walk has to stop whenever it reaches n or -n.
The code is the following
function [N] = rand_walk2(n)
j=0; %J will be the outpur of the function
v=0; %Defines variable v.
i=2; %The following 'while' loop will start from the second entry of the vector.
while abs(j(i-1)) < n %This 'while' loop will continue running only if the previous entry is strictly between -n and n.
if rand >= 0.5 %This 'if' statement returns 1 or -1 with equal probability (it uses the uniform distribution with parameters [0,1]).
v = 1;
else v = -1;
end
j(i)=j(i-1)+v;
j = [j,j(i)];
i=i+1;
end
N = j;
end
The problem is that the output returns n or -n twice instead of only once, for example, if I evaluate rand_walk2(3) I get 0 -1 -2 -3 -3 instead of just o -1 -2 -3. Does anyone have any suggestions as to what I can change to fix this?
Line 10 pushes the current position into vector j
j(i)=j(i-1)+v;
and then line 11 concatenates that same value on to the end of the array
j = [j,j(i)];
Simply remove line 11.
As a usual side note, the symbols i and j are often used to produce imaginary numbers and often mistakenly as indexing variables. It is recommended by Mathworks:
Since i is a function, it can be overridden and used as a variable. However, it is best to avoid using i and j for variable names if you intend to use them in complex arithmetic.
In this case, it is not necessary, but I like to promote good programming practices when I can (also, I needed to force my brain to think through your code because of this practice).

indices must either be real positive integers or logicals. && Attempted to access f(0,0); index must be a positive integer or logical

I am new in matlab coming from c++ and trying to do 2d convolution. I have done dry run but nothing seems to go wrong but i dont know why this coming
My simple logic if value is lesser equal to zero then place zero. I am using this expression for solving that problem
i am facing 2 errors at
1)at f(q,p)=zeros(q,p);
2) at output_args(x,y)=output_args(x,y) + (W(s, t)* f(q,p));
function output_args = convo(img,k )
//skipped the portion of code---
z=s;i=t;
for x=1:row
for y=1:col
for s=s:a+2
% disp(s);
for t= t:b+2
q=z-s;
p=i-t;
if q<=0
if p <=0
f(q,p)=0; %// trying to place zero at index so help needed which is 1st error i have said
end
end
% disp(f(q,p));
output_args(x,y)=output_args(x,y) + (W(s, t)* f(q,p)); %//2nd error is comin (as i have told you above.
end
end
w=w+1;
end
z=z+1;
end
end
at console:(error occur when i apply
1) Subscript indices must either be real positive integers or logicals at f(q,p)=0
2) Attempted to access f(0,0); index must be a positive integer or logical. output_args(x,y)=output_args(x,y) + (W(s, t)* f(q,p));
so any idea?
if q<=0
if p <=0
f(q,p)=0; % p and q will always be invalid!
end
end
Well, that is begging for that error. In MATLAB, Indices must be greater than zero. While you can invert the if-condition to ensure that the index is positive, that will change the mending of your code.
if q > 0
if p > 0
f(q,p)=0; % p and q will always be valid.
end
end
If you need to index from -5 to 5 (for example) you could instead index from 1 to 11 instead. Just remember that you subtract 6 every time you display values. Alternatively, store the "real" indices in another vector.