Can we use probabilities to determine whether an algorithm is non-terminating? - probability

My question isn't about some specific code but rather theoritical, so my apologies in advance if that's not the right place to post it.
Given a certain problem - for the sake of discussion, let it be a permutation problem - with an input of size N, and P the constant time needed to calculate 1 permutation; in this case, we need about T = ~ (P)*(N!) = O(N!) time to produce all results. If by any chance our algorithm takes much more than the expected time, it may be safe to assume it doesn't terminate.
For example, for P = 0.5 secs, N = 8, ⇒ T = 0.5*8! = 20.160 secs. Any value above T is 'suspicious' time.
My question is, how can one introduce a probability function which asymptotically reaches 1, while running time increases infinitely?
Our 'evaluation' shall depend on a constant time P, the size N of our input, and the time complexity Q of our current problem, thus it may have this form: f(P, N, Q) = ... , whereas 0 ≤ f ≤ 1, f increasing, and f indicates the probability of having 'fallen' in a non-terminating state.
If this approach isnt enough, how can we make sure that after a certain amount of time, our program is probably running endlessly?

Related

How to calculate time complexity of existing code in production [duplicate]

This question already has answers here:
Big O, how do you calculate/approximate it?
(24 answers)
Closed 2 years ago.
I have gone through a couple of examples in understanding the time complexity of a program using the operation count and step count technique but these examples are small and straight. However in real-world programming, if someone gives you the production or live code to find the worst case time complexity of it. How does one start analyzing it?
Is there any technique or thumb rules for analyzing the program? say I have a function as shown below that has if and for conditions. For some values of n, it goes deep but for some values, it does not.
So maximum number of operations is the worst case and the minimum number of operations is the best case.
How can we know when do maximum operations occur if we have this kind of conditional statement? (Other functions can go much deeper conditional and not the loops)
int l = 0;
while (l <= n){
int m = l + (n-l)/2;
if (arr[m] == x) return;
if (arr[m] < x) l = m + 1;
else n = m - 1;
}
How does one calculate the worst and best case by looking at the above code? Do the perform step count for the above program and by substituting n = 1 till 20 and get some values and then trying to derive the function? I would like to know how do people analyze time complexity for existing code when they have this kind of branching statements.
Step by Step analysis or Set of statements to follow to solve the above problem would be greatly helpful.
As each time half of the n is added to m and the loop will be continued by increasing a unit of l or changing the value of the n to the m-1 the following scenario can give the maximum operation:
In each iteration, the `else` part is happened and set `n` to `m-1`.
Let see what is happened for this case. As each time n is dividend by 2, and l is keeping 0, after O(log(n)) iteration, l == n.
Therefore, the time complexity of the loop is O(log(n)).
Notice that other cases can increase l faster towards n. For example, if l = m + 1 it means that l = (n-1)/2, and in the next iteration, m will be increased to n-1. Hence, just 2 iteration we will have to reach to the end of the loop.

Time complexity of recursive function dividing the input value by 2/3 everytime

I know that the time complexity of a recursive function dividing its input by /2 is log n base 2,I have come across some interesting scenarios on
https://stackoverflow.com/a/42038565/8169857
Kindly help me to understand the logic behind the scenarios in the answer regarding the derivation of the formula
It's back to the recursion tree. Why for 1/2 is O(log2(n))? Because if n = 2^k, you should divide k times to reach to 1. Hence, the number of computation is k = log2(n) comparison at most. Now suppose it is (c-1)/c. Hence, if n = (c/(c-1))^k, we need log_{c/(c-1)}(n) operations to reach to 1.
Now as for any constant c > 1, limit log2(n)/log_{c/(c-1)}(n), n \to \infty is equal to a constant greater than zero, log_{c/(c-1)}(n) = \Theta(log2(n)). Indeed, you can say this for any constants a, b > 1, log_a(n) = \Theta(log_b(n)). Now, the proof is completed.

How to obtain expected execution time and worst case for algorithms with infinite loop?

I have a one doubt about expected execution time and worst case executio time of algorithms, I was think a simple algorithm but this contain a loop infinite and I don't know about to explain that expected execution time?. I could always determine the time to algorithms with stop conditions.
This is my example (I just know, that more than n/2 can give true):
while (true)
{
int i = random(0,n-1);
bool e = decision(i); //Θ(n)
if (e==true)
return i;
}
The expected execution time is O(n).
With probability p >= 1/2, the first i will give decision(i) == true, so the loop will terminate after one call to decision.
Let q = 1 - p be the probability that it did not happen.
Then, with probability q * p, the second i will give decision(i) == true, so the loop will terminate after two calls to decision.
Similarly, with probability q^2 * p, the third i will give decision(i) == true, so the loop will terminate after three calls to decision, and so on.
By taking the sum, we have the expected number of calls to decision as
1 + q + q^2 + q^3 + ....
As q <= 1/2, the sum is at most 1 + 1/2 + 1/4 + 1/8 + ... which has an upper limit of 2.
So, the expected number of calls is limited by 2.
In total, as each call to decision takes O(n) time, the expected time to run is O(2*n) which is still O(n) since 2 is a constant.
It seems dependant on the probabilty that decision ends up being true.
So in this case decision takes n steps.
Which means that the runtime is O(n),
Now we take the probability of decision being true, assume 50%, that means on avarage we need to 2n steps per loop( sum(prob^x*n) , x=0..infinity ,prob=0.5).
Eventhough O increases by the decision probability, the multiplication is still linearly bound to the change of "decision" being true, and therefore stil O(n).

How to estimate the runtime when the input size is unknown

I am learning how to analyze the algorithm (First 5 chapters) by myself using this book: Introduction of Algorithms. Assume I have the following simple algorithm (I made it up):
for i <- 0 to 100
for j <- 1 to 2000
if (i*10 + j*20 = n)
c <- c+1
Now, if I want to estimate the running time of this algorithm for a specific input size, say n= 500, I'd say:
The running time in worst case is T(n) = 100*2000*500 = 10 * 10^7. However, when I want to generalize for any input size, i.e. n, I don't know how to do this! Kindly, can someone enlighten me?
Thank you
Your current running time is wrong. Since you always loop through all i and j values, it takes a total of 100*2000 iterations. This is independent of your n value. However, if you assume your unknowns are the max i and j values, denoted by I and J respectively, you could say your algorithm has running time O(IJ). You're on the right track, just replace your constants for variables.

How do you calculate big O on a function with a hard limit?

As part of a programming assignment I saw recently, students were asked to find the big O value of their function for solving a puzzle. I was bored, and decided to write the program myself. However, my solution uses a pattern I saw in the problem to skip large portions of the calculations.
Big O shows how the time increases based on a scaling n, but as n scales, once it reaches the resetting of the pattern, the time it takes resets back to low values as well. My thought was that it was O(nlogn % k) when k+1 is when it resets. Another thought is that as it has a hard limit, the value is O(1), since that is big O of any constant. Is one of those right, and if not, how should the limit be represented?
As an example of the reset, the k value is 31336.
At n=31336, it takes 31336 steps but at n=31337, it takes 1.
The code is:
def Entry(a1, q):
F = [a1]
lastnum = a1
q1 = q % 31336
rows = (q / 31336)
for i in range(1, q1):
lastnum = (lastnum * 31334) % 31337
F.append(lastnum)
F = MergeSort(F)
print lastnum * rows + F.index(lastnum) + 1
MergeSort is a standard merge sort with O(nlogn) complexity.
It's O(1) and you can derive this from big O's definition. If f(x) is the complexity of your solution, then:
with
and with any M > 470040 (it's nlogn for n = 31336) and x > 0. And this implies from the definition that:
Well, an easy way that I use to think about big-O problems is to think of n as so big it may as well be infinity. If you don't get particular about byte-level operations on very big numbers (because q % 31336 would scale up as q goes to infinity and is not actually constant), then your intuition is right about it being O(1).
Imagining q as close to infinity, you can see that q % 31336 is obviously between 0 and 31335, as you noted. This fact limits the number of array elements, which limits the sort time to be some constant amount (n * log(n) ==> 31335 * log(31335) * C, for some constant C). So it is constant time for the whole algorithm.
But, in the real world, multiplication, division, and modulus all do scale based on input size. You can look up Karatsuba algorithm if you are interested in figuring that out. I'll leave it as an exercise.
If there are a few different instances of this problem, each with its own k value, then the complexity of the method is not O(1), but instead O(k·ln k).

Resources