Time complexity of this primality testing algorithm? - algorithm

I have the following code which determines whether a number is prime:
public static boolean isPrime(int n){
boolean answer = (n>1)? true: false;
for(int i = 2; i*i <= n; ++i)
{
System.out.printf("%d\n", i);
if(n%i == 0)
{
answer = false;
break;
}
}
return answer;
}
How can I determine the big-O time complexity of this function? What is the size of the input in this case?

Think about the worst-case runtime of this function, which happens if the number is indeed prime. In that case, the inner loop will execute as many times as possible. Since each iteration of the loop does a constant amount of work, the total work done will therefore be O(number of loop iterations).
So how many loop iterations will there be? Let's look at the loop bounds:
for(int i = 2; i*i <= n; ++i)
Notice that this loop will keep executing as long as i2 ≤ n. Therefore, the loop will terminate as soon as i ≥ √n + 1. Consequently, the loop will end up running O(√n) times, so the worst-case time complexity of the function is O(√n).
As to your second question - what is the size of the input? - typically, when looking at primality testing algorithms (or other algorithms that work on large numbers), the size of the input is defined to be the number of bits required to write out the input. In your case, since you're given a number n, the number of bits required to write out n is Θ(log n). This means that "polynomial time" in this case would be something like O(logk n). Your runtime, O(√n), is not considered polynomial time because O(√n) = O((2log n)1/2), which is exponentially larger than the number of bits required to write out the input.
Hope this helps!

Related

What is the time complexity of for ( int i =0; i <n %10;++ i )

I think the answer to this question is o(1) since the loop only iterates from 0 to 9 but when discussing this same question with my friend he told me its o(n) since according to him "the number of iterations is directly proportional to n."
Which one of us is correct here? Is it O(1) or O(n)?
Neither of you is entirely correct, as you aren't properly specifying the input size.
The number of iterations is bounded by the constant 10, but you need to know how long it takes to compute n % 10 in order to discover that constant. n % 10 is not proportional to n, but neither is it independent of n.
After you get n % 10, your loop has O(1) iterations. The total complexity depends on how long it takes to find n % 10, as well as how long each iteration takes.
Keep in mind that n is not the size of your input. The input size is the number of bits you need to represent the value n, which is N = O(log n).
Thats actually a very interesting and definition dependent question.
Since n%10 is not proportional to the asymptotic complexity of n, but it can't be viewed as a constant either, because it depends on n.
The only not-incorrect solution is to make n%10 constant, since it can be statically (in the sense of exaustibly in advance) written as
int f = n % 10; // % itself is in O(1)
int iterations;
switch(f){
case 0:
iterations = 0;
break;
case 1:
iterations = 1;
break;
case 2:
iterations = 2;
break;
// ...
case 9:
iterations = 9;
break;
default: iterations = 0; break;
}
for(int i = 0; i < iterations; i++){
//...
}
Which is obviously in O(1).
Saying your for loop corresponds to O(n) would directly suggest that it increases in computational complecity linearly with n, which is only the case if the module operator is in O(n), which it is not for most CPUs.

Simplifying Big O

// Assume n is some random integer
int q = 1;
while (q <= Math.Sqrt(n))
{
q++;
int k = 1;
while (k <= Math.Log(n, 2))
{
k++;
if (q^k == n){
return true;
}
}
return false;
In this code above, I'm finding it very difficult to decide what the Big O would be for the worst case. Since the loop runs N times with a nested loop that runs log2(N) times I know it should be O(sqrt(n)*log2(n)) times. However, I find it very confusing as to how it's suppose to be simplified. I understand that sqrt(n) grows faster but I'm unsure if I can disregard log2(n) since it's being multiplied. If I'm not disregarding log2(n), I'm not sure if it should be n^2 since it's two terms of n being multiplied, or if I should leave it as it is.
Take it simple, think that the extern while loop is executed sqrt(n) times and that inside there is another while loop that is executed log2(n) times and inside it assume that all operations take O(1) time to being executed.
So we have a while loop executed sqrt(n) times and an operation inside it that take O(log2(n)) to being executed (that is the other while loop, think of it as a black box pf which you know the asymptotic running time). Therefore the complexity of the algorithm is O(sqrt(n)log2(n)).

How does calculating a FOR loop sentinel affect complexity

Let's say we have a FOR loop
int n;
for (int i = 0; i < sqrt(n); i++)
{
statement;
}
Does calculating the sqrt of i add complexity to the loop's O(n) complexity? In my example the sqrt function in Java has a time complexity of O(log n), how does this affect the time complexity of the loop? Is the sqrt function applied for every sequence of the loop or just once and that value is stored and used again?
I suppose this can depend on language but generally i < sqrt(n) check will be ran after each loop's iteration so effectively you'll call it sqrt(n) times. Good idea is to store sqrt(n) result in variable and compare it to i, so
int n;
double sn = sqrt(n);
for (int i = 0; i < sn; i++)
{
statement;
}
The sqrt function is applied for every sequence of the loop. The use of n is different though. The complexity of O(logn) for the sqrt has n being the number of bits in the value, but the O(n) of the loop has n being the actual value. With that definition of n, then the sqrt is more like O(loglogn).
For loops like these, the complexity of operations on numbers like sqrt can be treated as constant time. The number of bits is bounded so the time is insignificant compared to the larger loop.
For the variable n given in the question,
O(log(d=bits of variable) * sqrt(n=number in the loop))
assuming finding n-digit(bits) numbers' sqrt is log(D) as 'fgb' said first.
Assuming n's number of bits are constant all through the computation and for all hardwares, so:
O(log(constant) * sqrt (n))
O(constant * sqrt(n))
O(sqrt(n))
But if it is not strongly-styped and if n's bits is increased gradually(such as going from 64 bit to 128 to 256 bit to 1024 but having same value) then it would be
O(log(d)*sqrt(n))
Answering your question about time complexity
sqrt(n) is called once each iteration, and this will take some additionally time.
But since sqrt(n) is independent from i or any other value in the statement, the calculation takes always the same amount of time. So from my understaning it does not increase the complexity of the loop, it is still O(n).

Big Oh nested while

I am having some challenges with big-oh problems. These are NOT homework problems. I am writing these problems to better understand the concept here.
function func(n)
{
int k,i = 0;
while(k < n){ < --- Guessing this outer loop is O(n/2)
k = n + 2
i = 0;
while(i < k){ <--- Not sure what this is?
i ++;
i = i * i;
}
}
}
I would really like it if you can explain to me what is going on in the inner loop and how your logic ends up at the big-o notation that you finally end up at.
The outer loop, with its test (k < n) and its step, k = n + 2, will run one time, providing an O(1) factor of complexity.
The inner loop has test (i < k) which is to say (i < n+2), and has steps i++; i=i*i; At the end,
i = (...(((1+1)^2+1)^2+1)^2+ ... )^2 > n+2`
which makes the value of i super-exponential. That is, i grows faster than exp(exp(p)) in p passes so that overall complexity is less than O(log log n). This is a tighter bound than the previously-mentioned O(log n), which also is an upper bound but not as tight.
While #alestanis has provided what looks to me like a much more accurate analysis of this problem than those in the comments, I still don't think it's quite right.
Let's create a small test program that prints out the values of i produced by the inner loop:
#include <iostream>
void inner(double k) {
double i;
i = 0.0;
while(i < k) {
i ++;
i = i * i;
std::cout << i << "\n";
}
}
int main() {
inner(1e200);
return 0;
}
When I run this, the result I get is:
1
4
25
676
458329
2.10066e+011
4.41279e+022
1.94727e+045
3.79186e+090
1.43782e+181
1.#INF
If the number of iterations were logarithmic, then the number of iterations to reach a particular number should be proportional to the number of digits in the limit. For example, if it were logarithmic, it should take around 180 iterations to reach 1e181, give or take some (fairly small) constant factor. That's clearly not the case here at all -- as is easily visible by looking at the exponents of the results in scientific notation, this is approximately doubling the number of digits every iteration, where logarithmic would mean it was roughly adding one digit every iteration.
I'm not absolutely certain, but I believe that puts the inner loop at something like O(log log N) instead of just O(log N). I think it's pretty easy to agree that the outer loop is probably intended to be O(N) (though it's currently written to execute only once), putting the overall complexity at O(N log log N).
I feel obliged to add that from a pragmatic viewpoint, O(log log N) can often be treated as essentially constant -- as shown above, the highest limit you can specify with a typical double precision floating point number is reached in only 11 iterations. As such, for most practical purposes, the overall complexity can be treated as O(N).
[Oops -- didn't notice he'd answered as I was writing this, but it looks like #jwpat7 has reached about the same conclusion I did. Kudos to him/her.]
The second loop squares the value of i until it reaches k. If we ignore the constant term, this loop runs in O(log k) time.
Why? Because if you solve i^m = k you get m = constant * log(k).
The outer loop, as you said, runs in O(n) time.
As bigger values of k depend on n, you can say the inner loop runs in O(log n) which gives you an overall complexity of O(n log n).

Worst Case Time Complexity for an algorithm

What is the Worst Case Time Complexity t(n) :-
I'm reading this book about algorithms and as an example
how to get the T(n) for .... like the selection Sort Algorithm
Like if I'm dealing with the selectionSort(A[0..n-1])
//sorts a given array by selection sort
//input: An array A[0..n - 1] of orderable elements.
//output: Array A[0..n-1] sorted in ascending order
let me write a pseudocode
for i <----0 to n-2 do
min<--i
for j<--i+1 to n-1 do
ifA[j]<A[min] min <--j
swap A[i] and A[min]
--------I will write it in C# too---------------
private int[] a = new int[100];
// number of elements in array
private int x;
// Selection Sort Algorithm
public void sortArray()
{
int i, j;
int min, temp;
for( i = 0; i < x-1; i++ )
{
min = i;
for( j = i+1; j < x; j++ )
{
if( a[j] < a[min] )
{
min = j;
}
}
temp = a[i];
a[i] = a[min];
a[min] = temp;
}
}
==================
Now how to get the t(n) or as its known the worst case time complexity
That would be O(n^2).
The reason is you have a single for loop nested in another for loop. The run time for the inner for loop, O(n), happens for each iteration of the outer for loop, which again is O(n). The reason each of these individually are O(n) is because they take a linear amount of time given the size of the input. The larger the input the longer it takes on a linear scale, n.
To work out the math, which in this case is trivial, just multiple the complexity of the inner loop by the complexity of the outer loop. n * n = n^2. Because remember, for each n in the outer loop, you must again do n for the inner. To clarify: n times for each n.
O(n * n).
O(n^2)
By the way, you shouldn't mix up complexity (denoted by big-O) and the T function. The T function is the number of steps the algorithm has to go through for a given input.
So, the value of T(n) is the actual number of steps, whereas O(something) denotes a complexity. By the conventional abuse of notation, T(n) = O( f(n) ) means that the function T(n) is of at most the same complexity as another function f(n), which will usually be the simplest possible function of its complexity class.
This is useful because it allows us to focus on the big picture: We can now easily compare two algorithms that may have very different-looking T(n) functions by looking at how they perform "in the long run".
#sara jons
The slide set that you've referenced - and the algorithm therein
The complexity is being measured for each primitive/atomic operation in the for loop
for(j=0 ; j<n ; j++)
{
//...
}
The slides rate this loop as 2n+2 for the following reasons:
The initial set of j=0 (+1 op)
The comparison of j < n (n ops)
The increment of j++ (n ops)
The final condition to check if j < n (+1 op)
Secondly, the comparison within the for loop
if(STudID == A[j])
return true;
This is rated as n ops. Thus the result if you add up +1 op, n ops, n ops, +1 op, n ops = 3n+2 complexity. So T(n) = 3n+2
Recognize that T(n) is not the same as O(n).
Another doctoral-comp flashback here.
First, the T function is simply the amount of time (usually in some number of steps, about which more below) an algorithm takes to perform a task. What a "step" is, is somewhat defined by the use; for example, it's conventional to count the number of comparisons in sorting algorithms, but the number of elements searched in search algorithms.
When we talk about the worst-case time of an algorithm, we usually express that with "big-O notation". Thus, for example, you hear that bubble sort takes O(n²) time. When we use big O notation, what we're really saying is that the growth of some function -- in this case T -- is no faster than the growth of some other function times a constant. That is
T(n) = O(n²)
means for any n, no matter how large, there is a constant k for which T(n) ≤ kn². A point of some confustion here is that we're using the "=" sign in an overloaded fashion: it doesn't mean the two are equal in the numerical sense, just that we are saying that T(n) is bounded by kn².
In the example in your extended question, it looks like they're counting the number of comparisons in the for loop and in the test; it would help to be able to see the context and the question they're answering. In any case, though, it shows why we like big-O notation: W(n) here is O(n). (Proof: there exists a constant k, namely 5, for which W(n) ≤ k(3n)+2. It follows by the definition of O(n).)
If you want to learn more about this, consult any good algorithms text, eg, Introduction to Algorithms, by Cormen et al.
write pseudo codes to search, insert and remove student information from the hash table. calculate the best and the worst case time complexities
3n + 2 is the correct answer as far as the loop is concerned. At each step of the loop, 3 atomic operations are done. j++ is actually two operations, not one. and j

Resources