Algorithm...Assignment
Algorithm...Assignment
Name ID
Level 2:
Subproblems become \( n/4, n/8, n/16 \), etc. Each level’s cost is approximately (7/8) of the
previous level.
Depth:
The largest subproblem is T(n/2) , which reduces to T(1) when n/2^k = 1 , so k = log_2 n
.
The smallest subproblem T(n/8) reaches base case faster, at
Thus,
T(n) = O(n) .
- Assume
T(n) ≤ cn for some constant c
Then, substitute:
Require c*(⅞)n + n ≤ cn
Such that
n ≤ c *(⅛)n ⇒ c ≥ 8
Base case:
Assume T(1) = 1 . Then T(1) ≤ 8 *1 , which holds.
T(n) \geq dn \), and find \( d \leq 8 \), confirming \( T(n) = \Omega(n) \).
Final Answer ⇒ T(n) = Theta(n) , simply because big oh and omega are equal.
2. Modified COUNTING-SORT
Modified Loop:
Change line 10 from `for j = A.length downto 1` to `for j = 1 to A.length`.
Does It Work?
- Original Algorithm
Place elements in the output array B from right to left (high to low indices), ensuring
stability (equal elements maintain relative order).
Modified Algorithm
Place elements from left to right. For each A[j] , place it at B[C[A[j]]] , then decrement \
( C[A[j]] .
-Correctness:
The array C[i] contains the number of elements ≤ i . Placing A[j] at B[C[A[j]]]
and decrementing C[A[j]] ensures each element is placed in its correct position.
The algorithm still sorts correctly because each element is placed based on the
count of elements ≤
it.
Is It Stable?
- Stability:
A sorting algorithm is stable if equal elements maintain their relative order.
- Example: Input A = [2, 2', 1] , where 2 and 2' are distinct but equal. Let k = 2 .
Initialize
C[0..2] = [0, 0, 0] .
- Count: C = [0, 1, 2] (1 element = 1, 2 elements = 2).
- Cumulative: \( C = [0, 1, 3] \).
- Conclusion: The modified algorithm sorts correctly but is not stable, as equal elements are
placed in reverse order of their appearance.
Answer
The modified COUNTING-SORT works but is not stable.
Example: Input [2, 2', 1] yields [1, 2', 2] , reversing the order of equal elements.
Case 3: If f(n) = Omega(n^(log_b a + epsilon)) and regularity condition holds, then T(n) =
Theta f(n).
A.
-a = 5 , b = 2 , \( f(n) = n^(2) lg n .
- Compare: f(n) = n^2 (lg n ). Since n^2 lg n = O(n^(2.322 - epsilon) for epsilon approx
0.322 , Case 1 applies.
- Answer
Answer
T(n) = Theta(n) .
a = 6 , b = 3 , f(n) =( n^2)* lg n
Pseudocode
BUBBLESORT(A)
for i = 1 to A.length - 1
for j = A.length downto i + 1
if A[j] < A[j-1]
exchange A[j] with A[j-1]
(a) In order to show that BUBBLESORT actually sort, We need to prove that,
After BUBBLESORT terminates, the array A is sorted (i.e., A[1] ≤ A[2] ≤A[3].... ≤
A[n] ).
Loop Invariant
After the i -th iteration of the outer loop, the last i elements ( A[n-i+1..n] ) are sorted and
contain the i largest elements.
Initialization
Before the first iteration, i = 1 , the invariant holds trivially (no elements fixed).
-Maintenance
In the inner loop, if A[j] < A[j-1] , swap them, ensuring the smaller element moves left. After
the inner loop, the i -th largest element is at position n-i+1
Termination
When i = n , the entire array is sorted.
- Stability: Show that equal elements maintain relative order (Bubble Sort is stable due to
adjacent swaps).
- Bubble Sort:
- Insertion Sort:
- For each element, shift larger elements right to insert it in the correct position.
- Worst case: Reverse sorted array, (n(n+1))/2 = Theta(n^2) \) comparisons and shifts.
- Running Time: Theta(n^2) .
- Comparison:
- Both have Theta(n^2) worst-case running time.
- Insertion Sort is typically faster in practice due to fewer swaps (shifts are cheaper than
swaps in Bubble Sort).
- Insertion Sort performs better on partially sorted arrays ( O(n + d) , where d is the number
of inversions).
Answer
(a) Prove the array is sorted using a loop invariant: after i-th iteration, the last i elements
are sorted and contain the i largest elements.
(b) Worst-case running time is Theta(n^2) same as Insertion Sort, but Insertion Sort is often
faster due to fewer swaps and better performance on nearly sorted arrays.
Find common elements in two sorted lists, e.g., < 2,5,5,5 > and < 2,2,3,5,5,7 > where the
output should be < 2,5,5 >.
(a) Pseudocod
COMMON-ELEMENTS(A, B)
m = A.length, n = B.length
C = new empty list
i = 1, j = 1
while i ≤ m and j ≤ n
if A[i] = B[j]
append A[i] to C
i=i+1
j=j+1
else if A[i] < B[j]
i=i+1
else
j=j+1
return C
-Explanation:
Use two pointers i and j Compare A[i] and B[j] :
- If equal, add to the result and advance both.
- If A[i] < B[j] , advance i .
- If A[i] > B[j] , advance j .
Proof
Since lists are sorted, equal elements are found in order, and duplicates are handled
correctly.
Programming Questions
Purpose
The purpose of this report is to detail the design, implementation, and evaluation of
five fundamental sorting algorithms—Insertion Sort, Merge Sort, Heap Sort, Quick
Sort, and Selection Sort—using the Java programming language. This study aims to
analyze the computational efficiency and performance characteristics of each
algorithm under varying conditions of array size and initial order (presortedness). By
implementing and testing these algorithms, this report seeks to provide a
comparative analysis of their operational dynamics and to verify theoretical
computational complexities with empirical data. The ultimate goal is to enhance
understanding of algorithmic design and optimization techniques within the context
of sorting functions.
Scope
This report encompasses a comprehensive analysis of five sorting algorithms:
Insertion Sort, Merge Sort, Heap Sort, Quick Sort, and Selection Sort. Each algorithm
has been implemented in Java to assess its performance across a range of
conditions, including varying array sizes and degrees of presortedness. The scope
includes:
● Implementation Details: Each sorting algorithm is implemented as a separate
method within a single Java class named Sort, ensuring consistency in the
coding environment and execution.
● Testing Parameters: Algorithms are tested with array sizes that progressively
increase from 1,000 to 2,000,000 elements, divided into intervals, to explore
scalability and efficiency under larger data sets. Additionally, each array is
tested under three different states of presortedness (0, 0.5, and 1),
representing reversed, randomly ordered, and already sorted data
respectively.
● Performance Metrics: The primary focus is on measuring the execution time
and efficiency of each algorithm, with special attention to how well the
empirical results match theoretical expectations of algorithmic complexity.
● Environmental Considerations: Tests are conducted under controlled
conditions to minimize external impacts on performance metrics, such as
other running processes or system load variations.
Methodology
Implementation
The implementation of the sorting algorithms was carried out using Java, a choice
influenced by its robust standard libraries and strong memory management
capabilities, which are ideal for handling large datasets efficiently. Below is a
summary of the implementation details for each algorithm:
The code was structured to ensure that all sorting methods were tested under
identical conditions, using the same input arrays for each test run to maintain
fairness and consistency across trials.
Testing Framework
The testing framework was designed to evaluate the performance of each sorting
algorithm under a range of controlled conditions, focusing on execution time and
algorithmic efficiency across various data scales and presortedness levels. The key
components of the testing framework included:
● Array Sizes and Scaling: Tests were conducted using arrays of different sizes
to understand how each sorting algorithm scales with data volume. The sizes
ranged from 1,000 to 2,000,000 elements, increasing in intervals of 100,000
elements. This gradual scaling allowed for a detailed analysis of each
algorithm’s performance at different data capacities.
● Presortedness Levels: Each algorithm was tested against arrays with three
distinct levels of presortedness to assess adaptability to different data
conditions:
● 0 (Reversed Order): Completely reversed arrays to test the worst-case
scenario for some algorithms.
● 0.5 (Random Order): Arrays randomly shuffled to represent a typical,
average-case scenario.
● 1 (Already Sorted): Fully sorted arrays to test the best-case scenario,
particularly beneficial for algorithms like Insertion Sort.
● Repetitions: To mitigate the effects of random anomalies and external
system factors, each test configuration (specific array size and presortedness
level) was repeated multiple times. The REP constant was set to 10, meaning
each specific test case was run 10 times to ensure the data’s statistical
significance and reliability.
● Performance Measurement: Execution times were accurately measured using
System.nanoTime(), chosen for its precision in timing short durations. The
start time was recorded just before the sorting process began, and the end
time immediately after it completed, ensuring that only the sorting process
itself was measured.
● Environment Consistency: Tests were performed on a single machine with
minimal background processes to reduce interference from external factors.
This setup provided a consistent baseline for all tests, ensuring that
differences in performance were attributable solely to the algorithms’
efficiencies and not to varying system conditions.
● Data Recording and Analysis: The results were systematically recorded,
including execution times and the conditions under which each test was
conducted. This data was later analyzed to draw comparisons and
conclusions, presented in the form of graphs and tables for clear visualization
of performance trends.
Results
Performance Analysis
The performance of each sorting algorithm was evaluated based on execution time
across varying array sizes and degrees of presortedness. The results are
summarized in the tables and illustrated in the graphs below:
Comparison
The comparison between algorithms revealed several interesting findings:
Discussion
Efficiency Considerations
Reflecting on the implementations, several optimizations and trade-offs were
considered:
Conclusion
The comprehensive testing and analysis of five fundamental sorting algorithms—
Insertion Sort, Merge Sort, Heap Sort, Quick Sort, and Selection Sort—have yielded
valuable insights into their performance under various conditions. Key findings from
this study include:
Conclusions Drawn:
● Choice of Algorithm: The choice of sorting algorithm should be driven by the
specific requirements of the application, including data size, presortedness,
and the need for stability in execution time.
● Algorithm Optimization: There is potential for optimizing each algorithm to
enhance its performance, especially Quick Sort’s pivot selection mechanism
and Merge Sort’s memory usage.
● Practical Implications: These results not only validate theoretical concepts
but also provide practical insights that can guide the selection and
implementation of sorting algorithms in real-world applications.