Determining time complexity of for loop? - discrete-mathematics

I have loop
sum = 0 ;
for ( i = n ; i > 0; i = i/3 )
for ( j = 0 ; j < n^3 ; j++ )
sum++ ;
I have to figure out the time complexity in big theta notation, but the i/3 in the first loop is confusing me.

In normal math, this loop will never terminate. O(inf). In discrete math, it is O(n^3).
The outer loop...
for ( i = n ; i > 0; i = i/3 )
The loop runs while i is greater than 0. If i is positive, reducing i by a third will never bring i below 0 becoming fractionally smaller and smaller.
If i is an IEEE Floating Point number, it will eventually reach 0 after a lot of iterations depending on the size of your floats.
If the numbers are assumed to be integers (which is what I think you're asking with the discrete-mathematics tag) the outer loop is O(log(n)) as i is cut by a third each iteration and will rapidly reach 0.
The inner loop, standing on its own, is pretty easy to see as O(n^3).
for ( my $j = 0; $j < $n**3 ; $j++ ) {
$sum++;
}
An O(n^3) loop inside an O(log(n)) loop is O(n^3 log(n)) which isn't a significantly different growth curve from O(n^3).

Related

Can operations on submatrices (and subvectors) be vectorized?

I'm currently working on an edge detector in octave. Coming from other programming languages like Java and Python, I'm used to iterating in for loops, rather than performing operations on entire matrices. Now in octave, this causes a serious performance hit, and I'm having a bit of difficulty figuring out how to vectorize my code. I have the following two pieces of code:
1)
function zc = ZeroCrossings(img, T=0.9257)
zc = zeros(size(img));
# Iterate over central positions of all 3x3 submatrices
for y = 2:rows(img) - 1
for x = 2:columns(img) - 1
ndiff = 0;
# Check all necessary pairs of elements of the submatrix (W/E, N/S, NW/SE, NE/SW)
for d = [1, 0; 0, 1; 1, 1; 1, -1]'
p1 = img(y-d(2), x-d(1));
p2 = img(y+d(2), x+d(1));
if sign(p1) != sign(p2) && abs(p1 - p2) >= T
ndiff++;
end
end
# If at least two pairs fit the requirements, these coordinates are a zero crossing
if ndiff >= 2
zc(y, x) = 1;
end
end
end
end
2)
function g = LinkGaps(img, k=5)
g = zeros(size(img));
for i = 1:rows(img)
g(i, :) = link(img(i, :), k);
end
end
function row = link(row, k)
# Find first 1
i = 1;
while i <= length(row) && row(i) == 0
i++;
end
# Iterate over gaps
while true
# Determine gap start
while i <= length(row) && row(i) == 1
i++;
end
start = i;
# Determine gap stop
while i <= length(row) && row(i) == 0
i++;
end
# If stop wasn't reached, exit loop
if i > length(row)
break
end
# If gap is short enough, fill it with 1s
if i - start <= k
row(start:i-1) = 1;
end
end
end
Both of these functions iterate over submatrices (or rows and subrows in the second case), and particularly the first one seems to be slowing down my program quite a bit.
This function takes a matrix of pixels (img) and returns a binary (0/1) matrix, with 1s where zero crossings (pixels whose corresponding 3x3 neighbourhoods fit certain requirements) were found.
The outer 2 for loops seem like they should be possible to vectorize somehow. I can put the body into its own function (taking as an argument the necessary submatrix) but I can't figure out how to then call this function on all submatrices, setting their corresponding (central) positions to the returned value.
Bonus points if the inner for loop can also be vectorized.
This function takes in the binary matrix from the previous one's output, and fills in gaps in its rows (i.e. sets them to 1). A gap is defined as a series of 0s of length <= k, bounded on both sides by 1s.
Now I'm sure at least the outer loop (the one in LinkGaps) is vectorizable. However, the while loop in link again operates on subvectors, rather than single elements so I'm not sure how I'd go about vectorizing it.
Not a full solution, but here is an idea how you could do the first without any loops:
% W/E
I1 = I(2:end-1,1:end-2);
I2 = I(2:end-1,3:end );
C = (I1 .* I2 < 0) .* (abs(I1 - I2)>=T);
% N/S
I1 = I(1:end-2,2:end-1);
I2 = I(3:end, 2:end-1);
C = C + (I1 .* I2 < 0) .* (abs(I1 - I2)>=T);
% proceed similarly with NW/SE and NE/SW
% ...
% zero-crossings where count is at least 2
ZC = C>=2;
Idea: form two subimages that are appropriately shifted, check for the difference in sign (product negative) and threshold the difference. Both tests return a logical (0/1) matrix, the element-wise product does the logical and, result is a 0/1 matrix with 1 where both tests have succeeded. These matrices can be added to keep track of the counts (ndiff).

How does this prime number generation method work?

I want to figure out what the background of this code is, specifically, the reason of the commands written. I understand everything until the first line of the for loop, but then I got lost with the second line:
N= input('Enter your number: ');
primes = 2:N;
p=2;
while (p <= N)
for i = 2*p:p:N
primes(i - 1) = 0;
end;
p = p + 1;
end
primes = primes(primes > 0)
Can someone help me to understand this code please?
The code implements the Sieve of Eratosthenes to find the primes numbers.
It generated the array primes which contains the integer numbers from 2 to N
primes = 2:N
The while loop iterates across the integer values
while (p <= N)
At each iteration of the while loop in the for loop, the multiples of the current value of p are generated within the definition of the set of values of the loop index variable i
2*p:p:N
at each iteration of the for loop the element of the primes array in the position i is set to 0
At the end of the for loop, all the multiples of the current value of p are then set to 0
primes(i - 1) = 0;
The process is then repeated for all the integers values between 2 and N by the while loop.
At the end of the while loop the array primes will contains the prime numbers between 2 and N.
primes = primes(primes > 0)
This instruction finds the numbers different from 0 in the array primes and re-defines the array itself by assigning to it all all the numbers different from 0 or, that is the same. it removes from the array primes all of the 0s.
By definition the multiple of a given number is not a prime number.
Hope this helps.

project euler 23 MATLAB

I'm slowly working my way though problem 23 in project Euler but I;ve run into a snag. Problem #23 involves trying to find the sum of all numbers that cannot be creat by two abundant numbers.
First here's my code:
function [divisors] = SOEdivisors(num)
%SOEDIVISORS This function finds the proper divisors of a number using the sieve
%of eratosthenes
%check for primality
if isprime(num) == 1
divisors = [1];
%if not prime find divisors
else
divisors = [0 2:num/2]; %hard code a zero at one.
for i = 2:num/2
if divisors(i) %if divisors i ~= 0
%if the remainder is not zero it is not a divisor
if rem(num, divisors(i)) ~= 0
%remove that number and all its multiples from the list
divisors(i:i:fix(num/2)) = 0;
end
end
end
%add 1 back and remove all zeros
divisors(1) = 1;
divisors = divisors(divisors ~= 0);
end
end
This function finds abundant numbers
function [abundantvecfinal] = abundantnum(limitnum)
%ABUNDANTNUM creates a vector of abundant numbers up to a limit.
abundantvec = [];
%abundant number count
count = 1;
%test for abundance
for i = 1:limitnum
%find divisors
divisors = SOEdivisors(i);
%if sum of divisors is greater than number it is abundant, add it to
%vector
if i < sum(divisors)
abundantvec(count) = i;
count = count + 1;
end
end
abundantvecfinal = abundantvec;
end
And this is the main script
%This finds the sum of all numbers that cannot be written as the sum of two
%abundant numbers and under 28123
%get abundant numbers
abundant = abundantnum(28153);
%total non abundant numbers
total = 0;
%sums
sums = [];
%count moves through the newsums vector allowing for a new space for each
%new sum
count = 1;
%get complete list of possible sums under using abundant numbers under
%28123 then store them in a vector
for i = 1:length(abundant)
for x = 1:length(abundant)
%make sure it is not the same number being added to itself
if i ~= x
sums(count) = abundant(i) + abundant(x);
count = count + 1;
end
end
end
%setdiff function compares two vectors and removes all similar elements
total = sum(setdiff(1:28153, sums));
disp(total)
The first problem is it gives me the wrong answer. I know that I'm getting the correct proper divisors and the correct abundant numbers so the problem probably lies in the main script. And it seems as though it almost certainly lies in the creation of the abundant sums. I was hoping someone might be able to find an error I havent been able to.
Beyond that, the code is slow due to the multiple for loops, so I'm also looking for ways to do problems like this more efficiently.
Thanks!
Well, I don't have enough reputation to just comment. Why are you ruling out adding the same number to itself? The problem statement gives the example 12+12=24.
I also don't see a reason that x should ever be less than i. You don't need to sum the same two numbers twice.

Find the number of pairs whose sum is divisible by k?

Given a value of k. Such that k<=100000
We have to print the number of pairs such that sum of elements of each pair is divisible by k.
under the following condition first element should be smaller than second, and both element should be less than 109.
I've found a solution, let a and b numbers such that (a+b)%k=0 then we have to find that pairs (a,b), where a<b, so let's count how many pairs (a,b) satisfy the condition that a+b=k, for example if k=3 0+3=3, 1+2=3, 2+1=3, 3+0=3 there are 4 pairs but only 2 pairs which is (K+1)/2 (integer division) so similar for find the pairs (a,b) which sum is 2k, 3k,.. nk, and the solution will be (k+1)/2 + (2k+1)/2 + (3k+1)/2 + ... + (nk+1)/2, and that is equal to (k*n*(n+1)/2 + n)/2 with time complexity O(1), take care in the case if n*k=2*10^9, because a can't be more than 10^9 for the given constraint.
Solution in O(N) time and O(N) space using hash map.
The concept is as follows:
If (a+b)%k=0 where
a=k*SOME_CONSTANT_1+REMAINDER_1
b=k*SOME_CONSTANT_2+REMAINDER_2
then (REMAINDER_1 +REMAINDER_2 )%k will surely be 0
so for an array (4,2,3,31,14,16,8) and k =5 if you have some information like below , you can figure out which all pairs sum %k =0
Note that, Bottom most row consist of all the remainders from 0 to k-1 and all the numbers corresponding to it.
Now all you need to do is move both the pointer towards each other until they meet. If both the pointers locations have number associated with it their sum%k will be 0
To solve it, you can keep track of all the remainder you have seen so far by using hash table
create a hash map Map<Integer, List>.
Pre-populate its keys with 0 to k-1;
iterate over array and put remainder of each number in the map with Key = remainder and put the actual number in the list,
Iterate over the key set using two pointers moving each other. And sum += listSizeAsPointedByPointer1 * listSizeAsPointedByPointer2
One way is brute force:
int numPairs = 0;
for (i = 0; i < 10e9; i++)
{
for (j = i+1; j < 10e9; j++)
{
int sum = i + j;
if (sum % k == 0) numPairs++;
}
}
return numPairs;
I'll leave it up to you to optimize this for performance. I can think of at least one way to significantly speed this up.
Some psuedo-code to get you started. It uses the brute-force technique you say you tried, but maybe something was wrong in your code?
max = 1000000000
numberPairs = 0
for i = 1 to max - 2 do
for j = i + 1 to max - 1 do
if (i + j) mod k = 0 then
numberPairs = numberPairs + 1
end if
end do
end do

Matlab: Convert elements larger (smaller) than 1 (-1) into a sequence of 1 (-1)

UPDATE: I've done some testing, and the solution of Jonas is the fastest for a range of different size input vectors. In particular, as angainor points out, the solution scales up to large sizes incredibly well - an important test as it is usually the large size problems that prompt us to pose these kind of questions on SO. Thanks to both Jonas and tmpearce for your solutions - based on the efficiency of the solution for large size problems I'm giving the answer tick to Jonas.
My Question: I have this column vector:
Vec = [0; 1; 2; -1; -3; 0; 0; 2; 1; -1];
I would like to convert every element greater than one into a sequence of ones that has length equal to the value of the element. Similarly, I want to convert every element less than minus one into a sequence of minus ones. Thus my output vector should look like this:
VecLong = [0; 1; 1; 1; -1; -1; -1; -1; 0; 0; 1; 1; 1; -1];
Note that each 2 has been changed into two 1's, while the -3 has been changed into three -1's. Currently, I solve the problem like this:
VecTemp = Vec;
VecTemp(VecTemp == 0) = 1;
VecLong = NaN(sum(abs(VecTemp)), 1);
c = 1;
for n = 1:length(Vec)
if abs(Vec(n)) <= 1
VecLong(c) = Vec(n);
c = c + 1;
else
VecLong(c:c + abs(Vec(n))) = sign(Vec(n));
c = c + abs(Vec(n));
end
end
This doesn't feel very elegant. Can anyone suggest a better method? Note: You can assume that Vec will contain only integer values. Thanks in advance for all suggestions.
You can use the good old cumsum-approach to repeating the entries properly. Note that I'm assigning a few temporary variables that you can get rid of, if you want to put everything into one line.
%# create a list of values to repeat
signVec = sign(Vec);
%# create a list of corresponding indices that repeat
%# as often as the value in signVec has to be repeated
tmp = max(abs(Vec),1); %# max: zeros have to be repeated once
index = zeros(sum(tmp),1);
index([1;cumsum(tmp(1:end-1))+1])=1; %# assign ones a pivots for cumsum
index = cumsum(index); %# create repeating indices
%# repeat
out = signVec(index);
out'
out =
0 1 1 1 -1 -1 -1 -1 0 0 1 1 1 -1
Edit: I thought of another (slightly obscure) but shorter way to do this, and it is faster than the loop you've got.
for rep=1:100000
#% original loop-based solution
end
toc
Elapsed time is 2.768822 seconds.
#% bsxfun-based indexing alternative
tic;
for rep=1:100000
TempVec=abs(Vec);TempVec(Vec==0)=1;
LongVec = sign(Vec(sum(bsxfun(#gt,1:sum(TempVec),cumsum(TempVec)))+1))
end
toc
Elapsed time is 1.798339 seconds.
This answer scales pretty well too, compared to the original - at least, to a point. There's a performance sweet spot.
Vec = repmat(OrigVec,10,1);
#% test with 100,000 loops
#% loop-based solution:
Elapsed time is 19.005226 seconds.
#% bsxfun-based solution:
Elapsed time is 4.411316 seconds.
Vec = repmat(OrigVer,1000,1);
#% test with 1,000 loops - 100,000 would be horribly slow
#% loop-based solution:
Elapsed time is 18.105728 seconds.
#% bsxfun-based solution:
Elapsed time is 98.699396 seconds.
bsxfun is expanding the vector into a matrix, then collapsing it with sum. With very large vectors this is needlessly memory heavy compared to the loop, so it ends up losing. Before then though, it does quite well.
Original, slow answer:
Here's a one-liner:
out=cell2mat(arrayfun(#(x) repmat(((x>0)*2)-1+(x==0),max(1,abs(x)),1),Vec,'uni',0));
out' =
0 1 1 1 -1 -1 -1 -1 0 0 1 1 1 -1
What's going on:
((x>0)*2)-1 + (x==0) #% if an integer is >0, make it a 1, <0 becomes -1, 0 stays 0
max(1,abs(x)) #% figure out how many times to replicate the value
arrayfun(#(x) (the above stuff), Vec, 'uni', 0) #% apply the function
#% to each element in the array, generating a cell array output
cell2mat( (the above stuff) ) #% convert back to a matrix