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;
}
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 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;
}
I need to insert two values num1 = 50 and num2 = 80 into an array which has been sorted into ascending order. I cannot use dynamic arrays or lists. Also no structs or classes. This is for a class assignment so I must follow the guidlines. The professor suggested that I should create a new array, newarray and copy the values from my orignal numarray until I reach a condition which would prompt the numbers to be inserted where they would fit in ascending order. I set my loop up to run as many times as it should take to fill up my newarray and I set up the conditionals to check if the previous number in the numarray is less than num and if the next value is greater than num. The numbers get inserted at the correct locations, however, inserting the new value overwrites the value that would be there.
I thought that
newarray[newindex] = num1;
newarray[newindex+1] = numarray[newindex]; would write the inserted value into the current index, and then write numarray[newindex] into the index after it.
I will attach the function below. Everything is self contained in the function except numarray which is just sorted values up to 100. Thanks in advance. Maybe walking away for a while will help me figure it out.
void insertArray(int* numarray) {
int num1 = 50;
int num2 = 80;
int counter = 0;
int newarray[103] = {};
for (int newindex = 0; newindex < 103; newindex++) {
if (numarray[newindex] <= num1 && numarray[newindex+1] > num1) {
newarray[newindex] = num1;
newarray[newindex+1] = numarray[newindex];
}
else if (numarray[newindex] <= num2 && numarray[newindex+1] > num2) {
newarray[newindex] = num2;
newarray[newindex+1] = numarray[newindex];
}
else {
newarray[newindex] = numarray[newindex];
}
cout << newarray[newindex] << endl;
}
}
int main() {
int numarray[100] = {};
randomgenerator();
read(numarray);
printArray(numarray);
searchArray(numarray);
Delete(numarray);
sortArray(numarray);
insertArray(numarray);
return 0;
}
UPDATE:
After input for you guys, I tried the function suggested, and it successfully inserts two values. It was inserting 0 instead of 50, and it inserts 78 instead of 80. I got it to insert 50, but I don't understand what condition is causing it to insert 78, or what would be 80. I tried writing it in the same format as the for loop to insert 50, but it doesn't work.
void insertArray(int* numarray) {
int num1 = 50;
int num2 = 80;
int index = 0;
int newarray[102] = {};
for (; index < 100 && numarray[index] < num1; ++index) {
newarray[index] = numarray[index];
}
newarray[index++] = num1;
for (; index < 101 && numarray[index - 1] < num2; ++index) {
newarray[index] = numarray[index - 1];
}
if (index == 102) {
newarray[index++] = num2;
}
for (; index < 102; ++index) {
newarray[index] = numarray[index - 2];
}
for (int i = 0; i <= 101; i++) {
cout << newarray[i] << endl;
}
}
In my opinion the approach in general is wrong.
The new array should be declared in the function main where it should be outputted after inserting a number.
The function should not insert two numbers at once. It should insert only one number but called as many times as new numbers should be inserted in an array.
The function might be called to insert a value when the destination and source arrays are the same one array.
So I am suggesting the following approach shown in the demonstrative program below.
#include <iostream>
void insert( const int *a1, size_t n, int *a2, int value )
{
const int *p = a1 + n;
a2 += n;
while ( ( p != a1 ) && ( value < *( p - 1 ) ) ) *a2-- = *--p;
*a2-- = value;
while ( p != a1 ) *a2-- = *--p;
}
int main()
{
int a1[] = { 5, 15, 25, 35, 45, 55, 65, 75, 85, 95 };
const size_t N = sizeof( a1 ) / sizeof( *a1 );
int a2[N + 2];
insert( a1, N, a2, 50 );
for ( size_t i = 0; i < N + 1; i++ )
{
std::cout << a2[i] << ' ';
}
std::cout << '\n';
insert( a2, N + 1, a2, 80 );
for ( size_t i = 0; i < N + 2; i++ )
{
std::cout << a2[i] << ' ';
}
std::cout << '\n';
return 0;
}
The program output is
5 15 25 35 45 50 55 65 75 85 95
5 15 25 35 45 50 55 65 75 80 85 95
As for your code then for example this loop
for (int newindex = 0; newindex < 103; newindex++) {
if (numarray[newindex] <= num1 && numarray[newindex+1] > num1) {
//...
invokes undefined behavior because the array numarray does not have elements with indices 100, 101, and 102.
Also in general it can occur such away that the source array does not have an element that is greater than num1 or num2.
Another approach is to place the inserted values in a separate array and then combain the source array and the array of the inserted values in the destination array.
Here is a demonstrative program.
#include <iostream>
void insert( const int *a1, size_t n1, const int *a2, size_t n2, int *result )
{
const int *p1 = a1;
const int *p2 = a2;
while ( p1 != a1 + n1 && p2 != a2 + n2 )
{
if ( *p2 < *p1 )
{
*result++ = *p2++;
}
else
{
*result++ = *p1++;
}
}
while ( p1 != a1 + n1 ) *result++ = *p1++;
while ( p2 != a2 + n2 ) *result++ = *p2++;
}
int main()
{
int a1[] = { 5, 15, 25, 35, 45, 55, 65, 75, 85, 95 };
const size_t N1 = sizeof( a1 ) / sizeof( *a1 );
int a2[] = { 50, 80 };
const size_t N2 = sizeof( a2 ) / sizeof( *a2 );
int result[N1 + N2];
insert( a1, N1, a2, N2, result );
for ( int item : result )
{
std::cout << item << ' ';
}
std::cout << '\n';
return 0;
}
Its output is
5 15 25 35 45 50 55 65 75 80 85 95
I agree whole heartedly with #Vlad from Moscow that your new array should be declared in the caller and passed as a parameter to your insert function. This is mandatory if you are using a POA (plain old array) as a new POA declared within your insert function ceases to exist when your function returns. You can always allocate storage for your array within the insert function and return a pointer to the allocated block, but even then, if you want to keey your insert function type as void, you may as well declare it in the caller and pass it as a parameter.
There are a number of ways to do an insertion into a new array in sorted order, and all are fine for small arrays (say 1000 or so elements). However, since your original array is already sorted, for larger arrays, taking advantage of that fact you can write a routine that is orders of magnitude more efficient. An efficient approach using the sorted array is similar to a bsearch.
There you simply compare your value to be inserted with the middle-element in the array. If it is greater than the first element of your array and smaller than then middle element, you know you will insert it in the first 1/2 of the original array. You can copy the entire second 1/2 of the array to the middle-element + 1 of the new array at that point and repeat.
If the value is greater than the middle element, you can copy the entire first 1/2 of the original to the beginning of your new array and continue your search in the second 1/2 of your array.
When your value is less than or equal to the first element in the subarray, you have found the insertion point and can set the value in the new array to the value to be inserted copying the remainder of the subarray to your new array beginning at the next index.
Similarly, if your value is greater than the end value of the subarray, you can insert the value at the end of range in your new array to the value and then copy the remaining value from the original to the beginning of the range in your new array.
What this does is dramatically reduce your worst case number of iterations required to find the insertion point and allows you to use a much more efficient copy to fill in the elements of the new array above and below the inserted value. For example with a 1,000,000 element array, if you start your iteration at the end and work back toward the beginning and your actual new element is the 1st element in the array, you will iterate 1,000,000 times to insert the new value and copy the remaining elements from your original to the new array.
Contrast that with the method described above where you are effectively bisecting the original into subarrays, your worst case scenario is 20-iterations max to find the insertion point, and 2-copies to copy the elements below and above the inserted value.
While you can do it all in one function, it helps to split the function into two-separate functions. One to find the insertion position for the new element which will make recursive calls (say call it findinspos which does not need the new array as a parameter at all), and the second function (say insinsorted that calls findinspos and then copies the remaining elements)
They could be implemented as:
/* returns index where v should be inserted in a for a given
* start index and an array of nelem elements.
*/
int findinspos (const int *a, int start, int nelem, int v)
{
int mid = (start + nelem) / 2;
if (v <= a[start]) /* exit conditon ins at start */
return start;
else if (a[nelem - 1] < v) /* exit condition ins at end */
return nelem;
else if (v < a[mid]) /* v in 1st 1/2 subarray */
return findinspos (a, start, mid, v);
else /* v in 2nd 1/2 subarray */
return findinspos (a, mid, nelem, v);
}
/* inserts v in sorted positon within the nelem elements of a with
* the results stored in b.
*/
void insinsorted (const int *a, int nelem, int v, int *b)
{
int inspos = findinspos (a, 0, nelem, v); /* get insert positon */
b[inspos] = v; /* set value at inspos in new array */
if (inspos == 0) /* if ins at start copy a beginning at next element */
memcpy (b + 1, a, nelem * sizeof *b);
else if (inspos == nelem) /* if at end, copy a to b */
memcpy (b, a, nelem * sizeof *b);
else { /* otherwise, copy begin and end of a to b */
memcpy (b, a, inspos * sizeof *b);
memcpy (b + inspos + 1, a + inspos, (nelem - inspos) * sizeof *b);
}
}
A complete test program (that borrows the test array from Vlad from Moscow), and lets you enter any number you would like to insert as the 1st argument to the program (default: 4 if no arguments entered) could be:
#include <iostream>
#include <cstring>
/* returns index where v should be inserted in a for a given
* start index and an array of nelem elements.
*/
int findinspos (const int *a, int start, int nelem, int v)
{
int mid = (start + nelem) / 2;
if (v <= a[start]) /* exit conditon ins at start */
return start;
else if (a[nelem - 1] < v) /* exit condition ins at end */
return nelem;
else if (v < a[mid]) /* v in 1st 1/2 subarray */
return findinspos (a, start, mid, v);
else /* v in 2nd 1/2 subarray */
return findinspos (a, mid, nelem, v);
}
/* inserts v in sorted positon within the nelem elements of a with
* the results stored in b.
*/
void insinsorted (const int *a, int nelem, int v, int *b)
{
int inspos = findinspos (a, 0, nelem, v); /* get insert positon */
b[inspos] = v; /* set value at inspos in new array */
if (inspos == 0) /* if ins at start copy a beginning at next element */
memcpy (b + 1, a, nelem * sizeof *b);
else if (inspos == nelem) /* if at end, copy a to b */
memcpy (b, a, nelem * sizeof *b);
else { /* otherwise, copy begin and end of a to b */
memcpy (b, a, inspos * sizeof *b);
memcpy (b + inspos + 1, a + inspos, (nelem - inspos) * sizeof *b);
}
}
int main (int argc, char **argv) {
int a[] = { 5, 15, 25, 35, 45, 55, 65, 75, 85, 95 },
nelem = sizeof a / sizeof *a,
*b = new int[nelem+1] {},
v = argc > 1 ? strtol (argv[1], NULL, 0) : 4;
for (int i = 0; i < nelem; i++)
std::cout << " " << a[i];
std::cout << '\n';
insinsorted (a, nelem, v, b);
for (int i = 0; i < nelem + 1; i++)
std::cout << " " << b[i];
std::cout << '\n';
delete[] b;
}
(note: the array b is allocated with new, if that is outside your assignment, just replace it with an array with enough storage to hold the original plus the new elements -- and don't forget to remove delete[] b; from the end)
Like I began with, for small arrays -- how you do it really doesn't matter, but for larger arrays, taking advantage of the fact the original array is already sorted can provide orders of magnitude improvements in efficiency.
The professor suggested that I should create a new array, newarray and copy the values from my orignal numarray until I reach a condition which would prompt the numbers to be inserted where they would fit in ascending order.
So let's do exactly that.
void copyAndInsert(int* numarray, int size, int* newarray, int num1, int num2)
{
if (num1 > num2) { std::swap(num1, num2); } // ensure we write the smaller number first
int * num1pos = std::lower_bound(numarray, numarray + size, num1); // find where to put num1
int * new1pos = std::copy(numarray, num1pos, newarray); // copy the numbers less than num1
*new1pos++ = num1; // copy num1 and move past it
int * num2pos = std::lower_bound(num1pos, numarray + size, num2); // find where to put num2
int * new2pos = std::copy(num1pos, num2pos, new1pos); // copy the numbers between num1 and num2
*new2pos++ = num2; // copy num2 and move past it
std::copy(num2pos, numarray + size, new2pos); // copy the numbers greater than num2
}
int main() {
int numarray[100] = {};
int newarray[102] = {};
randomgenerator();
read(numarray);
printArray(numarray);
searchArray(numarray);
Delete(numarray);
sortArray(numarray);
copyAndInsert(numarray, 100, newarray, 50, 80);
return 0;
}
If you can't use things from the standard header <algorithm>, you can implement them as follows (adapted from cppreference)
int* lower_bound(int* first, int* last, int value)
{
int count = last - first;
while (count > 0) {
int step = count / 2;
int* it = first + step;
if (*it < value) {
first = ++it;
count -= step + 1;
}
else {
count = step;
}
}
return first;
}
int* copy(int* first, int* last, int* d_first)
{
while (first != last) {
*d_first++ = *first++;
}
return d_first;
}
I suggest to use std::merge from the c++ standard library. It merges two sorted ranges into one sorted range. As numarray is already sorted, the only thing we need to do is to make sure that the numbers we are adding are sorted as well.
#include<algorithm>
void insertArray(int* numarray) {
int num1 = 50;
int num2 = 80;
std::vector<int> numbersToAdd{num1, num2};
//make sure numbersToAdd is sorted
std::sort(begin(numbersToAdd), end(numbersToAdd));
int newarray[102] = {};
std::merge(numarray, numarray + 100, begin(numbersToAdd), end(numbersToAdd), newarray);
}
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 am still a beginner at programming and i am taking an online course (algorithms)
one of the practice questions was to count the number of inversions in a file containing 100000 numbers randomly ordered. I have tried this code on small data sets and it worked fine but when passing the actual data set it gives inversion count in negative number. Tried various solutions from different platforms but still couldn't resolve it yet.
so this is my code
#include "stdafx.h"
#include <iostream>;
#include <conio.h>:
#include <fstream>
using namespace std;
long merge(int a[], int start, int mid, int end)
int i = start;
int j = mid + 1;
int k = start;
int inversion=0;
int temp[100000];
while (i <= mid && j <= end)
{
if (a[i] < a[j])
{
temp[k++] = a[i++];
}
else
{
temp[k++] = a[j++];
inversion =inversion + (mid - i);
}
}
while (i <= mid)
{
temp[k++] = a[i++];
}
while (j <= end)
{
temp[k++] = a[j++];
}
for (int i = start; i <= end; i++)
{
a[i] = temp[i];
}
return inversion;
long Msort(int a[], int start,int end)
{
if (start >= end)
{
return 0;
}
int inversion = 0;
int mid = (start + end) / 2;
inversion += Msort(a, start, mid);
inversion += Msort(a, mid + 1, end);
inversion += merge(a, start, mid, end)
return inversion;
}
long ReadFromFile(char FileName[], int storage[],int n)
{
int b;
int count=0;
ifstream get(FileName);
if (!get)
{
cout << "no file found";
}
while (!get.eof())
{
get >> storage[count];
count++;
}
b = count;
return b;
}
int main()
{
int valuescount = 0;
int arr[100000];
char filename[] = { "file.txt" };
long n = sizeof(arr) / sizeof(arr[0]);
valuescount=ReadFromFile(filename, arr,n);
int no_Of_Inversions = Msort(arr, 0, valuescount -1);
cout << endl << "No of inversions are" << '\t' << no_Of_Inversions <<'\t';
cout <<endl<< "Total no of array values sorted"<< valuescount<<endl;
system("pause");
}
`
The issue with your code is not directly related to the input size. Rather, in an indirect way, the negative number of inversions you find is the result of an overflow in the variable inversion of the function merge.
Consider the case for your input size N = 100000. If this array of numbers is sorted in decreasing order, then all the ordered pairs in that array will be an inversion. In other words, there will be N * (N-1) / 2 inversions to be counted. As you may have noticed, that value is slightly higher than the bounds of unsigned int type. Consequently, when you try and count this value in a variable of type int, overflow occurs, leading to a negative result.
To remedy this issue, you should change the type of the variable inversion from int to long long, in functions merge and Msort. (You should also update the return type of the functions merge and Msort) Naturally, you should assign the return value of the Msort call in the main function to a variable of type long long as well. In other words, change the type of variable no_Of_Inversions into a long long as well.
I am trying to get all set of permutation of any array taken certain number of element at a time for example if array = {1,2,3,4} and r=3 then possible permutation will be 24. Here is my implementation using recursion but this is not giving expected result.
void permutationUtil(vector<int> arr, vector<int> data, int start, int end, int index, int r) {
// Current permutation is ready to be printed, print it
if (index == r){
for (int j=0; j<r; j++)
printf("%d ", data[j]);
printf("\n");
return;
}
// replace index with all possible elements. The condition
// "end-i+1 >= r-index" makes sure that including one element
// at index will make a permutation with remaining elements
// at remaining positions
for (int i = start; i <= end && end - i + 1 >= r - index; i++) {
data[index] = arr[i];
permutationUtil(arr, data, i + 1, end, index + 1, r);
}
}
void printPermutation(vector<int> arr, int n, int r) {
// A temporary array to store all permutation one by one
vector<int> data(n);
// Print all permutation using temprary array 'data[]'
permutationUtil(arr, data, 0, n - 1, 0, r);
}
You may do it with 2 loop with std::next_permutation:
void permutationUtilInner(std::vector<int> v,
std::function<void (const std::vector<int>&)> f)
{
do {
f(v);
} while (std::next_permutation(v.begin(), v.end()));
}
void permutationUtil(std::vector<int> v,
std::size_t r,
std::function<void (const std::vector<int>&)> f)
{
// remainder: range should be sorted for std::next_permutation
std::vector<bool> b(v.size() - r, false);
b.resize(v.size(), true);
std::sort(v.begin(), v.end());
do {
std::vector<int> sub;
for (std::size_t i = 0; i != b.size(); ++i) {
if (b[i]) {
sub.push_back(v[i]);
}
}
permutationUtilInner(sub, f);
} while (std::next_permutation(b.begin(), b.end()));
}
Demo
This is Delphi recursive implementation:
procedure Arrangement(var A: array of Integer; n, k: Integer; s: string);
var
i, t: Integer;
begin
if k = 0 then
Output(s)
else
for i := 0 to n - 1 do begin
t := A[i];
A[i] := A[n - 1]; //store used item in the tail
Arrangement(A, n - 1, k - 1, s + IntToStr(t) + ' '); //recursion without tail
A[i] := t; //get it back
end;
end;
Your algorithm is incomplete. It is always increasing. Cases like 2,1,3 that require non-increasing sequence are not covered.
In second for loop, change int i=start to int i=0 to fix the problem.