DEPARTMENT OF COMPUTER SCIENCE CPT414 ASSIGNMENT GROUP ASSIGNMENT Submitted by: s/n 1 Name Matric Number Questions 1. Determine Empirically the efficiency of the following I. Bubble sort vs Merge sort II. Selection sort vs Quick sort 2. Define the following complexity of problems I. Tractable problems II. Intractable problems III. p problems IV. NP problems V. Np-complete problems VI. NP-hard problems and give 5 examples of each. 1.I. Bubble sort Vs Merge sort Implementation and Working principle of Bubble sort function bubbleSort(array) { let swapped = true; let i = 0; while (swapped) { swapped = false; i++; for (let j = 0; j < array.length - i; j++) { if (array[j] > array[j + 1]) { [array[j], array[j + 1]] = [array[j + 1], array[j]]; swapped = true; } } } return array; } This code works by repeatedly iterating through the array, comparing adjacent elements. If the current element is greater than the next element, the two elements are swapped. This process is repeated until no more swaps are made, which means that the array is sorted. Here is an explanation of how the code works: The bubbleSort() function takes an array as input. The while loop iterates through the array, starting at index 0. The for loop iterates through the array, starting at index 0 and ending at index array.length - i. The if statement checks if the current element is greater than the next element. If it is, the two elements are swapped. The swapped variable is used to track if any swaps have been made in the current iteration. If no swaps have been made, the while loop will terminate. The return statement returns the sorted array. Implementation and Working principle of Merge sort function mergeSort(array) { if (array.length === 1) { return array; } const middle = array.length / 2; const leftArray = mergeSort(array.slice(0, middle)); const rightArray = mergeSort(array.slice(middle)); return merge(leftArray, rightArray); } function merge(leftArray, rightArray) { const mergedArray = []; let i = 0; let j = 0; while (i < leftArray.length && j < rightArray.length) { if (leftArray[i] < rightArray[j]) { mergedArray.push(leftArray[i]); i++; } else { mergedArray.push(rightArray[j]); j++; } } mergedArray.push(...leftArray.slice(i)); mergedArray.push(...rightArray.slice(j)); return mergedArray; } This code works by recursively dividing the array in half, sorting each half, and then merging the sorted halves back together. The merge() function takes two sorted arrays as input and merges them into a single sorted array. Here is an explanation of how the code works: The mergeSort() function takes an array as input. If the array is only one element long, the function returns the array. Otherwise, the function recursively calls itself on the left and right halves of the array. The merge() function takes two sorted arrays as input and merges them into a single sorted array. The function iterates through the two arrays, comparing the elements at each index. The smaller element is added to the merged array. The process is repeated until all elements from both arrays have been added to the merged array. The merged array is returned. Comparing Bubble sort and Merge sort Sample Input array sizes for both algorithms: Array0 length 10 Array1 length 15 Array2 length 23 Array3 length 34 Array4 length 51 Array5 length 76 Array6 length 114 Array7 length 171 Array8 length 257 Array9 length 385 Array10 length 577 Array11 length 865 Array12 length 1298 Array13 length 1947 Array14 length 2920 Array15 length 4379 Array16 length 6569 Array17 length 9853 Array18 length 14779 Array19 length 22169 Tabular Representation Of running times of Bubble sort VS Merge sort Array Size 10 15 23 34 51 76 114 171 257 Bubble Sort 0.1 0.0999 0.1999 0.3000 0.5 1.6999 2 12.1000 0.4000 Merge Sort 0.1 0 0.1 0 0.1 0.3 0.4 0.5999 0.6 Array Size 385 577 865 1298 1947 2920 4379 6569 9853 14779 22169 Bubble Sort 1.2000 1.3999 4.3999 4.1000 18.5 28.8 64.2999 124.8999 267.0999 592.3999 1329.7000 Merge Sort 0.8 8.1 1.1 12 1.3999 2.6999 4 9.0999 4.2999 11.6999 12.8999 Graphical Representation Of running times of Bubble sort VS Merge sort Here we can see that for small input sizes both algorithms perform comparatively similar, However for large input sizes, Merge sort is much more efficient than bubble sort and has lower running time. 1.II. Selection sort Vs quick sort Implementation and Working principle of Selection sort function selectionSort(array) { for (let i = 0; i < array.length; i++) { // Initialize minIndex as the current index let minIndex = i; // Loop through the rest of the array to find the smallest element for (let j = i + 1; j < array.length; j++) { if (array[j] < array[minIndex]) { minIndex = j; } } // Swap the current element with the smallest element [array[i], array[minIndex]] = [array[minIndex], array[i]]; } return array; } This code works by repeatedly finding the smallest element in the unsorted array and swapping it with the element at index i. This process is repeated until the entire array is sorted. Here is an explanation of how the code works: The selectionSort() function takes an array as input. The for loop iterates through the array, starting at index 0. The minIndex variable is initialized to the current index. The for loop iterates through the array, starting at index i + 1. The if statement checks if the element at index j is smaller than the element at index minIndex. If it is, the minIndex variable is updated to store the index of the element at index j. The array[i] and array[minIndex] variables are swapped. The return statement returns the sorted array. Implementation and Working principle of Quick sort function quicksort(arr) { if (arr.length <= 1) { return arr; } const pivot = arr[0]; const left = []; const right = []; for (let i = 1; i < arr.length; i++) { if (arr[i] < pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } return [...quicksort(left), pivot, ...quicksort(right)]; } This code works by recursively partitioning the array around a pivot element. The pivot element is chosen randomly or by some other deterministic means. The array is then partitioned into two subarrays, one containing all the elements smaller than the pivot element and the other containing all the elements larger than the pivot element. The quick sort algorithm is then recursively applied to the two subarrays. Here is an explanation of how the code works: The quickSort() function takes an array as input. The if statement checks if the array is empty or only has one element. If it is, the function simply returns the array. The pivot variable is assigned the value of the element at the midpoint of the array. The smaller and larger arrays are initialized to empty arrays. The for loop iterates through the array, starting at index left and ending at index right. The if statement checks if the element at index i is smaller than the pivot element. If it is, the element is pushed onto the smaller array. Otherwise, the element is pushed onto the larger array. The sortedSmaller and sortedLarger variables are assigned the results of recursively calling the quickSort() function on the smaller and larger arrays, respectively. The return statement returns the concatenation of the sortedSmaller, pivot, and sortedLarger arrays. Comparing Selection sort and Quick sort Sample Input array sizes for both algorithms: Array0 length 10 Array1 length 15 Array2 length 23 Array3 length 34 Array4 length 51 Array5 length 76 Array6 length 114 Array7 length 171 Array8 length 257 Array9 length 385 Array10 length 577 Array11 length 865 Array12 length 1298 Array13 length 1947 Array14 length 2920 Array15 length 4379 Array16 length 6569 Array17 length 9853 Array18 length 14779 Array19 length 22169 Tabular Representation Of running times of Bubble sort VS Merge sort Array Size 10 15 23 34 51 76 114 171 257 Selection Sort 0.2 0.0999 0.0999 0.1999 0.0999 1.3999 0.8999 1.5 6.1 Quick Sort 0 0 0 0.0999 0.1000 0.1999 0.1000 0.3 0.3999 Array Size 385 577 865 1298 1947 2920 4379 6569 9853 14779 22169 Selection Sort 0.3 0.5 1.5999 2.6 4.0999 6.3000 14 37.800 74.3000 155 325 Quick Sort 0.9 1.5 3.1 4 2.5999 2.4 6.9000 6.0999 11.2999 16.4000 23.1999 Graphical representation of Quick sort vs Selection Sort 2. I. Tractable problems: a tractable problem is a problem that can be solved in polynomial time. This means that the time it takes to solve the problem grows as a polynomial function of the size of the input. For example, the problem of sorting a list of numbers is tractable, as the time it takes to sort a list of n numbers grows as n log n. some examples of such problems are: 1. Production Defects: In manufacturing or software development, defects that occur during production can be traced back to their root causes through quality control processes. This tracking helps identify patterns and prevent future occurrences. 2. Inventory Discrepancies: In supply chain management, discrepancies in inventory levels can be traced by tracking the movement and status of goods at different stages of the supply chain. This helps in identifying potential theft, errors, or inefficiencies. 3. Customer Complaints: Companies can trace and analyze customer complaints to identify common issues or recurring problems. This analysis helps in improving products or services and enhancing customer satisfaction. 4. Network Downtime: In IT and networking, network downtime can be traced by monitoring and logging network activities. Analyzing the data helps identify the root causes of outages and enables proactive measures to prevent future downtime. 5. Financial Irregularities: In accounting and finance, traceable problems include irregularities in financial statements, transactions, or billing. Detailed audits and analysis can trace these irregularities to their sources, such as accounting errors or fraudulent activities. II. Intractable problems: an Intractable problems are challenges or issues that are difficult or seemingly impossible to solve within a reasonable time frame or using conventional methods. These problems often lack clear solutions, and their complexity makes them resistant to straightforward approaches. Here are five examples of intractable problems: 1.Traveling salesman problem: Given a list of cities and the distances between them, find the shortest possible route that visits each city exactly once and returns to the starting city. 2. Knapsack problem: Given a set of items with weights and values, and a knapsack with a limited capacity, find the subset of items that has the maximum value and fits within the knapsack. 3. Bin packing problem: Given a set of items with sizes, and a set of bins with limited capacities, find the minimum number of bins needed to pack all of the items. 4. Job shop scheduling problem: Given a set of jobs with processing times and precedence constraints, find a schedule that minimizes the makespan, i.e. the total time it takes to complete all of the jobs. 5. Satisfiability problem: Given a Boolean formula, determine whether there is an assignment of truth values to the variables that makes the formula true. III. p problems: P is a complexity class that contains all decision problems that can be solved by a deterministic Turing machine using a polynomial amount of computation time, or polynomial time. This means that the time it takes to solve a problem in P grows as a polynomial function of the size of the input. Some examples of P problems include: 1.Integer factorization: Given a positive integer n, determine whether n is prime. 2.Graph connectivity: Given a graph, determine whether there is a path between two given vertices. 3.Linear programming: Given a set of linear constraints, find a point that satisfies all of the constraints. 4.Sorting: Given a list of numbers, sort the list in ascending order. 5.String matching: Given two strings, determine whether the second string occurs as a substring of the first string. IV. NP problems: NP problems are a class of decision problems that can be verified in polynomial time, but may not be solvable in polynomial time. This means that if you are given a solution to an NP problem, you can quickly verify that the solution is correct, but you may not be able to find a solution in a reasonable amount of time. Some examples of NP problems include: 1.Traveling salesman problem: Given a list of cities and the distances between them, find the shortest possible route that visits each city exactly once and returns to the starting city. 2.Knapsack problem: Given a set of items with weights and values, and a knapsack with a limited capacity, find the subset of items that has the maximum value and fits within the knapsack. 3.Boolean satisfiability problem: Given a Boolean formula, determine whether there is an assignment of truth values to the variables that makes the formula true. 4.Hamiltonian cycle problem: Given a graph, determine whether there is a cycle that visits each vertex exactly once. 5.Subset sum problem: Given a set of integers and a target sum, determine whether there is a subset of the integers that sums to the target sum. V. NP-complete problems: NP-complete problems are a class of decision problems that are both NP-hard and NP-complete. This means that they are difficult to solve, but if you can solve one NP-complete problem, you can solve all NP-complete problems. Some examples of NP-complete problems include: 1.Traveling salesman problem: Given a list of cities and the distances between them, find the shortest possible route that visits each city exactly once and returns to the starting city. 2.Knapsack problem: Given a set of items with weights and values, and a knapsack with a limited capacity, find the subset of items that has the maximum value and fits within the knapsack. 3.Boolean satisfiability problem: Given a Boolean formula, determine whether there is an assignment of truth values to the variables that makes the formula true. 4.Hamiltonian cycle problem: Given a graph, determine whether there is a cycle that visits each vertex exactly once. 5.Subset sum problem: Given a set of integers and a target sum, determine whether there is a subset of the integers that sums to the target sum. VI. NP-Hard problems: NP-hard problems are a class of decision problems that are at least as hard as any NP-complete problem. This means that if you can solve one NP-hard problem, you can solve all NP-complete problems. Some examples of NP-hard problems include: 1. Boolean satisfiability problem (SAT): Given a Boolean formula, determine whether there is an assignment of truth values to the variables that makes the formula true. 2. Traveling salesman problem (TSP): Given a list of cities and the distances between them, find the shortest possible route that visits each city exactly once and returns to the starting city. 3. Knapsack problem: Given a set of items with weights and values, and a knapsack with a limited capacity, find the subset of items that has the maximum value and fits within the knapsack. 4. Set cover problem: Given a set of elements and a collection of subsets of those elements, find the minimum number of subsets needed to cover all of the elements. 5. Graph coloring problem: Given a graph, color the vertices so that no two adjacent vertices have the same color, using the minimum number of colors possible.