an algorithm that solves the problem of finding the sequence
an algorithm that solves the problem of finding the sequence
Consider the following algorithms which are an algorithm that solves the problem of finding
the sequence that corresponds to the maximum contagious subsequence sum given a set of
(possibly negative) integers.
void maxSubsequenceSum( int arr[ ], void maxSubsequenceSum( int arr[ void maxSubsequenceSum( int arr[],
long n){ ], long n){ long n){
long maxSum = 0; long maxSum = 0; long thisSum = 0;
for( long i = 0; i < n; i++ ){ for( long i = 0; i < n; i++ ){ long maxSum = 0;
for( long j = i; j < n; j++ ){ long thisSum = 0; for( long i = 0, j = 0; j < n; j++ ){
long thisSum = 0; for( long j = i; j < n; j++ ){ thisSum += a [ j ] ;
for( long k= i; k<= j; k++) thisSum += a[ j ]; if( thisSum > maxSum ) {
thisSum += a[ k ]; if( thisSum > maxSum ){ maxSum = thisSum;
if( thisSum > maxSum ){ maxSum = thisSum;
maxSum = thisSum; } }else if ( thisSum < 0 ){
} thisSum = 0;
} } }
} } }
} }
}
a. Write small program that is going to be used in the determination of the clock time
(empirical approach) for each of the algorithms for the randomly generated sequence
of integers of size n. Make one forth of the sequence negative.
Answer
import java.util.Random;
import java.util.Scanner;
1
long startTime = System.nanoTime();
maxSubsequenceSum3(arr, n);
long endTime = System.nanoTime();
System.out.println("Time for Algorithm with 3 nested loops: " + (endTime -
startTime) + " ns");
2
int j = rand.nextInt(i + 1);
// Swap arr[i] with the element at random index
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
3
public static void maxSubsequenceSum1(int arr[], long n) {
long thisSum = 0;
long maxSum = 0;
for (long j = 0; j < n; j++) {
thisSum += arr[(int) j];
if (thisSum > maxSum) {
maxSum = thisSum;
} else if (thisSum < 0) {
thisSum = 0;
}
}
}
}
b. Run the program written in (a) for the first algo and for n=5000 jot down the result
and compare it with your friend result.
Answer
Enter the size of the array (n): 5000
Time for Algorithm with 3 nested loops: 31057162900 ns
Time for Algorithm with 2 nested loops: 25954700 ns
Time for Algorithm with 1 nested loop: 443800 ns
c. Have you got the same result? If not, what is the possible explanation?
Answer
The results are not the same. Here are possible explanations;
1. Algorithm Complexity
• Time Complexity: Each algorithm has a different time complexity:
o The algorithm with 3 nested loops has a time complexity of O(n³), which means it
grows cubically with the input size. This will take significantly longer.
o The algorithm with 2 nested loops has a time complexity of O(n²), which is faster
than the cubic version but still relatively slow for larger n.
o The algorithm with 1 nested loop has a time complexity of O(n), making it the
fastest among the three.
• This fundamental difference in complexity means you should expect the execution time
to vary greatly between them.
2. Random Array Generation
• The values and distribution of the random array can impact how quickly the algorithms
find their results, particularly for the nested loop algorithms. If the array has many
4
negative values in specific configurations, it might lead to longer execution times for the
nested loops.
3. Memory Access Patterns
• Algorithms with multiple nested loops may have different memory access patterns, which
can affect cache performance. In cases where data is not accessed sequentially, cache
misses can slow down execution significantly.
4. JVM Optimization
• The Java Virtual Machine (JVM) optimizes code execution differently. The Just-In-Time
(JIT) compiler can optimize some parts of the code more effectively than others, leading
to variations in execution time based on which algorithm is being executed.
5. Garbage Collection
• Java’s garbage collection can introduce pauses, especially if one of the algorithms uses
more memory or creates more temporary objects. This can lead to variability in execution
times.
6. System Load
• If the system is under different loads (e.g., other applications running in the background),
it can affect the execution time of the algorithms. Running the test at different times can
yield different results.
7. Input Method and Timing
• The way user input is handled and any potential delays or issues in input processing can
affect the overall timing reported by the program.
d. Run the program again 2 times for the same algo and compare the results
Answer
Enter the size of the array (n): 5000
Time for Algorithm with 3 nested loops: 31153838100 ns
Time for Algorithm with 2 nested loops: 26826700 ns
Time for Algorithm with 1 nested loop: 327900 ns
Comparison
1st time 2nd time Difference (1st-2nd)
Time for Algorithm 31057162900 ns 31153838100 ns -96675200 ns
with 3 nested loops
Time for Algorithm 25954700 ns 26826700 ns -872000 ns
with 2 nested loops
Time for Algorithm 443800 ns 327900 ns 115900
with 1 nested loop
e. Have you got the same result? If not, what is the possible explanation?
5
Ans
The results of running twice the code are not the same but running the program twice,
help to assess the stability of the performance for each algorithm. the discrepancies
observed can be due to system load or random input variations.
f. Run the program written in (a) for the three algos for different values of n as shown
in the table below jotting down the result for each algos
n Clocktime(Algo1) Clocktime(Algo2) Clocktime(Algo3)
100 0.888 2.48 0.799
500 1.196 1.781 1.237
900 1.588 1.79 0.929
1300 2.965 1.772 1.059
1700
2000
3000
4000 53.824
6000 181.542
10000 820.208 2.006 1.153
Ans
n Clocktime(Algo1) Clocktime(Algo2) Clocktime(Algo3)
100 0.888 2.48 0.799
500 1.196 1.781 1.237
900 1.588 1.79 0.929
1300 2.965 1.772 1.059
1700 1.250 0.0885 0.00132
2000 1.998 0.0115 0.00321
3000 6.699 0.141 0.00401
4000 53.824 0.191 0.00450
6000 181.542 0.326 0.398
10000 820.208 2.006 1.153
g. What do you say about the relationship between the clock time and the data size n
for a particular algo?
Ans
as n increases, the clock time for each algorithm also rises, but the rate of increase
differs:
• Algo 1 (3 nested loop): Shows the most dramatic increase in time, confirming
its cubic nature.
• Algo 2 (2 nested loops): Displays a more significant increase in time, especially
for larger n, reflecting its quadratic complexity.
6
• Algo 3 (1 nested loops): Likely shows the least increase in time, exhibiting
more linear behavior.
h. Comparing the result found for the three algorithms, what can you say about the clock
time relationship between the different algorithms? Which is the best algorithm from
the perspective of time efficiency?
Ans
Performance Analysis
• Algo1 (3 Nested Loops): This algorithm generally exhibits the longest execution
time across varying sizes of n. With cubic time complexity (O(n3), it becomes
impractical for larger datasets due to its significant increase in execution time.
• Algo 2 (2 Nested Loops): This algorithm shows a moderate increase in
execution time, with quadratic time complexity (O(n2). While it performs
reasonably well for smaller values of n, its efficiency drops considerably for
larger datasets compared to Algo 1.
• Algo 3 (1 Nested Loop): This algorithm exhibits the shortest execution time
across the tests. With linear time complexity (O(n), it efficiently handles larger
datasets without a dramatic increase in execution time.
• The clock times for these algorithms reflect their time complexities:
Cubic vs. Quadratic vs. Linear: As seen in the data, as n increases, the clock time for
Algo 3 increases the least, while Algo 1's clock time increases the most rapidly,
confirming the expected relationship based on their respective complexities.
7
1. Time Complexity:
Algo 1 (3 Nested Loops): This algorithm has a cubic time complexity (O(n3). This
means that the time taken grows proportionally to the cube of the input size. As n
increases, the number of operations increases dramatically, leading to significantly
longer execution times.
Algo 2 (2 Nested Loops): With a quadratic time complexity (O(n2), this algorithm
performs better than Algorithm 1 for the same n, as its growth rate is slower.
However, the increase in execution time is still noticeable as n rises, especially
compared to more efficient algorithms.
Algorithm 3 (1 Nested Loop): This algorithm has a linear time complexity (O(n),
leading to the fastest execution times. The number of operations increases linearly
with n, making it much more efficient than the other two algorithms.
2. Number of Operations:
Although Big O notation describes the growth rate, actual clock times can also be
affected by constant factors and overhead associated with each algorithm. For
example, the overhead of managing nested loops, maintaining variable states, and
performing summations can add to the execution time.
5. Input Characteristics:
8
The nature of the input data can also influence performance. For example, if the
input array has many negative numbers, it may affect the calculations and lead to
different performance characteristics.
In general, the differences in clock time among the algorithms for the same value of
n can be attributed to their respective time complexities, the number of operations
performed, the overhead involved, data access patterns, and the characteristics of the
input data. Understanding these factors is crucial for selecting the most efficient
algorithm for a given problem, especially as the size of the input grows.
ii. In conclusion, what do you say about the empirical approach of algorithm analysis?
Ans
The empirical approach to algorithm analysis is a vital complement to theoretical models,
providing practical insights into algorithm performance. Here are key points to consider:
• Practical Performance Insights:
Empirical analysis enables the observation of how algorithms perform in real-
world scenarios. For example, while an algorithm may have a theoretical
complexity of O(n3), empirical tests may reveal that its actual execution time is
significantly impacted by constant factors and input characteristics.
• Validation of Theoretical Models:
Empirical results can confirm or contradict theoretical predictions. A linear
algorithm (O(n) might outperform a quadratic one (O(n2) for small inputs, but as
n grows, the impact of time complexity becomes more pronounced, highlighting
the importance of both theoretical and empirical insights.
• Comparison of Algorithms:
This approach allows for direct comparisons of algorithms under controlled
conditions. For instance, comparing a quadratic algorithm to a linear one can
reveal that despite both having polynomial time complexities, the linear algorithm
will consistently outperform the quadratic one as n increases, especially when the
number of operations in the quadratic algorithm rises sharply.
• Adaptation to Real-World Conditions:
Algorithms may behave differently based on various environmental factors, such
as hardware and memory architecture. Empirical analysis captures these
variations, illustrating how an algorithm with a higher theoretical complexity
might still perform better due to lower constant factors or more efficient
operations.
• Benchmarking and Performance Tuning:
Empirical analysis assists in benchmarking algorithms, providing concrete metrics
on execution time and the number of operations. For instance, an algorithm with
O(n2) complexity may still be viable for smaller datasets, but performance tuning
9
can optimize its implementation to reduce constant factors and improve practical
efficiency.
• Limitations:
While empirical analysis provides valuable insights, it is sensitive to specific test
cases. Results may not generalize across all scenarios, and conclusions drawn from
limited experiments may misrepresent an algorithm's true performance.
Additionally, careful design is necessary to avoid bias in the results.
10