I am working on a project that asks us to implement different sorts and add counter variables to measure the runtime with different array sizes.
My problem is that the output is not the same as the expected output
I already completed the insertion sort and correctly counts the number of comparisons.
I am allowed to use reference parameter.
Any feedback is appreciated.
Output:[Updated from Answer]
Quick sort 16 384 6401 74378
Expected Output:
Array Size: 10 100 1000 10000
--------------------------------------------------------------------
Quick Sort 35 630 10292 132882
Whats inside the contents of the array for array size 10:
The contents stay Constant with the used seed.
Insertion sort
[ 935, 942, 697, 299, 382, 579, 408, 181, 366, 505 ] //unsorted
[ 181, 299, 366, 382, 408, 505, 579, 697, 935, 942 ] //sorted
Program: [Updated From Answer]
#include <iostream>
#include <stdlib.h>
#include <iomanip>
#include <cmath>
/******************************/
/* Start of Quick Sort Algorithm */
/******************************/
static const int MIN_SIZE = 10; // Smallest size of an array that quicksort will sort
/**
* Sorts the items in an array into ascending order.
*/
template<class ItemType>
int insertionSort(ItemType theArray[], int first, int last) {
int count = 0;
for (int unsorted = first + 1; unsorted <= last; unsorted++) {
ItemType nextItem = theArray[unsorted];
int loc = unsorted;
while ((loc > first) && (count++,theArray[loc - 1] > nextItem)) {
theArray[loc] = theArray[loc - 1];
loc--;
}
theArray[loc] = nextItem;
}
return count;
}
/**
* Arranges two specified array entries into sorted order by
* exchanging them, if necessary.
* */
template<class ItemType>
void order(ItemType theArray[], int i, int j) {
if (theArray[i] > theArray[j]) {
std::swap(theArray[i], theArray[j]);
}
}
/**
* Arranges the first, middle, and last entry in an array in sorted order.
*/
template<class ItemType>
int sortFirstMiddleLast(ItemType theArray[], int first, int last) {
int mid = first + (last - first) / 2;
order(theArray, first, mid); // Make theArray[first] <= theArray[mid]
order(theArray, mid, last); // Make theArray[mid] <= theArray[last]
order(theArray, first, mid); // Make theArray[first] <= theArray[mid]
return mid;
}
/**
* Partitions the entries in an array about a pivot entry for quicksort.
*/
template<class ItemType>
int partition(ItemType theArray[], int first, int last,int &counter) {
// Choose pivot using median-of-three selection
int pivotIndex = sortFirstMiddleLast(theArray, first, last);
// Reposition pivot so it is last in the array
std::swap(theArray[pivotIndex], theArray[last - 1]);
pivotIndex = last - 1;
ItemType pivot = theArray[pivotIndex];
// Determine the regions S1 and S2
int indexFromLeft = first + 1;
int indexFromRight = last - 2;
bool done = false;
while (!done) {
// Locate first entry on left that is >= pivot
while (theArray[indexFromLeft] < pivot) {
counter++;//I incremented Count here
indexFromLeft = indexFromLeft + 1;
}
// Locate first entry on right that is <= pivot
while (theArray[indexFromRight] > pivot) {
counter++;
indexFromRight = indexFromRight - 1;
}
if (indexFromLeft < indexFromRight) {
std::swap(theArray[indexFromLeft], theArray[indexFromRight]);
indexFromLeft = indexFromLeft + 1;
indexFromRight = indexFromRight - 1;
}
else {
done = true;
}
}
// Place pivot in proper position between S1 and S2, and mark its new location
std::swap(theArray[pivotIndex], theArray[indexFromLeft]);
pivotIndex = indexFromLeft;
return pivotIndex;
}
/**
* Sorts an array into ascending order. Uses the quick sort with
* median-of-three pivot selection for arrays of at least MIN_SIZE
* entries, and uses the insertion sort for other arrays.
*/
template<class ItemType>
int quicksort(ItemType theArray[], int first, int last) {
int result = 0;
int counter = 0;
if (last - first + 1 < MIN_SIZE) {
result = insertionSort(theArray, first, last);
}
else {
// Create the partition: S1 | Pivot | S2
int pivotIndex = partition(theArray, first, last,counter);
// Sort subarrays S1 and S2
result += quicksort(theArray, first, pivotIndex - 1);
result += quicksort(theArray, pivotIndex + 1, last);
result += counter;
}
return result; //return final count
}
/******************************/
/* Start of Sorting Benchmark */
/******************************/
int* makeRandomArray(int n, int seed) {
srand(seed);
int * a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = rand() % 1000;
}
return a;
}
int main(){
const int seed = 9000;
int *a;
/******************************/
/* Start of Quick Sort */
/******************************/
std::cout << "Quick sort";
int n = 10;
a = makeRandomArray(10, seed);
std::cout <<std::setw(13)<< quicksort(a, 0, n-1);
delete[] a;
n = 100;
a = makeRandomArray(100, seed);
std::cout <<std::setw(13)<< quicksort(a, 0, n-1);
delete[] a;
n = 1000;
a = makeRandomArray(1000, seed);
std::cout <<std::setw(13)<< quicksort(a, 0, n-1);
delete[] a;
n = 10000;
a = makeRandomArray(10000, seed);
std::cout <<std::setw(13)<< quicksort(a, 0, n-1)<<std::endl;
delete[] a;
return 0;
}
In quick sort, the comparisons are made when the partition method/function is looking for elements that break the rule that elements smaller than the pivot must be on the left of the pivot and elements larger than it must be on the right. These two whiles do exactly this. The first one keeps going to the right while the current element is smaller than the pivot (first set of comparisons). The second while do the opposite operation, i.e., it keeps going to the left while the current element is bigger than the pivot, doing the second set of comparisons. If the left index is smaller than the right index, it means that it found an element that is bigger than the pivot on the left and an element that is smaller than the pivot on the right, so it will swap them. The process continues, until the left index pass throught the right one. When it finishes, all the left values are smaller than the pivot and all the right values are bigger than the pivot, so it swaps the pivot with the last element of the smaller side and returns the pivot position, since now, the pivot is in the correct position and the "middle" of that partition was found, allowing the sorting process to continue to the left partition and to the right partition.
while (theArray[indexFromLeft] < pivot) {
// count here
indexFromLeft = indexFromLeft + 1;
}
while (theArray[indexFromRight] > pivot) {
// and here
indexFromRight = indexFromRight - 1;
}
Related
I am working on a project that asks us to implement different sorts and add counter variables to measure the runtime with different array sizes. My problem is that my current output is not matching the expected output for my Merge Sort. Its only off by a bit.
Any suggestions on what is wrong?
Output:
Array Size: 10 100 1000 10000
--------------------------------------------------------------------
Merge sort 19 569 9275 129630
Expected Output:
Array Size: 10 100 1000 10000
--------------------------------------------------------------------
Merge Sort 19 550 8706 120355
Whats inside the contents of the array for array size 10
Merge sort
[ 935, 942, 697, 299, 382, 579, 408, 181, 366, 505 ] //unsorted
[ 181, 299, 366, 382, 408, 505, 579, 697, 935, 942 ] //sorted
Program:
#include <iostream>
#include <stdlib.h>
#include <iomanip>
#include <cmath>
/******************************/
/* Start of Merge Algorithm */
/******************************/
/**
* The maximum size of the temporary array
*/
const int MAX_SIZE = 10000;
int count = 0; //I added this global variable but It seems Incorrect
/**
* Merges two sorted array segments theArray[first..mid] and
* theArray[mid+1..last] into one sorted array.
*/
template<class ItemType>
void merge(ItemType theArray[], int first, int mid, int last) {
ItemType tempArray[MAX_SIZE]; // Temporary array
// Initialize the local indices to indicate the subarrays
int first1 = first; // Beginning of first subarray
int last1 = mid; // End of first subarray
int first2 = mid + 1; // Beginning of second subarray
int last2 = last; // End of second subarray
// While both subarrays are not empty, copy the
// smaller item into the temporary array
int index = first1; // Next available location in tempArray
while ((first1 <= last1) && (first2 <= last2)) {
// At this point, tempArray[first..index-1] is in order
if (theArray[first1] <= theArray[first2]) {
tempArray[index] = theArray[first1];
first1++;
count++; //I incremented count here
}
else {
tempArray[index] = theArray[first2];
first2++;
count++; //I incremented count here
} // end if
index++;
} // end while
// Finish off the first subarray, if necessary
while (first1 <= last1) {
// At this point, tempArray[first..index-1] is in order
tempArray[index] = theArray[first1];
first1++;
index++;
} // end while
// Finish off the second subarray, if necessary
while (first2 <= last2) {
// At this point, tempArray[first..index-1] is in order
tempArray[index] = theArray[first2];
first2++;
index++;
} // end for
// Copy the result back into the original array
for (index = first; index <= last; index++) {
theArray[index] = tempArray[index];
}
}
/**
* Sorts the items in an array into ascending order.
*/
template<class ItemType>
int mergesort(ItemType theArray[], int first, int last) {
if (first < last) {
// Sort each half
int mid = first + (last - first) / 2;
// Sort left half theArray[first..mid]
mergesort(theArray, first, mid);
// Sort right half theArray[mid+1..last]
mergesort(theArray, mid + 1, last);
// Merge the two halves
merge(theArray, first, mid, last);
}
return count; //return count here
}
/******************************/
/* Start of Sorting Benchmark */
/******************************/
/**
* Generate an array of random integers
*/
int* makeRandomArray(int n, int seed) {
srand(seed);
int * a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = rand() % 1000;
}
return a;
}
int main(){
const int seed = 9000;
int *a;
/******************************/
/* Start of Merge Sort */
/******************************/
std::cout << "Merge sort";
int n = 10;
a = makeRandomArray(10, seed);
std::cout <<std::setw(13)<< mergesort(a, 0, n-1);
delete[] a;
n = 100;
a = makeRandomArray(100, seed);
std::cout <<std::setw(13)<< mergesort(a, 0, n-1);
delete[] a;
n = 1000;
a = makeRandomArray(1000, seed);
std::cout <<std::setw(13)<< mergesort(a, 0, n-1);
delete[] a;
n = 10000;
a = makeRandomArray(10000, seed);
std::cout <<std::setw(13)<< mergesort(a, 0, n-1)<<std::endl;
delete[] a;
}
I have the following quick sort algorithm:
int quicksort(int data[], size_t n, int &counter)
// Library facilities used: cstdlib
{
size_t pivot_index; // Array index for the pivot element
size_t n1; // Number of elements before the pivot element
size_t n2; // Number of elements after the pivot element
if (n > 1)
{
// Partition the array, and set the pivot index.
partition(data, n, pivot_index, counter);
// Compute the sizes of the subarrays.
n1 = pivot_index;
n2 = n - n1 - 1;
// Recursive calls will now sort the subarrays.
quicksort(data, n1, counter);
quicksort((data + pivot_index + 1), n2, counter);
}
return counter;
}
void partition(int data[], size_t n, size_t& pivot_index, int &counter){
int pivot = data[0];
size_t too_big_index = 1;
size_t too_small_index = n - 1;
while (too_big_index <= too_small_index)
{
while (++counter && (too_big_index < n) && (data[too_big_index] <= pivot)) too_big_index++;
while (++counter && data[too_small_index] > pivot ) too_small_index--;
counter++;
if (too_big_index < too_small_index) swap(data[too_big_index], data[too_small_index]);
};
pivot_index = too_small_index;
data[0] = data[pivot_index];
data[pivot_index] = pivot;
}
I have added the three counter increments in the partition function, however the the counter comes out with a value of 32019997 when using a sorted array of 8000 elements, I am using a pivot of the left-most element (I know that gives me a terrible worst-case in terms of sorted array), unless I am incorrect, shouldn't the worst case be n^2 i.e 64000000? So I assume the way I am counting comparisons is wrong, but I am not sure how.
I have implemented to the best of my abilities the quick sort algorithm from my textbook with the median-of-three pivot selectin function - Data abstraction and problem solving, Walls and Mirrors 7th ed. It is partially working - except, the results of the elements in the array are not all in correct ascending order. For some reason after a certain sequence of numbers are placed in correct order suddenly some ints will be out order in thier own interval it seems and then it will revert to continue afterwards in correct ascending order and then repeats with some ints out of order again. I have adhered to the textbook and I have searched many other solutions and noticed some subtle differences (e.g., my pivot selection function takes 3 parameters whereas all the others I have seen take two). I have attempted to figure it out to no avail, I think it is something minor logically I cannot seem to infer from the pseudo-code in the text.
template<typename ItemType>
void sortFirstMiddleLast(ItemType theArray[], int first, int middle, int last) {
ItemType temp, temp2, temp3;
if (theArray[first] > theArray[middle]) {
temp = theArray[first];
theArray[first] = theArray[middle];
theArray[middle] = temp;
}
if (theArray[middle] > theArray[last]) {
temp2 = theArray[last];
theArray[last] = theArray[middle];
theArray[middle] = temp2;
}
if (theArray[first] > theArray[middle]) {
temp3 = theArray[first];
theArray[first] = theArray[middle];
theArray[middle] = temp3;
}
}
template<typename ItemType>
int partition(ItemType theArray[], int first, int last)
{
ItemType temp;
//Choose pivot and reposition it
int mid = first + (last - first) / 2;
sortFirstMiddleLast(theArray, first, mid, last);
//Interchange
temp = theArray[last - 1];
theArray[last - 1] = theArray[mid];
theArray[mid] = temp;
int pivotIndex = last - 1;
ItemType pivot = theArray[pivotIndex];
//Determine the regions S sub 1 and S sub 2
int indexFromLeft = first + 1;
int indexFromRight = last - 2;
bool done = false;
while (!done) {
//locate first entry on left that is >= pivot
while (theArray[indexFromLeft] < pivot)
indexFromLeft = indexFromLeft + 1;
//locate first entry on right that is <= pivot
while (theArray[indexFromRight] > pivot)
indexFromRight = indexFromRight - 1;
//now indexFromLeft has a new index subscript and indexFromRight has a new index subscript
//compare the two indexes
if (indexFromLeft < indexFromRight) {
ItemType temp2 = theArray[indexFromRight];
theArray[indexFromRight] = theArray[indexFromLeft];
theArray[indexFromLeft] = temp2;
indexFromLeft = indexFromLeft + 1;
indexFromRight = indexFromRight - 1;
}
else
done = true;
}
//Place pivot in proper position between Ssub1 and Ssub2 and mark its new location
pivot = theArray[pivotIndex];
theArray[pivotIndex] = theArray[indexFromLeft];
theArray[indexFromLeft] = pivot;
pivotIndex = indexFromLeft;
return pivotIndex;
}
template<typename ItemType>
void quickSort(ItemType theArray[], int first, int last) {
//sift out small arrays
int n = last - first + 1;
if ( n < MIN_SIZE){//array is of size < 10 so use insertion sort
insertionSort(theArray, n);
}
else {
//Make the partition : S1 | Pivot | S2
int pivotIndex = partition(theArray, first, last);
//Sort subarrays S1 and S2
quickSort(theArray, first, pivotIndex - 1);
quickSort(theArray, pivotIndex + 1, last);
}
}
const int RAND_NUMSIZE = 51; // for quick sort array size (random number gen 1-50)
const int MIN_SIZE = 10;//specify size of smallest array to use quick sort
int main()
{
int array5[RAND_NUMSIZE] = { 50, 41, 45, 43, 48, 40, 47, 42, 46, 49, 44, 39, 31, 37, 35, 33, 32, 38, 33, 34, 30, 36, 21, 29, 20, 22, 28, 23, 27, 24, 26, 25, 19, 13, 18, 14, 17, 15, 16, 12, 10, 11, 7, 8, 1, 4, 2, 6, 3, 9, 5 }
std::cout << "\nThe quick sort array before sorting: \n";
for (int i = 0; i < RAND_NUMSIZE; i++) {
std::cout << array5[i] << ' ';
}
//call quick sort
quickSort(array5, 0, RAND_NUMSIZE - 1);
std::cout << "\nThe quick sort array after sorting: \n";
for (int i = 0; i < RAND_NUMSIZE; i++) {
std::cout << array5[i] << ' ';
}
Image link displaying the results I am talking about:
quickSort console output
I think you have a problem in your quickSort() here:
if (n < MIN_SIZE) { //array is of size < 10 so use insertion sort
insertionSort(theArray, n);
}
When you have a small partition you are always insertion sorting the first n elements of theArray.
You actually want to sort the range from first to last. You can do that by passing a pointer to theArray[first] and the small partition size like this:
if (n < MIN_SIZE) { //array is of size < 10 so use insertion sort
insertionSort(theArray + first, n);
}
Of course, you want to be sure your insertion sort is correct as well...
The problem is resolved once I corrected the implementation of my base case in the quick sort function, instead of using insertion sort, I called sortFirstMiddleLast after setting the const value of MIN_SIZE to 4, thus using the pivot selection function to sort 3 entries instead of causing logic error by attempting to use partitioning and recursion for small subarrays..
template<typename ItemType>
void quickSort(ItemType theArray[], int first, int last) {
//relegate out small arrays
int n = last - first + 1;//index range of elements to consider
int middle = first + (last - first) / 2;
if ( n < MIN_SIZE){//array is of size < 4 so no partition or quick sort
sortFirstMiddleLast(theArray, first, middle, last);//base case
}
else {
//Make the partition : S1 | Pivot | S2
int pivotIndex = partition(theArray, first, last);
//Sort subarrays S1 and S2
quickSort(theArray, first, pivotIndex - 1);
quickSort(theArray, pivotIndex + 1, last);
}
}
I'm trying to implement quicksort so I can analyze its runtime on different inputs. My implementation below runs very poorly on presorted inputs (integers in ascending order).
Given the integers 1-5000, here are the runtimes for my sort:
Randomly ordered input: 0.052 seconds
Descending order input: 0.065 seconds
Ascending order input: 0.209 seconds
Given the integers 1-50000, here are the runtimes for my sort:
Randomly ordered input: 0.585 seconds
Descending order input: 1.598 seconds
Ascending order input: 6.540 seconds
I'm not sure what's causing the long runtime on presorted inputs.
int median(vector<int> *input, int left, int right) {
int mid = (right + left) / 2;
int temp;
// This method also orders the selected left, mid, and right values
if ((*input)[mid] < (*input)[left]) {
temp = (*input)[mid];
(*input)[mid] = (*input)[left];
(*input)[left] = temp;
}
if ((*input)[right] < (*input)[left]) {
temp = (*input)[left];
(*input)[left] = (*input)[right];
(*input)[right] = temp;
}
if ((*input)[mid] < (*input)[right]) {
temp = (*input)[mid];
(*input)[mid] = (*input)[right];
(*input)[right] = temp;
}
temp = (*input)[mid];
(*input)[mid] = (*input)[right-1];
(*input)[right-1] = temp;
return (*input)[right-1];
}
void quickSort2(vector<int> *input, int left, int right) {
if (left < right) {
// Get pivot (median of 3)
int pivot = median(input, left, right);
int temp;
int i = left, j = right - 1;
for (; ; ) {
while ((*input)[++i] < pivot) {}
while (j > 0 && (*input)[--j] > pivot) {}
if (i < j) {
temp = (*input)[i];
(*input)[i] = (*input)[j];
(*input)[j] = temp;
} else {
break;
}
}
temp = (*input)[i];
(*input)[i] = (*input)[right - 1];
(*input)[right - 1] = temp;
quickSort(input, left, i - 1);
quickSort(input, i + 1, right);
}
}
Because you're using the rightmost element as a pivot and when this is done in a sorted list this results in chronically bad pivot selection. This has been covered at length: geeksforgeeks.org/when-does-the-worst-case-of-quicksort-occur
Try changing your third conditional:
if ((*input)[mid] < (*input)[right]) {
to
if ((*input)[right] < (*input)[mid]) {
Otherwsie, the swap will put the rightmost (largest) element into [mid] and then you (eventually) return it as output.
Edited for clarity
I am trying to count the amount of basic operations done by Hoare's quicksort algorithm. I am wondering if I placed the counter in the correct position. My assignment is to count the number of basic operations on 100 randomly generated arrays of sizes 10,100,1k, and 10k.
Here is the algorithm with the counter placed (line 6):
void QuickSort(int* array, int startIndex, int endIndex, int &counter) {
int pivot = array[startIndex]; //pivot element is the leftmost element
int splitPoint;
if (endIndex > startIndex)
{
counter++; //counting
splitPoint = SplitArray(array, pivot, startIndex, endIndex);
array[splitPoint] = pivot;
QuickSort(array, startIndex, splitPoint - 1, counter); //Quick sort first half
QuickSort(array, splitPoint + 1, endIndex, counter); //Quick sort second half
}
}
void swap(int &a, int &b) {
int temp;
temp = a;
a = b;
b = temp;
}
int SplitArray(int* array, int pivot, int startIndex, int endIndex) {
int leftBoundary = startIndex;
int rightBoundary = endIndex;
while (leftBoundary < rightBoundary)
{
while (pivot < array[rightBoundary]
&& rightBoundary > leftBoundary)
{
rightBoundary--;
}
swap(array[leftBoundary], array[rightBoundary]);
while (pivot >= array[leftBoundary]
&& leftBoundary < rightBoundary)
{
leftBoundary++;
}
swap(array[rightBoundary], array[leftBoundary]);
}
return leftBoundary;
}
Do these results make sense?
Array[Amount] Array[10] Array[100] Array[1k] Array[10k]
MAX: 8 72 682 7122
MIN: 5 63 653 7015
AVERAGE: 6.36 66.54 667.87 7059.41
Or did I put the counter in the wrong place.
The counter is in the wrong place. Your assignment is to count basic operations. What is a basic operation in sorting? Typically we count the number of compare operations to measure complexity of sort.
We know that QuickSort is O(N Log N) on average where N is the number of items being sorted, while worst case it is O(N^2).
Your numbers are smaller than N, which is not possible since each of the N items must be compared to some other elemnt at least once, the cost of sorting cannot be less than N (otherwise at least one element was not compared to anything, so you could not guaranteed that it is sorted).
In your algorithm, the compare operation occurs when you compare the elements of the array to the pivot value. So increment your counter every time you compare an array element to Pivot. Your measured numbers should be at least N, and will typically be about N*log N, and rarely be be close to N^2.
See the suggested points below in SplitArray where to increment counter:
void QuickSort(int* array, int startIndex, int endIndex, int &counter) {
int pivot = array[startIndex]; //pivot element is the leftmost element
int splitPoint;
if (endIndex > startIndex)
{
// counter++; // Don't count here
splitPoint=SplitArray(array, pivot, startIndex, endIndex, counter);
array[splitPoint] = pivot;
QuickSort(array, startIndex, splitPoint - 1, counter); //Quick sort first half
QuickSort(array, splitPoint + 1, endIndex, counter); //Quick sort second half
}
}
No change to swap:
void swap(int &a, int &b) {
int temp;
temp = a;
a = b;
b = temp;
}
SplitArray does the comparisons, so the counter should be incremented here:
int SplitArray(int* array,int pivot,int startIndex,int endIndex,int &counter) {
int leftBoundary = startIndex;
int rightBoundary = endIndex;
while ((++counter) && (leftBoundary < rightBoundary))
{
while (pivot < array[rightBoundary]
&& rightBoundary > leftBoundary)
{
rightBoundary--;
}
swap(array[leftBoundary], array[rightBoundary]);
while ((++counter) && (pivot >= array[leftBoundary])
&& leftBoundary < rightBoundary)
{
leftBoundary++;
}
swap(array[rightBoundary], array[leftBoundary]);
}
return leftBoundary;
}