The programs should sort a set of a number using a quick sort. Use Data Sets Starting at 10,000,000 randomly generated numbers. I've been learning about quicksort. Another, I finished merge sort. However, the quicksort is syntax. I don't know why it's syntax.
public static void main(String[] args)throws Exception{
for(int n = 1; n<=100; n++)
{// begin for
int size = 10000000; //change this num to change the size of the random array 10,000,000
int[] a = new int[size];
int[] temp = new int[a.length]; //temporary array, it's empty.
//fill the array with random integers
for(int i = 0; i< a.length; i++)
a[i] = (int)(Math.random()*100000 + 1);
long startTime = System.nanoTime();
quickSort(a, temp, 0,(a.length - 1));
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.printf("%12.8f %n", (double)duration/100000000);
}// End for
}// end main
public static void quickSort(int[] a, int[] temp, int startIndex, int endIndex)
{// begin quickSort
int pivotIndex; //the index of pivot returned by the quciksort partition
if(startIndex < endIndex)
{//Begin if
pivotIndex = partition(a, temp, startIndex, endIndex);
quickSort(a, temp, startIndex, pivotIndex -1);
quickSort(a, temp, pivotIndex+1, endIndex);
}//End if
}//end quickSort
public static int partition(int[] a, int[] temp, int startIndex, int endIndex) {
int pivotIndex;
int pivot;
int midIndex = startIndex;
pivotIndex = (startIndex + endIndex) / 2;
pivot = a[pivotIndex];
swap(a, temp, pivotIndex, endIndex);
for (int i = startIndex; i < endIndex; i++) {
if (a[i] < pivot) ;
{
swap(a, temp, i, midIndex);
midIndex = midIndex + 1;
}
}
swap(a, temp, midIndex, endIndex);
return midIndex;
}// end partition
public static void swap(int[]a, int[]temp, int first, int second)
{//Begin swap
for(int i = first; i <= second; i++){
temp[i] = a[i];
}
int c;
c = a[first];
a[first] = a[second];
a[second] = c;
}//End swap
public static void writeLines(int[] a, String fileName) throws Exception
{ // Begin writeLines(int[] a, String fileName)
// create a File class object with the given file name
java.io.File out = new java.io.File(fileName);
// Create a PrintWriter output stream and link it to the File object
java.io.PrintWriter outfile = new java.io.PrintWriter(out);
// write the elements of an int array, separated by spaces
for (int i = 0; i < a.length; i++)
{ // Begin for (int i = 0; i < a.length; i++)
outfile.print(a[i] + " ");
} // End for (int i = 0; i < a.length; i++)
// print a newline at the end of the list of integers
outfile.println();
outfile.close();
} // End writeLines(int[] a, String fileName) throws Exception
}//end quickSORT
I got syntax. It said quick.partition and quick.quickSort.
In simple terms, Hoare's quicksort works by:
having an array of data
picking some middle element
logically dividing the array into 3 categories:
everything to the left of the middle element - we'll call this the lower section
the middle element
everything to the right of the middle element - we'll call this the upper section
remembering the value at the middle element
starting from the very left of the lower section looking for a value that is greater than the middle element, or in other words it tries to find something that shouldn't be in the lower section because it is too big
when it finds one it starts from the right hand side of the upper section looking for an element that is "too small for the upper section"
if values are found that meet these criteria, they're swapped over
this happens repeatedly until everything that is too big for the lower section has been transferred into the upper section and everything too small for the upper section has been transferred into the lower section
the result is a "partly sorted" array; everything to the left of the middle element is smaller than it and everything to the right of the middle element is larger than it
the algorithm starts over, this time treating just the lower section as if it was the entire array, then it does the same with the upper section
by taking the whole, then halving it, then quartering it, then... you reach a point where you're just making sure that a bunch of pairs or single elements are sorted, and then that's all done
Your algorithm uses something like a mix of the Hoare and Lomuto partitioning strategies but mostly more like Lomuto. There are various strategies for partitioning (choosing how to divide the array into sections) depending on things like how the data container works (Hoare's strategy requires containers that can be randomly accessed or accessed from each end, like a double linked list, Lomuto only needs a data container to be readable in one direction) but quicksort's basic algorithm is this notion that you split everything into two piles of "want smaller values" and "want larger values", swap things repeatedly between the piles until "smaller" and "larger" really is true, then do the process again first to the smaller pile, then the larger pile. If you keep going and dividing the work then you reach a point where everything is sorted.
If you're still struggling with it, maybe consider instead: If you think of it like sorting a large amount of words that only use the characters A and Z, into alphabetical order you might have a strategy of putting everything into piles: 2 piles of those that start with A and those that start with Z. Then you make 4 piles, those that start AA, AZ, ZA and ZZ. Then you move to 8 piles of three letters AAA, AAZ, AZA, AZZ, ZAA, ZAZ, ZZA and ZZZ (which is probably appropriate if you're asleep by now 🤪). This is also a "divide and conquer strategy"; you'll reach a point where you've divided the piles so much that they each only contain one thing, then if you gather piles up in order (and if you've been careful when you laid them out they will already be in order) then they are sorted
Oh, and your code has an error in that you've added a semicolon after an if, which creates an empty statement (makes the if useless), and you also don't seem to use the temp array you carry around. It's like this algo was hoping to be a blend of two different sort strategies but is unfinished
Related
This question already has answers here:
Write a generic method to copy an array
(2 answers)
Make copy of an array
(11 answers)
Closed last year.
This post was edited and submitted for review last year and failed to reopen the post:
Duplicate This question has been answered, is not unique, and doesn’t differentiate itself from another question.
// add elements left in the first array
while(i <= mid) {
temp[k] = Arr[i];
k += 1; i += 1;
}
// add elements left in the second array
while(j <= end) {
temp[k] = Arr[j];
k += 1; j += 1;
}
I need help figuring out what the code for a copyArray() function should be.
I'm stuck having to follow/implement a particular pseudocode, so I'm unable to use any other code solutions that make more sense.
The overall goal is to program a Merge Sort algorithm.
The catch is that apparently I'm unable to use any of the logic from algorithms I've come across searching the net, since none of the merge sort examples out there implement that function inside their sort() or merge() method that are usually part of the merge sort algorithm, and seem to just do what i need the copyArray() function to do in the last few lines of code of most merge() methods examples I've found.
Basically, I must create a copyArray() function that should receive and add an element to a temporary array that will replace the original array.
The copyArray() method should have the following signature/set of arguments/parameters
public void copyArray(int[]arrayTemp, int[] numbers, int start) {
}
I think it should do what basically the code above does, which is found in the last few lines of a usual merge/sort() method part of a typical MergeSort algorithms out there.
My best guess is that the copyArray() function should have a for-loop that iterates over the arrays replacing the elements sorted in the temp array to the original array. But I'm kind of lost now.
Does anyone have tips/ideas on how to tweak a typical iteration for loop if that's even how I should be going about it?
Here's the full code I've got so far, but it's not working correctly.
import java.util.Arrays;
public class MergeSortO {
// 1. Start from the mergeSort() method, which splits the array in two, recursively sorts both, and merges the result.
// mergeSort()
/* mergeSort(array, start, end)
if(start < end)
midPoint = (end - start) / 2 + start
mergeSort(array, start, midPoint)
mergeSort(array, midPoint + 1, start)
merge(array, start, midPoint, end)
*/
public void mergeSort(int[] numbers , int start, int end) {
if (start < end) {
int midPoint = (end - start) / 2 + start;
mergeSort(numbers, start, midPoint);
mergeSort(numbers, midPoint +1, start);
merge(numbers, start, midPoint, end); // CALLS merge()
}
}
//2. Then, implement the merge method, which merges both ends of the split array into another space.
/*
merge(array, start, middle, end) // merge()
i = start
j = middle + 1
arrayTemp = initArrayOfSize(end - start + 1)
for (k = 0 until end-start)
if (i <= middle && (j > end || array[i] <= array[j]))
arrayTemp[k] = array[i]
i++
else
arrayTemp[k] = array[j]
j++
copyArray(arrayTemp, array, start)
*/
public void merge(int[] numbers , int start, int middle, int end) { // merge()
int i = start;
int j = middle +1;
int n1 = (end - start + 1);
int[]arrayTemp = new int [n1];
for(int k = 0; k < end - start; k++) {
if ( i <= middle && (j > end || numbers[i] <= numbers[j])) {
arrayTemp[k] = numbers[i];
i++;
}
else {
arrayTemp[k] = numbers[j];
j++;
copyArray(arrayTemp, numbers, start);
}
}
}
/* 3. After the merge is done, copy the new array back in place of the input array.
*/
public void copyArray(int[] arrayTemp, int[] numbers, int start){
int len = arrayTemp.length-1;
for (int i = start; i <= start ; i+=1) {
numbers[i] = arrayTemp[i-start];
}
}
public static void main(String[] args) {
/*
1. Start from the mergeSort() method, which splits the array in two, recursively sorts both, and merges the result.
2. Then, implement the merge method, which merges both ends of the split array into another space.
3. After the merge is done, copy the new array back in place of the input array w/ copyArray().
*/
MergeSortO sort = new MergeSortO();
int[] numbers = new int[]{4,5,33,17,3,21,1,16};
int start = 0;
int end = numbers.length - 1;
int middle = (end - start) / 2 + start;
sort.mergeSort(numbers, start, end);
System.out.println(Arrays.toString(numbers));
}
}
///
Thanks in advance!
I am a little confused. I've been trying to test quicksort and everything seems to be working fine, except, when I have many duplicate array elements, I am a getting the unexpected result.
The code actually sorts the array.
The code actually sorts the array within a time frame that is
expected of quicksort.
When the array is sorted or reverse sorted I get the expected inefficiency (This, of course when the pivot is set to the first element)
I use the Random library nextInt method to fill the array.
When I decrease the data range (which consequently puts more duplicates in the array), Quick sort runs faster.
1million elements (range 0 - 1 million): 155 ms
1million elements (range 0 - 2): 118 ms
30million elements (range 0-1 million): 3085 ms
30million elements (range 0-2): 1021 ms
I tried this many times, so it doesn't seem like it's due to the randomization. The difference gets even bolder with larger data.
I couldn't find any explanation why this might occur.
Here is my code:
public void qsort2median(int array [], int lowest, int highest)
{
int pivot = array[(lowest+highest)/2];
int left = lowest;
int right = highest;
while (left <= right){
while (array[left] < pivot){
left ++;
}
while (array[right] > pivot){
right--;
}
if (left <= right){
int temp = array [left];
array[left] = array[right];
array[right] = temp;
left ++;
right--;
}
}
if (lowest < right) {
qsort2median(array, lowest, right);
}
if (left < highest) {
qsort2median(array, left, highest);
}
}
and here is how I run it:
int [] arraytest = new int [1000000];
int [] arraytest1 = new int [1000000];
Quicksort qsorttest = new Quicksort();
qsorttest.randomArrayGenerator(arraytest,2);
System.arraycopy(arraytest,0,arraytest1,0,arraytest.length);
int endpoint = arraytest.length-1;
//RUN FIRST ARRAY
long startTime = System.currentTimeMillis();
qsorttest.qsort2median(arraytest,0,endpoint);
long endTime = System.currentTimeMillis();
long duration = (endTime - startTime);
System.out.println("Quicksort with Median Element pivot ran in: " + duration);
and here is my randomArrayGenerator method:
void randomArrayGenerator(int [] array, int n){
Random generator = new Random();
System.out.println();
System.out.println(array.length);
for (int i = 0; i < array.length; i++){
array[i]= generator.nextInt(n);
}
}
Thanks for any input.
As of Monday, I am still trying to figure this out.
I guess I am allowed to answer my own question.
Upon further research, I discovered that duplicates only become a problem when Quicksort algorithm uses Lomuto's partitioning. This, on the other hand, uses Hoare's partitioning.
I tested both of them and got the expected result.
So Lomuto's partitioning gets slower with increased duplicates and Hoare's algorithm gets faster with duplicates.
I was trying to solve following programming exercise from some java programming book
Write method that partitions the array using the first element, called a pivot. After the partition, the elements in the list are rearranged so that all the elements before the pivot are less than or equal to the pivot and the elements after the pivot are greater than the pivot. The method returns the index where the pivot is located in the new list. For example, suppose the list is {5, 2, 9, 3, 6, 8}. After the partition, the list becomes {3, 2, 5, 9, 6, 8}. Implement the method in a way that takes at most array.length comparisons.
I've implemented solution, but it takes much more than array.length comparisons.
The book itself has solution, but unfortunately it's just plain wrong (not working with some inputs). I've seen the answer to this similar question, and understood "conquer" part of Quicksort algorithm, but in this algorithm values are partitioned using mid-value, but in my case using of 1st array value as a pivot is required.
This is the pivot routine from the linked answer (adapted from source here).
int split(int a[], int lo, int hi) {
// pivot element x starting at lo; better strategies exist
int x=a[lo];
// partition
int i=lo, j=hi;
while (i<=j) {
while (a[i]<x) i++;
while (a[j]>x) j--;
if (i<=j) swap(a[i++], a[j--]);
}
// return new position of pivot
return i;
}
The number of inter-element comparisons in this algorithm is either n or n+1; because in each main loop iteration, i and j move closer together by at exactly c units, where c is the number of comparisons performed in each of the inner while loops. Look at those inner loops - when they return true, i and j move closer by 1 unit. And if they return false, then, at the end of the main loop, i and j will move closer by 2 units because of the swap.
This split() is readable and short, but it also has a very bad worst-case (namely, the pivot ending at either end; follow the first link to see it worked out). This will happen if the array is already sorted either forwards or backwards, which is actually very frequent. That is why other pivot positions are better: if you choose x=a[lo+hi/2], worst-case will be less common. Even better is to do like Java, and spend some time looking for a good pivot to steer clear from the worst case. If you follow the Java link, you will see a much more sophisticated pivot routine that avoids doing extra work when there are many duplicate elements.
It seem that the algorithm (as taken from "Introduction to algorihtm 3rd ed") can be implemented as follows (C++) should be similar in Java`s generics:
template <typename T> void swap_in_place(T* arr, int a, int b)
{
T tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
template <typename T> int partition(T* arr, int l, int r)
{
T pivot = arr[r];
int i = l-1;
int j;
for(j=l; j < r; j++) {
if (arr[j] < pivot /* or cmp callback */) {
// preincrement is needed to move the element
swap_in_place<T>(arr, ++i, j);
}
}
// reposition the pivot
swap_in_place(arr, ++i, j);
return i;
}
template <typename T> void qsort(T* arr, int l, int r)
{
if ( l < r ) {
T x = partition<T>(arr, l, r);
qsort(arr, l, x-1);
qsort(arr, x+1, r);
}
}
However, its a simple pseudocode implementation, I dont know if it`s the best pivot to pick from. Maybe (l+r)/2 would be more proper.
Pretty simple solution with deque:
int [] arr = {3, 2, 5, 9, 6, 8};
Deque<Integer> q = new LinkedBlockingDeque<Integer>();
for (int t = 0; t < arr.length; t++) {
if (t == 0) {
q.add(arr[t]);
continue;
}
if (arr[t] <= arr[0])
q.addFirst(arr[t]);
else
q.addLast(arr[t]);
}
for (int t:q) {
System.out.println(t);
}
Output is:
2
3
5 <-- pivot
9
6
8
There is video that I made on Pivot based partition I explained both the methods of patitioning.
https://www.youtube.com/watch?v=356Bffvh1dA
And based on your(the other) approach
https://www.youtube.com/watch?v=Hs29iYlY6Q4
And for the code. This is a code I wrote for pivot being the first element and it takes O(n) Comparisons.
void quicksort(int a[],int l,int n)
{
int j,temp;
if(l+1 < n)
{
int p=l;
j=l+1;
for(int i=l+1;i<n;++i)
{
if(a[i]<a[p])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
j++;
}
}
temp=a[j-1];
a[j-1]=a[p];
a[p]=temp;
quicksort(a,l,j);
quicksort(a,j,n);
}
}
The partition function below works as follow:
The last variable points to the last element in the array that has not been compared to the pivot element and can be swapped.
If the element directly next to the pivot element is less than the pivot
element. They are swapped.
Else if the pivot element is less than the next element, the nextelement is swapped with the element whose index is the last variable.
static int partition(int[] a){
int pivot = a[0];
int temp, index = 0;
int last = a.length -1;
for(int i = 1; i < a.length; i++){
//If pivot > current element, swap elements
if( a[i] <= pivot){
temp = a[i];
a[i] = pivot;
a[i-1] = temp;
index = i;
}
//If pivot < current elmt, swap current elmt and last > index of pivot
else if( a[i] > pivot && last > i){
temp = a[i];
a[i] = a[last];
a[last] = temp;
last -= 1;
i--;
}
else
break;
}
return index;
}
I am trying to implement Quick Sort algorithm, here's my code:
public class Sort {
int count = 0;
public void Partition(int A[], int l, int h) {
if ((h-l) > 1) {
count += (h-l) - 1;
int pivot = A[l];
int i = l+1;
int temp;
int j;
for (j = l + 1; j < h; j++) {
if (A[j] < pivot) { // SWAP
temp = A[i];
A[i] = A[j];
A[j] = temp;
i++;
}
// else : j++
}
temp = A[i-1];
A[i-1] = A[l];
A[l] = temp;
Partition(A, l, i-1);
Partition(A, i, A.length);
}
}
}
the code does sort the input array, but when i count the number of comparisons, it gives a number, much greater than the original number of the comparisons. I added a break point and moved a step by step into the code, and found that the problem lies in the last two lines:
Partition(A, l, i-1);
Partition(A,i, A,length);
The 'i' sent in the 2nd call, is the i resulting from the 1st call to the function Partition, and not the 'i' from the very first code. for example, the first time the code runs on the following input:
3 8 4 6 10 2 5 7 1
the output is: 1 2 3 4 6 10 9 5 7 8
and i = 3.
then the first call to Partition takes i (where i equals 3) and keeps changing the i's value, when it's finally done, the i's value is different than 3, and another wrong value is sent to the 2nd recursive call.
My question is, is there a way, so that the two calls take the same parameter i, without anyone changing it ?
Try this. Don't try to do the sort in Partition; from there, just return the index i (you have to change the return type to int, of course). Then write a different method to implement your quicksort.
public static void quicksort(int[] n, int left, int right){
if (left<right){
int pivotindex=Partition(n, left, right);
quicksort(n, left, pivotindex-1);
quicksort(n, pivotindex+1, right);}
}
I tested this with the following, and it worked perfectly.
public static void main(String[] args){
int[] n= new int[8];
n[0]=3;
n[6]=2;
n[1]=5;
n[3]=20;
quicksort(n, 0, n.length);
for (int i=0; i<n.length; i++){
System.out.print(n[i]+",");
}
}
Since Java uses pass-by-value, there is no way the first call can change the value of the parameter i, since the called method has no reference to it.
You are probably expecting the second call to Partition to be the next one. However, the first call will in turn call Partition twice more, causing the parameter values of the next execution of Partition to be different than you might expect.
Also, please start your method names with a lower-case letter.
I implemented a simple quick sort (code below) to count the average and worst comparison made by quicksort. I declared a global variable to hold the counter for comparisons. I placed 3 counters in different positions that I thought would count the comparisons, the problem is the counter sum does not match the theoretical value of the total number of comparisons made by quick sort. I tried to solve this problem for hours but came up short. I really appreciate if you can point me where I should put the counters and why I should place them there. I assumed a counter should go where ever a comparison is made. apparently I'm wrong.
public int[] quickSort(int[] array, int start, int end){
if (start < end){
counter++;//1st comparison here
int pivot;
pivot = Partition(array, start, end);
quickSort(array, start, pivot - 1);
quickSort(array, pivot + 1, end );
}
return array;
}
private int Partition(int[] array, int start, int end) {
int pivot = array[end];
int i = start - 1;
for(int j = start; j <= end - 1; j++){
counter++;//2nd comparison here
if (array[j] <= pivot){
counter++;//3rd comparison here
i = i + 1;
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
int temp = array[i+1];
array[i+1] = array[end];
array[end] = temp;
return i + 1;
}
For the theory, only the comparisons of array elements are counted, not the comparisons of indices to the bounds, so you should only leave the second counter++; (you need to increment the counter independently of the result of the comparison).
Then there is the question against which theoretical values you compare. There are different implementations of quicksort which use slightly different numbers of comparisons. In particular, your choice of the pivot makes no attempt to avoid extreme values, so this implementation will easily degrade to O(n^2) behaviour.