O Notation Help - java

I am getting stuck with the class work we got this week and its a subject i really want to learn so for once i thought i would do the additional reading!!!!
The method is provided for us and i am just writign some test cases. This is where my knowledge gets a little hazy. If the time increases then i am underestimatign the complexity i believe? in this case n^3 isnt enough and n^4 is too much, hence the gradual reduction to 0.
That means there is a compelxity that lies between the 2, and this is where log n comes in as log n is a value less than n? but this is as far as my knowledge goes
i was really hoping someone could clear this confusion up for me with a better explanation than whats on the lecture slides as they make no sence to me at all, thanks
/**
* Number 5
*/
public int Five(int n)
{
int sum=0;
for(int i=0; i<n; i++){
for(int j=0; j<i*i; j++){
sum++;
}
}
return sum;
}
public void runFive()
{
// Test n^2 complexity
// System.out.println("We are passing the value 5, This returns us an n value of " + Five(5) + " , With a time complexity of " + complexityN2(Five(5), 5) + " This is to test the value of 5 in a n^2 test" );
// System.out.println("We are passing the value 10, This returns us an n value of " + Five(10) + " , With a time complexity of " + complexityN2(Five(10), 10) + "This is to test the value of 10 in a n^2 test" );
// System.out.println("We are passing the value 100, This returns us an n value of " + Five(100) + " , With a time complexity of " + complexityN2(Five(100), 100) + "This is to test the value of 100 in a n^2 test" );
// System.out.println("We are passing the value 1000, This returns us an n value of " + Five(1000) + " , With a time complexity of " + complexityN2(Five(1000), 1000) + "This is to test the value of 1000 in a n^2 test" );
// System.out.println("We are passing the value 10000, This returns us an n value of " + Five(10000) + " , With a time complexity of " + complexityN2(Five(10000), 10000) + "This is to test the value of 10000 in a n^2 test" );
// Test n^3 complexity
// System.out.println("We are passing the value 5, This returns us an n value of " + Five(5) + " , With a time complexity of " + complexityN3(Five(5), 5) + " This is to test the value of 5 in a n^3 test" );
// System.out.println("We are passing the value 10, This returns us an n value of " + Five(10) + " , With a time complexity of " + complexityN3(Five(10), 10) + "This is to test the value of 10 in a n^3 test" );
// System.out.println("We are passing the value 100, This returns us an n value of " + Five(100) + " , With a time complexity of " + complexityN3(Five(100), 100) + "This is to test the value of 100 in a n^3 test" );
// System.out.println("We are passing the value 1000, This returns us an n value of " + Five(1000) + " , With a time complexity of " + complexityN3(Five(1000), 1000) + "This is to test the value of 1000 in a n^3 test" );
// System.out.println("We are passing the value 10000, This returns us an n value of " + Five(10000) + " , With a time complexity of " + complexityN3(Five(10000), 10000) + "This is to test the value of 10000 in a n^3 test" );
//
//Test n^4 complexity
System.out.println("We are passing the value 5, This returns us an n value of " + Five(5) + " , With a time complexity of " + complexityN4(Five(5), 5) + " This is to test the value of 5 in a n^3 test" );
System.out.println("We are passing the value 10, This returns us an n value of " + Five(10) + " , With a time complexity of " + complexityN4(Five(10), 10) + "This is to test the value of 10 in a n^3 test" );
System.out.println("We are passing the value 100, This returns us an n value of " + Five(100) + " , With a time complexity of " + complexityN4(Five(100), 100) + "This is to test the value of 100 in a n^3 test" );
System.out.println("We are passing the value 1000, This returns us an n value of " + Five(1000) + " , With a time complexity of " + complexityN4(Five(1000), 1000) + "This is to test the value of 1000 in a n^3 test" );
System.out.println("We are passing the value 10000, This returns us an n value of " + Five(10000) + " , With a time complexity of " + complexityN4(Five(10000), 10000) + "This is to test the value of 10000 in a n^3 test" );
}
Here are the complexity Methods
public double complexityN2(double time, double n)
{
return time / (n * n);
}
public double complexityN3(double time, double n)
{
return time / (n * n * n);
}
public double complexityN4(double time, double n)
{
return time / (n * n * n * n);
}
public double complexityLog(double time, double n)
{
return time / (Math.log(n) * (n*n));
}

Keep in mind that big-O notation describes the behavior as the number of items approaches infinity. As such, you shouldn't expect to see an exact fit when dealing with almost any practical amount of computation. In fact, you don't necessarily see an exact fit under any circumstances -- it may approach a fit asymptotically, but even when (from a practical viewpoint) the number involved is really large, it's still not a very close fit. For as small of numbers as you're using for part of your test (e.g., 5, 10, 100) the fit will often be extremely poor even at best.
From a viewpoint of timing, most implementations of Java make life substantially more difficult as well. The problem is that most JVMs will interpret the first few (where "few" is rather loosely defined) iterations of some code, before they decide it's being executed often enough to be worth compiling to more optimized machine code. The numbers you're using are almost certainly small enough that in some cases you're timing interpreted code and in others compiled code (and somewhere in there, getting an execution that includes the time taken to compile the code as well). This has no real effect on the accuracy of big-O notation, but (especially for small number) can and will have a substantial effect on how closely your timing comes to fitting what big-O would predict.

The only question-mark in your question appears at the end of this sentence:
That means there is a compelxity that lies between the 2, and this is where log n comes in as log n is a value less than n?
That sentence is not a question, it is a statement: what are you asking here?
If you are asking what log(n) is, then it is the number p which, when 10 (denoted log10) or e (when talking about the natural logarithm) is raised to that power (i.e. 10p, ep) yields n. Hence it goes up very slowly as n increases (it is the exact opposite of an exponential increase in fact):
log10(10) is 1 (101 == 10)
log10(100) is 2 (102 == 100)
log10(1000) is 3 (103 == 1000)
Apologies if you already knew all this.

in this case n^3 isnt enough
That's not true. The outer loop in Five runs exactly n times. For each value of i, the inner loop runs exactly i² times, so the number of steps the outer loop does is the sum of i² while i runs from 0 to n-1, which is n/6 - n²/2 + n³/3 (simple to prove with induction). This is a polynomial of third degree, therefore it is O(n³).

I'm afraid you're not approaching the problem correctly: blindly testing against functions will only get you so far.
The O() notation is actually like saying, for a really big value of x, the function completes in time(aO(x)), where a is an arbitrary constant (can be 0.00001 as well as 6305789932).
Let's look at the code: the inner loop is executed i2 times, whereas (outer loop) is executed n times, with i from 0 to n.
Now, the inner operation (sum++) is executed Sumi=1,ni2,
which, by wikipedian wisdom becomes (*):
Then it's time to apply the O() notation. For a big n (say 10100), n3 overwhelms n2 and even more n1, so you just discard them: O(*) = O(n3), which is the solution to the excercise.
HTH

Try to understand it like this-
We need to find the number of times the loops will execute to find the time complexity.
The sum here also represents the same number, that is why you can use it in place of time in your complexity functions. This assumption is based on the assumption that each processing of a statement takes a constant time.
If we count the number of times the loops run-
for i = 0
the inner loop runs 0 times
for i = 1
the inner loop runs 1 time
for i = 2
the inner loop runs 4 times
for i = 3
the inner loop runs 9 times
so for i = m
the inner loop runs m*m times
So the total number of statements processed can be found as --
sum = 0 + 1 + 4 + 9 + .... + mm + ... +(n-1)(n-1)
sum = 1 + 4 + 9 + .... + mm + ... +(n-1)(n-1)
These are squares of natural numbers
sum of first N natural numbers can be found as - N(N+1)(2N+1) / 6
in our case N=n-1
so sum = (n-1)(n)(2n-1) / 6
sum = (n.n -n) (2n -1) /6
sum = (2n.n.n - 2n.n - n.n -n) /6
sum = (2n^3 -3n^2 -n) / 6
sum = 1/3n^3 - 1/2n^2 -1/6n
Now, the Big O will only consider the highest order of n..
So your complexity is of the order of n^3
Now your time complexity function for n^3 will take exactly this number and divide it by n^3
so your sum would be like 1/3 - 1/2n^-1 -1/6n^-2.
and for n^4 , an even smaller number, which will get even smaller as n increases which explains gradual reduction to 0.

Related

Time complexity : Getting incorrect result

The following code is from the book "Cracking the coding interview". The code prints all permutations of a string.
Question : What is the time complexity of the code below.
void permutation(String str) {
permutation(str, "");
}
private void permutation(String str, String prefix) {
if (str.length() == 0){
System.out.println(prefix);
} else {
for (int i = 0; i <str.length(); i++) {
String rem = str.substring(0, i) + str.substring(i + 1);
permutation(rem, prefix + str.charAt(i));
}
}
}
My Understanding:
I am able to derive the time complexity to : n * n!.
I am sure I am missing the time complexity of the blue, green and yellow nodes. But despite repeated attempts have not been clearly able to figure out the way out.
Can someone share some inputs, preferably with examples?
You're very much on the right track here, and your overall assessment (that the runtime is Θ(n · n!)) is correct! One technique we can use to derive the runtime would be to sum up, the work done at each layer in the tree. We know that
in layer 0 (the top layer), there's 1 node processing a string of length n,
in layer 1, there are n nodes each processing strings of length n - 1,
in layer 2, there are n(n-1) nodes each processing strings of length n - 2,
in layer 3, there are n(n-1)(n-2) nodes each processing strings of length n -3,
...
in layer n, there are n! nodes each processing strings of length 0.
To account for the total work done here, let's assume that each recursive call does O(1) work at baseline, plus work proportional to the length of the string that it's processing. This means that we need to work out two sums to determine the total work done:
Sum 1: Number of Calls
1 + n + n(n-1) + n(n-1)(n-2) + ... + n!
and
Sum 2: Work Processing Strings
1 · n + n · (n-1) + n(n-1)·(n-2) + ... + n! · 0
But there's one other factor to account for - each recursive call that hits a base case prints out the string produced this way, which takes O(n) time. So that adds in a final factor of Θ(n · n!). The total work done, therefore, will be Θ(n · n!), plus the work done by all the intermediate recursive calls building up the answers.
Let's treat each of those sums individually.
Sum 1: Number of Calls
We're dealing with this unusual sum:
1 + n + n(n-1) + n(n-1)(n-2) + ... + n!
The main observation we need is that
1 = n! / n!,
n = n! / (n-1)!,
n(n-1) = n! / (n-2)!
...
n! = n! / (n-n)!
So, in other words, this sum is
n! / n! + n! / (n-1)! + n! / (n-2)! + ... + n! / 0!
= n!(1 / n! + 1/(n-1)! + 1/(n-2)! + ... + 1/0!)
≤ en!
= Θ(n!)
Here, that last step follows from the fact that the sum
1/0! + 1/1! + 1/2! + 1/3! + ...
out to infinity is one of the ways of defining the number e. This means that the number of total recursive calls made here is Θ(n!).
And, intuitively, that should make sense. Each recursive call, except for recursive calls on strings of length one, makes two other recursive calls, so the recursion tree is mostly branching. And there's a nice fact about trees that says that a tree where every node is branching will not have more internal nodes than leaves. Since there are n! leaves, it's not surprising that the remaining number of nodes comes out to something that's not asymptotically bigger than n!.
Sum 2: Work Processing Strings
This is the sum
1 · n + n · (n-1) + n(n-1)·(n-2) + ... + n! · 0
We can rewrite this as
n + n(n-1) + n(n-1)(n-2) + ...
and hey! We know that sum - it's almost literally the same one we just saw. That works out to Θ(n!).
Putting It All Together
To summarize, this recursive algorithm does
Θ(n!) work simply due to the number of recursive calls, with each call taking some base amount of time;
Θ(n!) work done by the recursive calls as they form and concatenate substrings; and
Θ(n · n!) work done by the final recursive calls to print out all the permutations.
Summing this all up gives the Θ(n · n!) runtime that you proposed in your question.
Hope this helps!
The time complexity will be O(n!). Here is an analysis(copied from geeksforgeeks). It is also known as Heap's algorithm.
Complexity Analysis:
The algorithm generates (n-1)! permutations of the first n-1 elements, adjoining the last element to each of these. This will generate all of the permutations that end with the last element.
If n is odd, swap the first and last element and if n is even, then swap the ith element (i is the counter starting from 0) and the last element and repeat the above algorithm till i is less than n.
In each iteration, the algorithm will produce all the permutations that end with the current last element.

How can i find the time complexity of below function? [duplicate]

This question already has answers here:
How can I find the time complexity of an algorithm?
(10 answers)
Closed 3 years ago.
Can anyone guide me to find the time complexity? Does the time complexity change over operating systems?
int fn(int n){
if(n==1){
return 1;
}
else{
return(fn(n-1)+ fn(n-1));
}
}
You can make a recurrence relation T which represents the time it would take to compute and input of size N and then use the method of telescoping to help find the Big-O like so:
T(N) = T(N-1) + T(N-1) + c = 2*T(N-1) + c
Here, we can see that the time it will take to compute T(N) will be 2*T(N-1) plus a constant amount of time c. We can also see by your function that:
T(1) = b
Here, we can see by your base case that there are no recursive calls when N=1, so, it will take a constant time b to compute T(1).
If we take a look at T(N), we need to find out what T(N-1) is, so computing that we get:
T(N-1) = 2*T(N-2) + c
Computing T(N-2) we get:
T(N-2) = 2*T(N-3) + c
Thus, we can sub these back into each other...
T(N-1) = 2*(2*T(N-3) + c) + c = 4*T(N-3) + 3c
T(N) = 2*(4*T(N-3) + 3c) + c = 8*T(N-3) + 7c
By looking at the pattern produced by stepping down into our equation, we can generalize it in terms of k:
T(N) = 2^k * T(N-k) + ((2^k)-1 * c)
We know that our recursive calls will stop when T(N-k) = T(1), so we want to find when N-k = 1, which is when k = N-1, so subbing in our value of k and removing the constant-variable times, we can find our Big-O:
T(N) = (2^N) * T(1) + (2^N)-1 * c
= (2^N) * b + (2^N)-1*c
= O(2^N) (-1, b & c are constants, so they can be removed, giving 2*(2^N), where 2* is a constant, giving 2^N)
Time complexity is a measurement of how well an algorithm will scale in terms of input size, not necessarily a measure of how fast it will run. Thus, time-complexity is not dependent on your operating system.
The function is a recursive function (it calls itself). In that case, its time complexity is either linear or exponential (or something else, which we won't cover here):
It is linear, if you can do TCO (tail call optimization), or in other words, turn the function into a loop:
int loop(int i, int count) {
if(i > 10) return count;
return loop(i - 1, count + 1);
}
loop(0, 0);
// can be turned into
int count = 0;
for(int i = 0; i <= 10; i++) {
count = count + 1;
}
Otherwise, it is exponential, as every call will execute the function again m times, and each of those m calls, calls the function again m time and so on. This will happen until the depth n is reached, so the time complexity is:
O(m ^ n)
Now m is kind of a constant, as the number of recursive calls doesn't change (it's two in your case), however n can be changed. Therefore the function has exponential time complexity. That's generally bad, as exponential numbers get really large for relatively small datasets. In your case however, it is trivial to optimize. a + a is the same as a * 2, thus your code can be turned into:
int fn(int n){
if(n==1){
return 1;
} else {
return fn(n - 1) * 2;
}
}
And that is .... linear!
Does the time complexity change over operating systems?
No, the time complexity is an abstract concept, it doesn't depend on the computer, operating system, language,... You can even run it on an automaton.
when n=1, the time complexity is 1...
No! n is not a constant. n is part of the input, and time complexity is a way to estimate the time depending on the input.
Here is the approach I would take.
from the function definition we can deduce that
O(f(n)) = c + 2 * O(f(n-1))
If we ignore constant terms
O(f(n)) = 2 * (2 * f(n-2))
so we can say the complexity here is O(2^n)

Analyzing runtime of a short algorithm

I got the following code given:
public class alg
{
public static int hmm (int x)
{
if (x == 1)
{
return 2;
}
return 2*x + hmm(x-1);
}
public static void main (String[] args)
{
int x = Integer.parseInt(args[0]);
System.out.println(hmm(x));
}
}
So first question is, what does this algorithm count?
I have just typed and runned it in eclipse
so I can see better what it does (it was pseudocode before, I couldn't type it here so I typed the code). I have realized that this algorithm does following: It will take the input and multiply it by its following number.
So as examples:
input = 3, output = 12 because 3*4 = 12.
Or Input = 6, output 42 because 6*7 = 42.
Alright, the next question is my problem. I'm asked to analyze the runtime of this algorithm but I have no idea where to start.
I would say, at the beginning, when we define x, we have already got time = 1
The if loop gives time = 1 too I believe.
Last part, return 2x + alg(x-1) should give "something^x" or..?
So in the end we got something like "something^x" + 2, I doubt thats right : /
edit, managed to type pseudocode too :)
Input: Integer x with x > 1
if x = 1 then
return 2;
end if
return 2x + hmm(x-1);
When you have trouble, try to walk through the code with a (small) number.
What does this calculate?
Let's take hmm(3) as an example:
3 != 1, so we calculate 2 * 3 + hmm(3-1). Down a recursion level.
2 != 1, so we calculate 2 * 2 + hmm(2-1). Down a recursion level.
1 == 1, so we return 2. No more recursions, thus hmm(2-1) == hmm(1) == 2.
Back up one recursion level, we get 2 * 2 + hmm(1) = 2 * 2 + 2 = 4 + 2 = 6. Thus hmm(2) = 6
Another level back up, we get 2 * 3 + hmm(2) = 6 + 6 = 12
If you look closely, the algorithm calculates:
2*x + ... + 4 + 2
We can reverse this and factor out 2 and get
2 * (1 + 2 + ... + x).
Which is an arithmetic progression, for which we have a well-known formula (namely x² + x)
How long does it take?
The asymptotic running time is O(n).
There are no loops, so we only have to count the number of recursions. One might be tempted to count the individual steps of calculation, but those a are constant with every step, so we usually combine them into a constant factor k.
What does O(n) mean?
Well ... we make x - 1 recursion steps, decreasing x by 1 in every step until we reach x == 1. From x = n to x = 1 there are n - 1 such steps. We thus need k * (n - 1) operations.
If you think n to be very large, - 1 becomes negligible, so we drop it. We also drop the constant factor, because for large n, O(nk) and O(n) aren't that much different, either.
The function calculates
f(x) = 2(x + x-1 + x-2 + ... + 1)
it will run in O(x), i.e. x times will be called for constant time O(1).

how to exchange an amount of money into notes and coins

how can I exchange a given amount of money into notes and coins? lets say input is 1234,26
and we have notes for 1000, 500, 200, 100, 50, and coins for 20, 10, 1, and 0.5? so if input is greater than .25 and less than .75 it should be rounded to 1x 0.5 if its between .75 and 1.00 it should be rounded to 1x 1 and if its less than .25 it should be rounded to nothing?¨
for this exact program the desired output would look something like this:
1x: 1000
1x: 200
1x: 20
1x: 10
4x: 1
1x: 0.5
if it wasnt for the 0.5 coin, I think I would have been able to do it using int and %, but as of right now I am pretty much clueless(think I have to use array, but im not sure how) and have no idea how to start. also im beginnner, if you can keep that in mind as well when answering and explaining! any tips/solutions? thanks in advance!
like this?:
System.out.println((input/1000) + " thousand " + ((input/500)%2) + " fivehundred " + (input/200%2.5) + " two hundred " + (input/100%2) + " hundred " + (input/50%2) + " fifty " + (input/20%2.5) + " twenty " + (input/10%2) + " ten " + input/1%10 + " one " );
still not sure how to deal with the 0.5 since I have to use int, input only cuz if I use double I get it completely wrong, I also have to use a if statement for the 0.5 coin..
I believe this is the standard approach to this kind of question.
double input = 1234.26;
int thousands = input/1000;
input = input - 1000*thousands; //So now it would 234,26
int fivehundreds = input/500;
input = input - 500*fivehundreds;
etc...
Right, but you can't convert from double to int (i.e. thousands is an int, but input is a double, so input/1000 is a double). So you have a few options:
Make thousands, fivehundreds, etc... be double. That is kinda ugly, though, there's no way they will have any decimal valu
Casting mean anything to you? For example, (int)int thousands = input/1000; will work. You can read up on "casting", but basically I'm just telling Java to treat that number as an int, not a double
Keep input as a int, and round it down. Then just check at the end if it has a decimal value (input % 1 > 0), and if it does, you need a half dollar.

How can I estimate time complexity from a table of values?

I know that my naive matrix multiplication algorithm has a time complexity of O(N^3)...
But how can I prove that through my table of values? Size is the row or column length of the matrix. So square that for the full matrix size.
Size = 100 Mat. Mult. Elapsed Time: 0.0199 seconds.
Size = 200 Mat. Mult. Elapsed Time: 0.0443 seconds.
Size = 300 Mat. Mult. Elapsed Time: 0.0984 seconds.
Size = 400 Mat. Mult. Elapsed Time: 0.2704 seconds.
Size = 800 Mat. Mult. Elapsed Time: 6.393 seconds.
This is like looking at a table of values and estimating the graph of the function... There has to be some relationship between these numbers, and N^3. How do I make sense of it though?
I have provided my algorithm below. I already know it is O(N^3) by counting the loops. How can I relate that to my table of values above though?
/**
* This function multiplies two matrices and returns the product matrix.
*
* #param mat1
* The first multiplier matrix.
* #param mat2
* The second multiplicand matrix.
* #return The product matrix.
*/
private static double[][] MatMult(double[][] mat1, double[][] mat2) {
int m1RowLimit = mat1.length, m2ColumnLimit = mat2[0].length, innerLimit = mat1[0].length;
if ((mat1[0].length != mat2.length))
return null;
int m1Row = 0, m1Column = 0, m2Row = 0, m2Column = 0;
double[][] mat3 = new double[m1RowLimit][m2ColumnLimit];
while (m1Row < m1RowLimit) {
m2Column = 0;
while (m2Column < m2ColumnLimit) {
double value = 0;
m1Column = 0;
m2Row = 0;
while (m1Column < innerLimit) {
value += mat1[m1Row][m1Column] * mat2[m2Row][m2Column];
m1Column++;
m2Row++;
}
mat3[m1Row][m2Column] = value;
m2Column++;
}
m1Row++;
}
return mat3;
}
The methodology
Okay. So you want to prove your algorithm's time complexity is O(n^3). I understand why you would look at the time it takes for a program to run a calculation, but this data is not reliable. What we do, is we apply a weird form of limits to abstract away from the other aspects of an algorithm, and leave us with our metric.
The Metric
A metric is what we are going to use to measure your algorithm. It is the operation that occurs the most, or carries the most processing weight. In this case, it is this line:
value += mat1[m1Row][m1Column] * mat2[m2Row][m2Column];
Deriving the Recurrence Relation
The next step, as I understand it, is to derive a recurrence relation from your algorithm. That is, a description of how your algorithm functions based on it's functionality in the past. Let's look at how your program runs.
As you explained, you have looked at your three while loops, and determined the program is of order O(n^3). Unfortunately, this is not mathematical. This is just something that seems to happen a lot. First, let's look at some numerical examples.
When m1RowLimit = 4, m2ColumnLimit = 4, innerLimit = 4, our metric is ran 4 * 4 * 4 = 4^3 times.
When m1RowLimit = 5, m2ColumnLimit = 5, innerLimit = 5, our metric is ran 5 * 5 * 5 = 5^3 times.
So how do we express this in a recurrence relation? Well, using some basic maths we get:
T(n) = T(n-1) + 3(n-1)^2 + 3(n-1) + 1 for all n >= 1
T(1) = 1
Solving the Recurrence Relation using Forward Substitution and Mathematical Induction
Now, is where we use some forward substitution. What we first do, is get a feel for the relation (this also tests that it's accurate).
T(2) = T(1) + 3(1^2) + 3(1) + 1 = 1 + 3 + 3 + 1 = 8.
T(3) = T(2) + 3(2^2) + 3(2) + 1 = 8 + 12 + 6 + 1 = 27
T(4) = T(3) + 3(3^2) + 3(3) + 1 = 27 + 27 + 9 + 1 = 64
NOW, we assert the hypothesis that T(n) = n^3. Let's test it for the base case:
T(1) = 1^3 = 1. // Correct!
Now we test it, using mathematical induction, for the next step. The algorithm increases by 1 each time, so the next step is: T(n+1). So what do we need to prove? Well we need to prove that by increasing n by 1 on one side, the equal effect happens to n on the other. If it is true for all n + 1, then it is true for n + 1 + 1 and so on. This means, we're aiming to prove that:
T(n + 1) = (n + 1)^3
T(n + 1) = T(n - 1 + 1) + 3(n + 1 - 1)^2 + 3(n + 1 - 1) + 1
= T(n) + 3(n)^2 + 3(n) + 1
Assume T(n) = n^3
T(n + 1) = n^3 + 3(n)^2 + 3(n) + 1
T(n + 1) = (n+1)^3 // Factorize the function.
So at this point, you've proven your algorithm has a run time complexity of O(n^3).
Empirically, you can plot your data with an adjacent third-degree polynomial trend-line for reference.
CSV data:
100, 0.0199
200, 0.0443
300, 0.0984
400, 0.2704
800, 6.393
The first response covers how to prove the time complexity of your algorithm quite well.
However, you seem to be asking how to relate the experimental results of your benchmarks with time complexity, not how to prove time complexity.
So, how do we interpret the experimental data? Well, you could start by simply plotting the data (runtime on the y-axis, size on the x-axis). With enough data points, this could give you some hints about the behavior of your algorithm.
Since you already know the expected time complexity of your algorithm, you could then draw a "curve of best fit" (i.e. a line of the shape n^3 that best fits your data). If your data matches the line fairly well, then you were likely correct. If not, it's possible you made some mistake, or that your experimental results are not matching due to factors you are not accounting for.
To determine the equation for the best fitting n^3 line, you could simply take the calculated time complexity, express it as an equation, and guess values for the unknowns until you find an equation that fits. So for n^3, you'd have:
t = a*n^3 + b*n^2 + c*n + d
Find the values of a, b, c, and d that form an equation that best fits your data. If that fit still isn't good enough, then you have a problem.
For more rigorous techniques, you'd have to ask someone more well versed in statistics. I believe the value you'd want to calculate is the coefficient of determination (a.k.a. R^2, basically tells you the variance between the expected and actual results). However, on it's own this value doesn't prove a whole lot. This problem of validating hypothesized relationships between variables is known as Regression Model Validation; the wikipedia article provides a bit more information on how to go further with this if R^2 isn't enough for your purposes.

Categories

Resources