Klein 1 Peter D. Klein Research Project for Algorithms CSC320 Professor Shana Watters Spring 2016 Sorting Algorithm’s in Java Introduction Algorithms have existed far longer than the creation of the first computer. Algorithms are the backbone of searching, sorting or solving any problem and are used subconsciously every day. We as humans sort clothes, food, cars and countless other things in our daily lives, so it is important for us to understand the process it takes to perform these sorting algorithms. Algorithms are a necessity in the field of computing. They give step by step solutions in a finite amount of time to specific functions. Sorting algorithms are especially useful in computing because they give order to data or databases, which guide readability and further computation. All algorithms are based on certain criteria which judge their power. One of these major criteria is the algorithms efficiency. Efficiency is based on the algorithms process, as well as the state of data it is sorting. These factors make some algorithms more efficient than others in certain situations. This project will test the growth of six sorting algorithms based on the order of the data, to analyze its efficiency. Algorithm and hypothesis discussion Bubble Sort Bubble sort is a sort that contains a for loop embedded in a for loop. It looks at a list’s last position and compares it to the position one lower to see which one is a smaller value. Bubble sort takes the smaller value and puts it in the lower position of the two. Bubble sort then looks at the second to last position and compares it to the position one lower. It does the same Klein 2 process of comparing and swapping. It continues that process until it eventually “bubbles down” the lowest value in the list to the first position. The outer loop then starts this whole process over again, except since we know the lowest value is in the first position, we check every position except for the first. This whole process is continued until our list is sorted. Bubble sort is always going to have a run time of O(𝑛2 ) due to its embedded for loop, seen below in its pseudocode. The first for loop will be entered 𝑛 number of times, 𝑛 being the number of elements in the array; and its embedded loop will be entered n-1 number of times. It will always compare the elements but the swapping does not . It does not matter the order of the elements, therefore it will always run in a O(𝑛2 ). Taking the Big O into account, I hypothesize that bubble sort’s run time will grow exponentially as more elements are sorted. Selection Sort Selection sort is an algorithm that has a for loop embedded in a for loop. Starting at the lists first position, selection sort runs through the entire list and finds the smallest value. Once it is found, it puts that value in the lists first position and repeats the process starting at the second position in the list. It then continues this until the whole list is sorted. Selection sort is similar to bubble sort, as it will also always have a run time of O(𝑛2 ) because of its embedded for loop. As shown in the pseudocode below, the loops run on order 𝑛, 𝑛 being the number of elements in the array. Both loops will always be entered no matter the Klein 3 arrays condition and the swapping will always have a constant time. Because of this, the runtime will always be O(𝑛2 ). It does not matter the order of the elements. Taking the Big O into account, I hypothesizes that selection sort’s run time will grow exponentially as more elements are sorted. SELECTION-SORT(A) 1 for i = 1 to n-1 2 currentSmallest = A[i] 3 for j = i to n 4 If A[j] < currentSmallest 5 A[j] = currentSmallest 6 exchange A[i] with currentSmallest Insertion Sort Insertion sort is a sort that contains two sub lists that make up a greater list, one list is sorted and the other is not. Insertion sort starts by taking the left most element of a list and considers that one element to be sorted. It then checks the second element of the list and compares it to the first element to see if it is larger or smaller. If it is larger, it stays in the same spot If it’s smaller it goes behind and becomes the first element. It then checks the third element and compares it to the first two, placing it in its properly sorted position. It continues this with the whole list until it is sorted. Insertion sort has a worst case scenario of O(𝑛2 ). This occurs when the list is sorted in descending order because we must compare and shift each element behind the last. As shown in the pseudocode below, the worst case will cause the embedded while loop to be entered during every run through of the outer for loop. Since the both loop runs on order 𝑛, 𝑛 being the number of elements in the array, its worst case will be O(𝑛2 ). It has a best case of O(𝑛) when the list is Klein 4 sorted in ascending order because it will never enter its embedded loop, so no swapping and shifting is needed. Taking the Big O into account, I hypothesize that insertion sort’s run time will grow exponentially in its worst case, and linear in its best case. Heap Sort Heap sort is an algorithm that uses a tree structure to sort elements of a list. The first thing heap sort does is build a max heap out of a list. A max heap is a tree the satisfies these properties: all children nodes must have a lesser value than that of its parent; the tree must be filled left to right, top to bottom; each parent can have 0 to 2 children. Once the max heap has been built, the largest element will have been placed at the top of the tree. This is now the “heap.” The top element is then swapped with the last element in the tree. Then using maxheapify is built since the tree is now not a max heap, but heap sort does not check the last element because it is in its proper position. Once swapped, the “heap” is now one element smaller. Every time it repeats this process the largest element will be placed at the top of the tree, then it will be swapped with the heaps last position. This process is repeated until the list is properly sorted. Heap sort has a best and worst case scenario of 𝑂(𝑛 log 𝑛), so asymptotically is does not matter the order of the elements in the list. The reason it runs in 𝑂(𝑛 log 𝑛) is due to the amount Klein 5 of times the algorithm will max-heapify the list. As seen in the pseudocode below, the original “heapsort” and “build max heap” have non-embedded for loops that run on order 𝑛, 𝑛 being the number of elements in the array. Max-heapify will recursively call itself only if certain conditions are met. When run on a tree 𝐴, of size 𝑛, rooted at 𝑖, max-heapify has a run time of 𝜃(1). It has this run time to sort 𝐴[𝑖], 𝐴[𝑅𝐼𝐺𝐻𝑇(𝑖)], 𝑎𝑛𝑑 𝐴[𝐿𝐸𝐹𝑇(𝑖)], to meet the max heap properties. It must also run max-heapify on the subtree rooted at the child nodes of 𝐴[𝑖]. Each subtree has a worst case size of 2𝑛/3, which occurs when the bottom level of the tree is half full. Thus, Max-heapify’s running time can be described as: 𝑇(𝑛) ≤ 𝑇 ( 2𝑛 ) + 𝜃(1) 3 By case 2 of the master theorem, it can be determined 𝑇(𝑛) = 𝑂(𝑙𝑔𝑛). The run time of Max-heapify, combined with the linear times discussed earlier, give the total run time of heap sort to be 𝑂(𝑛 log 𝑛). Taking the Big O into account, I hypothesize that heap sort’s run time will grow logarithmically as more elements are sorted. Klein 6 Quick Sort Quick sort is a sort that uses pointers and swapping to sort elements. The algorithm starts by taking the last element of the array and naming it the pivot, then it takes the first element and names it the wall. Quick sort goes through each element in the list and compares it to the pivot, if the element is larger, it does nothing, but if it’s smaller, it takes that element and swaps it with the wall, then moves the wall up one position. By doing this, everything to the left of the wall is smaller than the pivot, and everything right of the wall is larger. It then takes the pivot and swaps it with the wall, and now the pivot is in its proper location. Quick sort then shifts the wall up one so it’s right of the previous pivot, and performs the previous process on the two sub lists divided by the previous pivot, creating two new walls and pivots. It continues this until the list is properly sorted. Quick sort has a worst case scenario of 𝑂(𝑛2 ). This occurs when the list is already in sorted order. The pivot will always be the largest element and will therefore swap with its own position every time. This means it will quick sort a list one size less, costing 𝑂(𝑛 − 1). The partition will always run on 𝜃(𝑛), because of its for loop. This means the total cost will equate to a total cost of 𝑂(𝑛2 ). Klein 7 The best case of quick sort is 𝑂(𝑛 log 𝑛), when our partition is as balanced as possible. Our pivot should always be as close to the median of list as possible, which would divide our problem in half every time. The partition will produce two sub problems, each of size no larger than 𝑛/2. This time combined with the partition time produces the equation: 𝑛 𝑇(𝑛) = 2𝑇 ( ) + 𝜃(𝑛) 2 By case 2 of the master theorem, it can be determined 𝑇(𝑛) = 𝑂(𝑛𝑙𝑔𝑛). When dividing the sub problems in half, quicksort becomes asymptotically faster. Quick sort’s average time is closer to 𝑂(𝑛𝑙𝑜𝑔𝑛) than 𝑂(𝑛2 ). With an average case, the partition will have a mix of bad splits resulting in an 𝑛 − 1, and good splits resulting in 𝑛/2. To explain this, let us assume the worst and best case alternate between partitions. The root will always cost 𝑛 for the partition, and the worst case will produces 2 sub-problems of 0 and 𝑛 − 1. Then both of these sub problems will be partitioned in the best case. The sub problem of 0 will produce two children of 0/2 and 0/2. The sub-problem of 𝑛 − 1 will produce the two children 𝑛−1 2 − 1 and 𝑛−1 2 . In total, the cost will be 𝜃(𝑛) + 𝜃(𝑛 − 1) = 𝜃(𝑛). In a sense, the bad partition cost is absorbed by the best partition cost. So when alternating between good and bad splits, it is like quick sort is running in its best case 𝑂(𝑛𝑙𝑜𝑔𝑛), but with a higher constant, not represented in the Big O. Taking the Big O into account, I hypothesize that quick sort’s run time will grow exponentially in its worst case, and logarithmically in its best case. Klein 8 Merge Sort Merge sort is a divide and conquer sort. Merge sort continuously splits a list in half, sorts the two lists, then merges and sorts the two back together. It starts by splitting a list in half and then splitting those halves in half and doing this until each element is in a list by itself. It then merges each list by looking at the first element of each list, and taking the smaller of the two and putting it in a new list. It continues this until the final two lists are sorted and merged together. This is an easy process when each list contains only one element, but the lists grow as they merge and the bigger lists get compared and merged with each other. It continues this process until it merges the two halves of the full list, which sorts the full list properly. Merge sort has a best and worst case scenario of 𝑂(𝑛 log 𝑛), so no matter the order of the list, the run time will be the same. The constant time may change slightly, but asymptotically they are the same. The reason it remains 𝑂(𝑛 log 𝑛) is due to the growth of the sorted list. Every time merge sort runs, the sorted lists starts as one element. As it merges with the other sorted lists, its size grows from 1, to 2, then 4, 8, 16, etc. Merge sort does 𝑙𝑜𝑔𝑛 halvings of a list, and subsequently it will take 𝑙𝑜𝑔𝑛 doublings to reach a fully sorted list with 𝑛 elements. Since there are 𝑛 elements being sorted on a time of 𝑂(𝑙𝑜𝑔𝑛), merge sorts total run time will be 𝑂(𝑛𝑙𝑜𝑔𝑛). Klein 9 Taking the Big O into account, I hypothesize that merge sort’s run time will grow logarithmically as more elements are sorted. Methodology To show each algorithms’ efficiency, an experiment will be set up that will time each algorithms’ sort on many different arrays, in many different states. The six algorithms being tested are bubble sort, insertion sort, selection sort, heap sort, quick sort and merge sort. Each sort has been implemented into Java, using the IDE Eclipse. The algorithms will be timed using the method “System.nanoTime().” I will call this method before and after the algorithm is run and take the difference of the two times. This will calculate the number of nanoseconds elapsed for each sorting algorithm, which will then be converted to seconds. There will be five sizes of arrays: 100; 1,000; 10,000; 100,000; and 1,000,000. Each array size will have three different orderings: one random, one in ascending order, and one in descending order, for a total of 15 arrays. Each array will contain n elements and will include the values 1 to n, with no duplicates. Klein 10 All arrays will be created using a for loop. The random arrays will be created as ascending order arrays at first, then will be shuffled using “Collections.shuffle(arrayList)” seven times in order to assure maximum randomization. All arrays will be generated and saved to files in order to make sure the same array is being tested for each algorithm, and eliminate miscued data. This experiment will be run on a HP Pavilion laptop in high performance mode with no other apps running at the same time, excluding background processes. Eclipse will also be allocated more RAM so the sorts do not crash. Each test will be run in these same conditions for the most accurate data. This whole process will be executed three separate times, and the average times will be taken for each array size and order for each algorithm. The number of tests, as well as different orders of each array size, will give us the most accurate time for each sort. After the runtimes have been collected, the data will be analyzed using the Big O. Simply put, the Big O characterizes the growth of a sorts runtime based on the number of elements it is sorting. One must understand that if an algorithm runs on 𝑂(𝑛2 ), that does not mean that when 𝑛 = 1,000,000, the run time will be 1,000,0002 . Rather, if an algorithm runs on 𝑂(𝑛2 ), the run time will grow exponentially as 𝑛 grows. The formal definition of the Big O is 𝑓(𝑛) = 𝑂(𝑔(𝑛)) when there exists positive constants c and k, such that 0 ≤ 𝑓(𝑛) ≤ 𝑐𝑔(𝑛) for all 𝑛 ≥ 𝑘. Once the raw data has been gathered, it will be graphed and compared to the Big O hypothesis of each algorithm. If the graphs of each sort will reflect the hypothesized Big O growth, the hypothesis is correct. Hypothesis The efficiency of an algorithm is based on its process, the number of elements in a list as well as the list’s order. When hypothesizing which run time will be “best,” one must take all of these elements into account. In many cases, certain algorithms will perform better when certain Klein 11 criteria are met. For example, some algorithms may be very efficient in small test cases but be exponentially more costly in large test cases. And in other cases some algorithms may be very efficient when a list is random but will lose efficiency if the list is already sorted. Many algorithms lack simplicity, but make up for it in efficiency, or vice versa. In my personal opinion, to truly see an algorithms efficiency, one must test the algorithm on lists of one million elements or larger. I believe this because when sorting small lists such as one hundred elements, most algorithms perform relatively the same. That being said, I believe bubble sort will always have the largest asymptotic running time as we test larger and larger lists. Bubble sort’s constant comparing and swapping will become very noticeable when it begins to test bigger and bigger sets of elements. Also its constant 𝑂(𝑛2 ) will lead to exponential time growth as more and more elements are tested. I believe that the most efficient algorithms will be heap sort or merge sort. I believe this due to the fact that they both utilize the “divide and conquer” method, which divides problems into much smaller problems, creating greater efficiency. Both sorts also have a consistent 𝑂(𝑛𝑙𝑜𝑔𝑛), no matter the order of the data. When comparing the Big O of all the tested algorithms, these algorithms should consistently have faster times as the number of elements increases. Big O Table Bubble sort Selection sort Insertion sort Heap sort Quick sort Merge sort Best case O(𝑛2 ) O(𝑛2 ) O(n) O(n log(n)) O(n log(n)) O(n log(n)) Worst case O(𝑛2 ) O(𝑛2 ) O(𝑛2 ) O(n log(n)) O(𝑛2 ) O(n log(n)) Klein 12 Table 1 – Chart of each algorithms Big O. Number of elements 100 1,000 10,000 100,000 1,000,000 INSERTION 2.13E-04 3.61E-03 4.59E-02 2.09E+00 1.74E+02 SELECTION 1.38E-04 2.34E-03 2.61E-02 2.50E+00 2.71E+02 BUBBLE QUICK 3.12E-04 4.09E-03 9.68E-02 1.03E+01 9.97E+02 MERGE 1.74E-04 2.06E-03 4.67E-02 4.79E+00 4.83E+02 1.42E-04 3.85E-04 3.48E-03 1.66E-02 1.51E-01 HEAP 1.29E-04 5.07E-04 2.58E-03 1.73E-02 1.60E-01 Table 2 – Average times of each algorithm. 100 elements 4.00E-04 3.50E-04 TIME (SECONDS) 3.00E-04 2.50E-04 2.00E-04 1.50E-04 1.00E-04 5.00E-05 0.00E+00 Insertion Selection Bubble Quick Merge SORT Random Low to High High to Low Figure 1 - Time elapsed for each sort on the arrays of 100 elements. Heap Klein 13 1,000 elements 8.00E-03 TIME ()SECONDS 7.00E-03 6.00E-03 5.00E-03 4.00E-03 3.00E-03 2.00E-03 1.00E-03 0.00E+00 Insertion Selection Bubble Quick Merge Heap SORTS Random Low to High High to Low Figure 2 - Time elapsed for each sort on the arrays of 1,000 elements. TIME ()SECONDS 10,000 elements 0.2 0.18 0.16 0.14 0.12 0.1 0.08 0.06 0.04 0.02 0 Insertion Selection Bubble Quick Merge SORTS Random Low to High High to Low Figure 3 - Time elapsed for each sort on the arrays of 10,000 elements. Heap Klein 14 100,000 elements 25 TIME (SECONDS) 20 15 10 5 0 Insertion Selection Bubble Quick Merge Heap SORTS Random Low to High High to Low Figure 4 - Time elapsed for each sort on the arrays of 100,000 elements. 1,000,000 elements 2500 TIME (SECONDS) 2000 1500 1000 500 0 Insertion Selection Bubble Quick Merge SORTS Random Low to High High to Low Figure 5 - Time elapsed for each sort on the arrays of 1,000,000 elements. Heap Klein 15 Heap Sort Time (Seconds) 0.25 0.2 0.15 0.1 0.05 0 0 200000 400000 600000 800000 1000000 Number of elements Ascending Descending Random Figure 6 - Run time of Heap sort. Merge Sort Time (Seconds) 0.25 0.2 0.15 0.1 0.05 0 0 200000 400000 600000 800000 Number of elements Ascending Descending Figure 7 - Run time of Merge sort. Random 1000000 Klein 16 Time (Seconds) Quick Sort 900 800 700 600 500 400 300 200 100 0 0 200000 400000 600000 800000 1000000 Number of elements Ascending Descending Random Figure 8 - Run time of Quick sort. Bubble Sort Time (Seconds) 2000 1500 1000 500 0 0 200000 400000 600000 800000 Number of elements Ascending Descending Figure 9 - Run time of Bubble sort. Random 1000000 Klein 17 Selection Sort 400 Time (Seconds) 350 300 250 200 150 100 50 0 0 200000 400000 600000 800000 1000000 Number of elements Ascending Descending Random Figure 10 - Run time of Selection sort. Insertion Sort 400 Time (Seconds) 350 300 250 200 150 100 50 0 0 200000 400000 600000 800000 1000000 Number of elements Ascending Descending Random Figure 11 - Run time of Insertion sort. Results The preceding graphs represent the data collected from the experiment. Three tests of each sort, on each array size, in each order have been run. The data was then averaged and produced into the previous 11 graphs. Overall, the data produced from this experiment was as expected. Each sort held true to the hypothesized Big O, and few errors occurred. Klein 18 When testing small amount of elements, such as 100, the difference in run time of these algorithms is next to nothing. But as the array size increases, it is apparent that certain algorithms perform much better in certain situations. It is clear to see from figures 1 – 5 that bubble sort appears to have the biggest increase in run time as more elements are added. These figures also show that as the number of elements increase, both merge and heap sort seem to be the most all around efficient algorithms. As the number of elements grew, quick sort performed very poorly when the lists were in ascending and descending order. However, quick sort actually had the overall fastest average time for one million elements. It clocked in at an average of 0.14 seconds when sorting one million randomly sorted elements. So what sort is best to use? When using small data sizes such as 100, 1,000, or even 10,000 elements, most sorts perform within 1 second of each other. However when sorting a list of 100,000 or 1,000,000 elements, efficiency is important. When sorting a list, most would assumes that the list is in a random order, or else why else would you want to sort it? If that is the case, the data collected and expressed in figures 1 – 5 of this experiment prove that the best sort to use would be quick sort if your list is random. If one knows that the list to be sorted could possibly be already sorted, or have some kind of trend simulating slight order, this experiment proves that the best sort to use should be either merge or heap sort. I believe that it is best to use merge and heap sort in any situation. Quick sort was faster in a certain situation, but all around, merge and heap sort are the most efficient algorithms in this experiment. Bubble Sort Klein 19 Bubble sort has a 𝑂(𝑛2 ) for both its best and worst case scenario. Using this information, it was hypothesized that bubble sort would have exponential growth as the number of elements to be sorted increased. As seen in figures 1 - 5 as well as figure 9, no matter the order of the lists, bubble sort’s run time increased exponentially. The times start off very similar with less elements, but the differences become more prominent when more elements are sorted. In fact, with every array size increase, the time it takes to perform the sort is multiplied by roughly 100. This can be seen in table 2, the time it takes to sort random goes from roughly 0.18 seconds, to 19 and then 1918 seconds. Ascending sort time goes from 0.03 seconds, then jumps to 3 and then 312 seconds. Descending sort time goes from roughly 0.07 seconds, to 7 then 760 seconds. This pattern is seen through all of bubble sort’s tests. Random order consistently took the longest to sort, while descending was always second longest, and ascending order was always fastest. The data collected supports the hypothesized 𝑂(𝑛2 ), however there is question as to why the random ordered sorting takes far longer than the descending ordered sort. It seems intuitive to believe that random should take less time. Descending order has to swap each element, every time, because elements to the left will always be of a smaller value, while random order should not have this occur every time. The reason this occurs is due to branch prediction. A branch predictor is a digital circuit that tries to guess which way an “if” statement will go before it is known. Branch predictors improve the efficiency of an algorithm if the same condition happens over and over and it had an effect on the descending ordered sort. Since the “if” statement was always entered, the Branch predictor improved bubble sort’s efficiency. Selection Sort Selection sort has a 𝑂(𝑛2 ) for both its best and worst case scenario as well. Using this information, it was hypothesized that selection sort would have exponential growth as the Klein 20 number of elements to be sorted increased. As seen in figures 1 - 5 as well as figure 10, no matter the order of the lists, selection sort’s run time increases exponentially. Just as bubble sort, the times start off very similar with less elements, but the differences become more prominent when more elements are sorted. With every array size increase, the time it takes to perform the sort is multiplied by roughly 100. The run time for sorting each list order are within a few seconds of each other until testing one million elements. As seen in figure 10, the random ordered and ascending ordered lists remain similar, and the descending order consistently takes longer. Taking a look back at the insertion sort pseudocode, random and ascending order will not swap for the smallest element every time. Since descending order is high to low, selection sort must swap every time, which is why it’s run time is always longer. Even though bubble and selection sort have different physical run times, and the order of the list changes the run time, they still both hold 𝑂(𝑛2 ). Both algorithms run times grow exponentially no matter the lists order. Insertion Sort Insertion sort has a best case run time of 𝑂(𝑛), and a worst case run time of 𝑂(𝑛2 ). Using this information, it was hypothesized that insertion sort would have exponential growth in its worst case when a list is in descending order, and it would have linear growth in its best case when a list is already sorted in ascending order. As seen in figures 1 – 5, insertion sort’s time for sorting random lists and descending lists grows exponentially, while the time for sorting ascending lists remains linear. This is better seen in figure 11. Ascending sorting time remains linear because the inner while loop of insertion sort is never entered, so only its outer for loop is accounted for. Descending sorting time grows exponentially because the inner while loop of insertion sort is entered every single time it is able. As seen in figure 11, random sorting time Klein 21 grows exponentially, but not as rapidly as descending sorting time. This is because it enters the inner while loop only on certain iterations, giving it a mixture of worst and best case scenario. Heap Sort Heap sort also has a both a best case and worst case run time of 𝑂(𝑛𝑙𝑜𝑔𝑛). As seen from figures 1 – 5 and figure 6, heap sort has a very similar run time growth as merge sort. It will always use the same process to develop the max heap, and it will always run on the same time because there are many different max heaps that can be generated using the same data. Heap sort’s big O is more efficient than most other algorithms tested, and this becomes apparent in figures 1 – 5. It can also be seen that even when sorting one million elements, the sorting time for each order of the lists remains within 0.12 seconds of each other, which would go unnoticed by the average user. Ascending and random ordered list have nearly identical times, even when reaching one million elements. Descending order sorting is also nearly identical, but is a little faster. It’s slightly faster due to the fact that the list starts out in a max heap, saving some constant time thereby reflected in figure 6. Quick Sort Quick sort has a best case run time of 𝑂(𝑛𝑙𝑜𝑔𝑛), and a worst case run time of 𝑂(𝑛2 ). Using this information, it was hypothesized that quick sort would have exponential growth in its worst case when a list is in ascending order, and it would have logarithmic growth in its best case when a list has a most balanced pivot. As seen in figure 8, the time of both ascending and descending sorting grows exponentially, while random sorting grows logarithmically. As discussed earlier in quick sort’s hypothesis, the reason the time of ascending and descending sort grows exponentially is because the pivot is always going to be the next largest or smallest value. In the ascending case, every Klein 22 quick sort recursion will be run on a list of n-1, because the pivot is swapped with itself. In the descending case every quick sort recursion will also run on a list of n-1, because the wall will never move and the pivot will just move to the first position. As seen in figure 8, random order sorting time has a logarithmic growth. The reason for this is because random is a mix of worst and best case partitioning. As discussed earlier in quick sort’s hypothesis, with enough elements, the best case “outweighs” the worst case and appears logarithmic. With a randomly sorted list, the pivot is more likely to be the median of the list. The closer it is to the median, the greater the division of the problem, which will induce faster, logarithmic running time. Merge Sort Merge sort has a both a best case and worst case run time of 𝑂(𝑛𝑙𝑜𝑔𝑛). It always has this run time because it performs the same subdivision and merging process no matter the data. Merge sort’s big O is more efficient than the other algorithms tested, and this becomes apparent in figures 1 – 5. It can also be seen that even when sorting one million elements, the sorting time for each order of the lists remains within 0.12 seconds of each other, which would go unnoticed by the average user. Figure 7 shows the ascending and descending ordered lists have nearly the same run time, while random takes slightly longer. When merging ascending and descending ordered lists, one of the sub lists will contain all the smaller values, so one sub list is just appended to the other. Random ordered lists must switch between taking the smallest value from the front of each sub list. This gives random slightly more constant time. Each list order runs on 𝑂(𝑛𝑙𝑜𝑔𝑛), so even when sorting one million elements, the wait time is miniscule. Conclusion Klein 23 Algorithms have existed far longer than the creation of the first computer. We sort clothes, food, cars and countless other things in our daily lives, so we must understand the process it takes to perform these sorting algorithms. Algorithm creation and understanding are used by every computer scientist and one must understand how algorithms truly work before creating new ones. Algorithms are what solve the problems thought to be once impossible, and will forever be used in computer science. This experiment has given insight into the world of algorithms and their efficiency. This experiment has taught me the tools to determine what algorithm should be used in certain situations. I now have a greater understanding of algorithms and the important role they continue to play in computer science as well as everyday life.
© Copyright 2026 Paperzz