0% found this document useful (0 votes)
3 views

Programming Report-1

This report analyzes a Java algorithm that counts distinct triplets in an array that sum to zero, focusing on its time and space complexity. The algorithm has a time complexity of O(n^3) and requires approximately 4N bytes of memory for execution. The empirical analysis confirms the theoretical findings, with the execution time growing cubically with input size.

Uploaded by

misscharchar247
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Programming Report-1

This report analyzes a Java algorithm that counts distinct triplets in an array that sum to zero, focusing on its time and space complexity. The algorithm has a time complexity of O(n^3) and requires approximately 4N bytes of memory for execution. The empirical analysis confirms the theoretical findings, with the execution time growing cubically with input size.

Uploaded by

misscharchar247
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

University of Ghardaia

Department of Mathematics and Computer Science


24 Dec 2024

ASD3
Programming Report of Mini-Project 3

Introduction:
In this mini-project that has been presented to us, we are concerned with making the study and analysis of a
computer algorithm’s time and space complexity by applying both mathematical and algorithmic approaches,
the former taking the form of a computer program written in the Java programming language.

To realize this tangible result, we have gone with a paradigm similar in merit to the standard scientific
method, we have also chosen for the analysis to be strictly mathematical as these types of problems tend to
be rooted in numerical bases:

We also had to take into consideration some obstacles that may arise such as:
-The inherent limitations of the computational machine that we are using.
-The syntactic and functional restrictions imposed by the Java programming language.

Problematic:
The algorithm to be analysed can be defined as follows:

“Given a non-empty array of size N of random


integers, quantify the number of distinct
triplets the sum of which adds up to 0”

The code for said algorithm being:

for (int i = 0; i < N-2 ; i++) {


for(int j = i+1; j < N-1; j++) {
for(int k = j+1; k < N; k++) {
if(a[i]+a[j]+a[k] == 0) count++;
}
}
}

We are asked to study the time and space complexity of this algorithm both theoretically and empirically,
that is, the rate of growth of the time taken by this algorithm in terms of its input size, as well as the amount
of space it will take, our data will take the following form:

Input: A non-empty array of size N.


Output: the number of triplets that add up to 0.
Theoretical Analysis:
To analyse this problem we must first pick the primary operations that we are going count in this algorithm
as “significant” operations worth our time, our options being:

1) The creation of the random array.


2) The increment of the i, j, and k counting variables.
3) The conditionals for the i, j, and k loops.
4) The innermost conditional for the triplets that checks the negation of sum.

Out of these 4 proposals we are going to pick the 4th one, that is, the instruction in the innermost loop,
because it has the highest order due to it being in a nested loop, we will consider all other operations
negligible.

We will analyse these loops from the inside out.

First Loop:
for(int k = j+1; k < N; k++) {
if(a[i]+a[j]+a[k] == 0) count++;
}

This loop will execute the significant operation from j+1 until N-1 and each time will increment k by 1,
We can translate this into a mathematical sum using sigma notation, we have then.

𝑁−1
∑ 1
𝑘 = 𝑗+1

We put one because for each iteration of the loop the operation gets executed only once, we will henceforth
refer to this loop as L1.

Second Loop:
for(int j = i+1; j < N-1; j++) {
L1();
}

This loop will execute the first loop from i+1 until N-2 and increment j by 1 each iteration and thus we can
symbolize it as follows.

𝑁−2
∑ 𝐿1(𝑗)
𝑗 = 𝑖+1
Which then becomes:

𝑁−2 𝑁−1
∑ ( ∑ 1)
𝑗 = 𝑖+1 𝑘 = 𝑗+1
We will henceforth refer to this statement as L2.

Third Loop:

for (int i = 0; i < N-2 ; i++) {


L2();
}

As before, this can be represented as a sum:


𝑁−3
∑ 𝐿2(𝑖)
𝑖=0
Which then becomes, and this is the full form of our temporal function:

𝑁−3 𝑁−2 𝑁−1


𝑇(𝑁) = ∑ ( ∑ ( ∑ 1) )
𝑖=0 𝑗 = 𝑖+1 𝑘 = 𝑗+1

If we work out the math it becomes:

𝑁−3 𝑁−2
𝑇(𝑁) = ∑ ( ∑ ( 𝑁 − 1 − 𝑗) )
𝑖=0 𝑗 = 𝑖+1
Which further simplifies to:
𝑁−3
(𝑁−𝑖−2)(𝑁−𝑖−1)
𝑇(𝑁) = ∑ 2
𝑖=0
Solving this final sum gives us:

2 (𝑁−3)(𝑁−2) (𝑁−3)(𝑁−2)(2𝑁−5)​
​(𝑁−2)𝑁 −𝑁( 2
​+2(𝑁−2))+ 6
+(𝑁−3)(𝑁−2)−2(𝑁−2)
𝑇(𝑁) = 2

And finally we have:


12 3 3 2 1 5
𝑇(𝑁) = 5
𝑁 − 2
​𝑁 + 12
​𝑁 + 2

This is the expression for our time function, meaning if we assume our significant operation takes 1 time
unit, we can plug in the size of our input into the function above and the latter will let us estimate how long it
would take to execute it.

In big O notation we would say that this function belongs to the O(n3) set, and we write:

3
𝑇(𝑁) ⊂ 𝑂(𝑁 )
Space complexity:
Everytime we execute our algorithm with an arbitrary input size N, we would need to first keep track of N
different integers using an array of size N, since an array is essentially just a contiguous collection of data,
and given that an integer is allocated with 4 bytes of memory by the java virtual machine, it is safe to assume
that an execution of our algorithm with an input size N would require 4N bytes of memory as well as another
24 more bytes as array overhead and we would write:

𝑆(𝑁) = 4𝑁 + 24 𝑏𝑦𝑡𝑒𝑠
In tilde notation this can be reduced to:

𝑆(𝑁) = ~4𝑁 𝑏𝑦𝑡𝑒𝑠

Empirical Analysis:
In order to conduct our empirical analysis, we are going to track how much “actual” time our algorithm took,
if we take a look at the code:

import java.util.Random;
public class Main{
public static void main(String[] args) {
Random rd = new Random();

This is just boilerplate for the main class and main method, as well as instantiating the Random object.

for (int N = 500; N <= 20000; N = N+500) {

Here, we are going to test the algorithm for many different input sizes starting from 500 and incrementing
them by 500 each new iteration, this loop is not going to factor in our time complexity because it is not part of
the main algorithm.

// create an array of size N


int[] a = new int[N];

// fill the array with random numbers from -100 to 100


for (int i = 0; i < N; i++) {
a[i] = rd.nextInt(201) - 100;
}
//initialize count variable
int count = 0;

For each iteration of the outermost loop, or rather each “test” of the algorithm, we will have to:

1. Define an empty array of size N.


2. Fill this array with random integers.
3. Initialize our counter variable.
The next part of the code is the most worthy of attention:

// get the start time


long start = System.nanoTime();

// find the number of unique triplets that sum to 0


for (int i = 0; i < N-2 ; i++) {
for(int j = i+1; j < N-1; j++) {
for(int k = j+1; k < N; k++) {
if(a[i]+a[j]+a[k] == 0) count++;
}
}
}
// get the end time
long end = System.nanoTime();

What we’re doing here is we’re getting the nano time before and after running our algorithm. In computer
science, the nanotime is the number of milliseconds that have passed since epoch ( january 1st 1970 ).

// calculate the duration


long duration = (end - start)/1000000;
System.out.println("Number of triplets that sum to 0 in an
array of size " + N + " is " + count +
".Duration:"+ duration +"milliseconds");

After that we just have to calculate the difference between the starting and ending nanotimes, and that will be
the duration of our algorithm.

If we track those durations and plot them in function of their input size we get the following graph:
3
Observing this graph we notice that it is very similar to the graph of the function 𝑓(𝑥) = 𝑥 , that is because it
3
belongs to the set 𝑂(𝑛 ), just like we deduced in our theoretical analysis.

3
𝑎 𝑔𝑟𝑎𝑝ℎ 𝑜𝑓 𝑡ℎ𝑒 𝑓𝑢𝑛𝑐𝑡𝑖𝑜𝑛 𝑦 = 𝑥

Conclusion:
In this mini project, we were concerned with the analysis of a computer algorithm that finds the number of
all triplets in an array where the sum of which equals 0.

After analysing this problem both theoretically and empirically, we have come to the conclusion that it has a
3
time complexity of 𝑂(𝑛 ), that is to say that the function of the duration that this algorithm takes in terms of
its input size follows a cubic growth rate, we have also concluded that the former takes up ~4N bytes of
space, meaning this algorithm will take roughly four times the input size in bytes for each execution.

While this analysis was rather thorough and meticulous, there are always measures to improve and optimize
this methodology, such as implementing a built-in GUI in the java program that automatically displays the
duration of each cycle rather than having to insert them all manually.

You might also like