Binary search infinite recursion - matlab

I have written simple code in Matlab for binary Search. It's working properly if the searched item is in the array but goes into an infinite recursion loop if not.
I'm not sure where the problem lies.
function [] = BinarySearch(A,beg,last,item)
mid=floor((last+beg)/2);
if (beg)<=last
if item==A(mid)
fprintf('Item found at position %d \n',mid);
else
if(item<A(mid))
BinarySearch(A,beg,mid,item)
else
BinarySearch(A,mid,last,item)
end
end
else
fprintf('Item not found\n');
end

Imagine the really simple case that you only have 2 items in your list
A = [1 3]
and you call your BinarySearch on an item which would lie in the middle of the list. Look at the comments below, which follow how your function behaves...
BinarySearch(A, 1, 2, 2)
% mid = 1
% item ~= A(1): go to else
% item > A(1): go to else
% BinarySearch(A, 1, 2, 2)
% ... rinse and repeat
If your item was too small
BinarySearch(A, 1, 2, 0)
% mid = 1
% item ~= A(1): go to else
% item < A(1)
% BinarySearch(A, 1, 1, 0)
% mid = 1
% beg <= last (this is still true)
% item ~= A(1): go to else
% item < A(1)
% BinarySearch(A, 1, 1, 0)
% ... rinse and repeat
Similarly for an item which is larger than any in the list,
BinarySearch(A, 1, 2, 5)
% leads to BinarySearch(A, 1, 2, 5)
% ... repeat!!
You keep re-checking the same region, because your left (beg) and right (last) indices are allowed to both stay the same.
Let's re-implement the function, returning an actual value instead of just printing out the position to the console too. The comments directly relate to the numbered steps in the Wikipedia article for binary search, which looks similar in structure to what you've attempted:
function idx = BinarySearch(A, L, R, item)
%% BINARYSEARCH search for an item in the array A. Assumes that A is sorted ascending
% 1. Should be initially called using idx = BinarySearch(A, 1, n, item)
% where n is the number of elements in A, numel(A)
% 2. if L > R, the search terminates as unsuccessful
if L > R
idx = 0;
else
% 3. set m (position of middle element) to the floor of (L+R)/2
m = floor((L+R)/2);
% 4. if Am < item, set L to m+1 and go to 2.
if A(m) < item
L = m + 1; % YOU MISSED THIS STEP, CAUSING OVERLAPPING SEARCH REGIONS
idx = BinarySearch(A, L, R, item);
% 5. if Am > item, set R to m-1 and go to 2.
elseif A(m) > item
R = m - 1; % THE OTHER HALF OF THE STEP YOU MISSED
idx = BinarySearch(A, L, R, item);
% 6. Now Am = item, search is done, return index
else
idx = m;
end
end
end
Tests with A as before:
BinarySearch(A, 1, 2, 2); % returns 0: not found
BinarySearch(A, 1, 2, 0); % returns 0: not found
BinarySearch(A, 1, 2, 5); % returns 0: not found
BinarySearch(A, 1, 2, 3); % returns 2: 3 is at index 2
BinarySearch(A, 1, 2, 1); % returns 1: 1 is at index 1
Note that it may not be most efficient to implement this recursively. I'll leave it as an exercise, but this could be easily implemented using a while loop instead. The same logical structure would be used.

Try to set breakpoints to examine values where you think a problem might be.
You can use the while loop and the break command to avoid going into infinite loop.
For example try something like this:
function [index] = binarySearch(A, n, num)
left = 1;
right = n;
flag = 0;
while left <= right
mid = ceil((left + right) / 2);
if A(mid) == num
index = mid;
flag = 1;
break;
else if A(mid) > num
right = mid - 1;
else
left = mid + 1;
end
end
end
if flag == 0;
index = -1;
end
end

Related

creating a loop to convert elements of a matrix in matlab

Question: Create a copy of check called checkflip. Then, create a loop that converts each element of checkflip to 1 if its value is 0 and to 0 if its value is 1. The code within your loop should operate on only one element of checkflip at a time
In previous questions, I established check in the code below:
x=[0 1];
y=x([2 1]);
check=repmat([repmat(y,1,4);repmat(x,1,4)], 4, 1);
I've tried the code below to solve my question but it doesn't seem to do anything and i am not sure why
checkflip=check;
x=checkflip;
for i=1:8
if x(i)==0
x(i)=1;
elseif x(i)==1
x(i)=0;
end
end
Taking the complement using ~ is an option to flip each value in the matrix using a set of for-loops. A set of two for-loops can be used to traverse through each index of the array 1 by 1. The outer for-loop controls the scanning through the rows and the inner for-loop controls scanning through the columns. Alternatively, method 3 shows how to use a single loop and evaluate the corresponding row (i) and column (j) accordingly. It's important to be careful with consecutive if statement because sometimes within the first if statement's body checkflip can be flipped causing the second if statement to trigger accidentally. Sometimes it can be good practice not to involve the same variables in consecutive if statements and their bodies.
Edit: Alternatively you can use one for-loop that runs from 1 to the number of array elements in this case 64 since MATLAB index arrays by traversing the rows from left to right.
Looping Methodology:
Below shows the looping method for a 5 by 5 array/matrix but this can easily be extended to arrays/matrices of any size.
Method 1: Using Complement
x=[0 1];
y=x([2 1]);
check = repmat([repmat(y,1,4);repmat(x,1,4)], 4, 1);
checkflip = check;
for i = 1: 8
for j = 1: 8
checkflip(i,j) = ~checkflip(i,j);
end
end
or
x=[0 1];
y=x([2 1]);
check = repmat([repmat(y,1,4);repmat(x,1,4)], 4, 1);
checkflip = check;
for i = 1: 64
checkflip(i) = ~checkflip(i);
end
Method 2: Using If Statements
x=[0 1];
y=x([2 1]);
check = repmat([repmat(y,1,4);repmat(x,1,4)], 4, 1);
checkflip = check;
for i = 1: 8
for j = 1: 8
if(checkflip(i,j) == 0)
Result = 1;
end
if(checkflip(i,j) == 1)
Result = 0;
end
checkflip(i,j) = Result;
end
end
Method 3: Limited to One Loop
x=[0 1];
y=x([2 1]);
check = repmat([repmat(y,1,4);repmat(x,1,4)], 4, 1);
checkflip = check;
i = 1;
j = 0;
for Loop = 1: 64
j = j + 1;
if(j > 8)
j = 1;
i = i + 1;
end
if(checkflip(i,j) == 0)
Result = 1;
end
if(checkflip(i,j) == 1)
Result = 0;
end
checkflip(i,j) = Result;
end
Edit: A great solution suggested by #David uses the function numel() to transverse over all the array elements seamlessly:
x=[0 1];
y=x([2 1]);
check = repmat([repmat(y,1,4);repmat(x,1,4)], 4, 1);
checkflip = check;
for i=1:numel(checkflip)
checkflip(i) = ~checkflip(i);
end
Extension: Without a Loop (excepted from the question, all elements at once)
x=[0 1];
y=x([2 1]);
check = repmat([repmat(y,1,4);repmat(x,1,4)], 4, 1);
checkflip = double(~check);
Ran using MATLAB R2019b

How to get a certain output in Matlab without for loops?

I have a list of vertices where each row corresponds to a (x, y) coordinate.
For example, the following includes the vertices (0, 0), (1, 0), and (0, 1)
V = [0 0;
1 0;
0 1];
I also have a list of edges where the first column specifies the row of the starting vertex and the second column specifies the row of the ending vertex.
For example, the following includes the edges (0, 0) to (1, 0) and (0, 0) to (0, 1)
E = [1 2; % V(1) -> V(2) = (0, 0) -> (1, 0)
1 3] % V(1) -> V(3) = (0, 0) -> (0, 1)
I need to produce a list of edges with their actual coordinates from these two lists. That is, from V and E, I need
edge1 = [0 0]; % = E(1, 1) = V(1)
edge2 = [0 0]; % = E(2, 1) = V(1)
edge3 = [1 0]; % = E(1, 2) = V(2)
edge4 = [0 1]; % = E(2, 2) = V(3)
I know how to do this with for loops, but my supervisor said there is a more optimal solution using the function find(x), which returns the nonzero indices in an array. I do not see how this could be done with find. Is there a way that this could be done without using for loops but using the find function?
As far as I remember find is not really recommended when it comes to runtime, and for loops are not as bad as they where in the past taking JIT into consideration and MATLAB's effort to improve them.
However one possible solution without using for-loops (and without find) would be:
E = [1 2;
1 3];
V = [0 0;
1 0;
0 1];
Edges=V(E(:),:)
Edges =
0 0
0 0
1 0
0 1
So the output is not a list of different variables / edges but rather a matrix holding all of them row-wise.

Program to find prime numbers in MATLAB

I wrote some code to display the prime numbers between 2 and a user-chosen number, following the pseudocode on wikipedia. I am not sure why this does not work, as I have the increments correct following Erastothenes' Sieve. Please help me.
I have tried changing the bounds but this did not work.
There are no errors, but it returns the wrong output. If I enter 10, it returns 2, 3, 4, 5, 6, 7, 8, 9, 10.
n=input("Enter an upper limit: ");
nums= 2:n;
p=2;
for i = p:sqrt(n)
for j = (i^2):i:sqrt(n)
nums(j) = 0;
end
end
for k = 1:n-1
if nums(k) ~= 0
disp(nums(k))
end
end
You can use the primes function in MATLAB for this
N = 10; % upper limit
p = primes(N); % List of all primes up to (and including) N
With one step less automation, you could use another in-built isprime
p = 1:N; % List of all numbers up to N
p( ~isprime( p ) ) = []; % Remove non-primes
Finally without using built-ins, we can address your code!
I assume you're referring to this pseudocode for the Sieve of Eratosthenes on Wikipedia.
Input: an integer n > 1.
Let A be an array of Boolean values, indexed by integers 2 to n,
initially all set to true.
for i = 2, 3, 4, ..., not exceeding √n:
if A[i] is true:
for j = i2, i2+i, i2+2i, i2+3i, ..., not exceeding n:
A[j] := false.
Output: all i such that A[i] is true.
I'll follow it step by step, pointing out differences to your code:
n = 10;
A = [false; true(n-1,1)]; % array of true Booleans, first element (1) is not prime
% But I've included a first element to make indexing easier.
% In your code, you were using index 'i' which was incorrect, as your array started at 2.
% Two options: (1) take my approach and pad the array
% (2) take your approach and using indices i-1 and j-1
for ii = 2:sqrt(n)
if A(ii) == true % YOU WERE MISSING THIS STEP!
for jj = ii^2:ii:n % YOU ONLY LOOPED UNTIL SQRT(n)!
A(jj) = false;
end
end
end
p = find(A);
disp(p)
This outputs the expected values.
Note that, at the end of the manual looping method, A is equivalent to isprime(1:n), mirroring my earlier suggestions.
There is two mistakes in your code:
The multiple should be check until n and not sqrt(n)
Since your nums vector start with 2 and not 1, if you want to
access the right value you need to use nums(j-1) = 0
So:
n=100
nums= 2:n;
p=2;
for i = p:sqrt(n)
for j = (i^2):i:n
nums(j-1) = 0;
end
end
for k = 1:n-1
if nums(k) ~= 0
disp(nums(k))
end
end
Noticed that you can skip one for loop using a modulo, it's probably not faster than the previous solution since this code create a logical index that include each prime that already been found.
n = 100
nums= 2:n;
for i = 2:sqrt(n)
nums(mod(nums,i)==0 & nums != i) = [];
end
nums.'
I simply delete the value in nums that can be divided by x but not x.

Output 1, 0.5, or 0 depending if a matrix elements are prime, 1, or neither

I am sending a matrix to my function modifikuj, where I want to replace the elements of the matrix with:
1 if element is a prime number
0 if element is a composite number
0.5 if element is 1
I dont understand why it is not working. I just started with MATLAB, and I created this function:
function B = modifikuj(A)
[n,m] = size(A);
for i = 1:n
for j = 1:m
prost=1;
if (A(i,j) == 1)
A(i,j) = 0.5;
else
for k = 2:(A(i,j))
if(mod(A(i,j),k) == 0)
prost=0;
end
end
if(prost==1)
A(i,j)=1;
else
A(i,j)=0;
end
end
end
end
With
A = [1,2;3,4];
D = modifikuj(A);
D should be:
D=[0.5, 1; 1 0];
In MATLAB you'll find you can often avoid loops, and there's plenty of built in functions to ease your path. Unless this is a coding exercise where you have to use a prescribed method, I'd do the following one-liner to get your desired result:
D = isprime( A ) + 0.5*( A == 1 );
This relies on two simple tests:
isprime( A ) % 1 if prime, 0 if not prime
A == 1 % 1 if == 1, 0 otherwise
Multiplying the 2nd test by 0.5 gives your desired condition for when the value is 1, since it will also return 0 for the isprime test.
You are not returning anything from the function. The return value is supposed to be 'B' according to your code but this is not set. Change it to A.
You are looping k until A(i,j) which is always divisible by itself, loop to A(i,j)-1
With the code below I get [0.5,1;1,0].
function A = modifikuj(A)
[n,m] = size(A);
for i = 1:n
for j = 1:m
prost=1;
if (A(i,j) == 1)
A(i,j) = 0.5;
else
for k = 2:(A(i,j)-1)
if(mod(A(i,j),k) == 0)
prost=0;
end
end
if(prost==1)
A(i,j)=1;
else
A(i,j)=0;
end
end
end
end
In addition to #EuanSmith's answer. You can also use the in built matlab function in order to determine if a number is prime or not.
The following code will give you the desired output:
A = [1,2;3,4];
A(A==1) = 0.5; %replace 1 number with 0.5
A(isprime(A)) = 1; %replace prime number with 1
A(~ismember(A,[0.5,1])) = 0; %replace composite number with 0
I've made the assumption that the matrice contains only integer.
If you only want to learn, you can also preserve the for loop with some improvement since the function mod can take more than 1 divisor as input:
function A = modifikuj(A)
[n,m] = size(A);
for i = 1:n
for j = 1:m
k = A(i,j);
if (k == 1)
A(i,j) = 0.5;
else
if all(mod(k,2:k-1)) %check each modulo at the same time.
A(i,j)=1;
else
A(i,j)=0;
end
end
end
end
And you can still improve the prime detection:
2 is the only even number to test.
number bigger than A(i,j)/2 are useless
so instead of all(mod(k,2:k-1)) you can use all(mod(k,[2,3:2:k/2]))
Note also that the function isprime is a way more efficient primality test since it use the probabilistic Miller-Rabin algorithme.

Multidimensional data storage and interpolation

I have a function (so to speak, i actually have data with this characteristic) with one variable x and several parameters a, b and c, so y = f(x, a, b, c).
Now i want to interpolate within families of parameters (for example for variations of a).
I'm currently doing this for data with one parameter (here, y is the data matrix)
% generate variable and data
x = linspace(0, 1, 100);
a = [0, 1]; % parameter
for i = 1:length(a)
y(:, i) = x.^2 + a(i);
end
% interpolate:
yi = interp1(a, y.', 0.5);
This works fine, but how do i expand this to more dimensions?
My current data format is like this: Each column of my data matrix represents one specific set of parameters, so for example:
0 0 0 0
1 1 1 1
2 2 2 2
3 3 3 3
where the first column denotes a = 0, b = 0, the second a = 1, b = 0, the third a = 0, b = 1 and the last a = 1, b = 1 (values are just for clarification, this is not on purpose binary. Also, the data columns are obviously not the same).
This data format is just the consequence of my data aquisition scheme, but i'm happy to change this into something more useful. Whatever works.
Works well for me:
% generate variable and data
x = linspace(0, 1, 100);
a = [0, 1, 2]; % parameter
b = [3, 4, 5]; % parameter
c = [6, 7, 8]; % parameter
% Create grid
[X,A,B,C]=ndgrid(x,a,b,c);
% define function
foo = #(x,p1,p2,p3) p1.*x.^2 + p2.*x + p3;
% evaluate function
Y = foo(X,A,B,C);
% interpolate:
yi = interpn(X,A,B,C,Y,x,1,4,6);
#zlon's answer works fine for the interpolation part, here i want to show how to convert the data from the format i provided to the needed format for the interpolation.
The two-dimensional matrix must be transformed into a N-dimensional one. Since the columns are not necessarily in order, we need to find the right ones. This is what i did:
First, we need to know the parameter set of each column:
a = [ 2, 2, 1, 0, 0, 1 ];
b = [ 1, 0, 0, 1, 0, 1 ];
These vectors length match the number of columns in the data matrix. The first column for example now contains the data for a = 2 and b = 1.
Now we can generate the new table:
A = -Inf;
i = 1;
while true
A = min(a(a > A)); % find next a
if isempty(A)
break
end
idxa = find(a == A); % store possible indices
B = -Inf;
j = 1;
while true
B = min(b(b > B))); % find next b
if isempty(B)
break
end
idxb = find(b == B); % store possible indices
% combine both indices
idx = intersect(idxa, idxb);
% save column in new data table
data(:, i, j) = olddata(:, idx);
% advance
j = j + 1;
end
i = i + 1;
end