Recursive Partition Sort is Acting Ineffecient - java

I have written a recursive method for a partition sort that sorts the array however when I use an array of more than 10-20 elements the program takes a really long time to complete (On my computer a bubble sort of a 100,000 int array will take about 15-20 seconds but with an array of only 30 ints my partition sort is taking around 45 seconds to be sorted.
Here is the code.
public static int[] partitionSortRecursive(int[] array, int beginning, int end)
{
if (end < beginning)
return array;
int pivot = (array[beginning] + array[end]) / 2;
int firstUnknown = beginning;
int lastS1 = beginning - 1;
int firstS3 = end + 1;
while (firstUnknown < firstS3)
{
if (array[firstUnknown] == pivot)
{
firstUnknown++;
}
else if (array[firstUnknown] > pivot)
{
firstS3--;
int temp = array[firstUnknown];
array[firstUnknown] = array[firstS3];
array[firstS3] = temp;
}
else
{
lastS1++;
int temp = array[firstUnknown];
array[firstUnknown] = array[lastS1];
array[lastS1] = temp;
firstUnknown++;
}
}
partitionSortRecursive(array, 0, lastS1);
partitionSortRecursive(array, firstS3, end);
return array;
}

You do not use the correct pivot element. You calculate the average of the left and right value but you have to take a sample value from the sub array to partition instead.
You may take the rightmost, the center or any other element. So your first line of codes should look like this
int pivot = array[(beginning + end) / 2];
// or
int pivot = array[end];
You could also take any other element (e.g. random)
EDIT: This does not solve the performance issue.
To my understanding, quick sort will divide an array into two sub arrays A and B where all elements in A are smaller than any element in B and then perform the same operation onto the two sub arrays.
So the basic call structure should be like this
void DoSort (array, i, j)
{
pivot = Partition (array, i, j)
DoSort (array, i,pivot)
DoSort (array, pivot + 1, j)
}
Put your implementation is basically
void DoSort (array, i, j)
{
pivot = Partition (array, i, j)
DoSort (array, 0, pivot) // <<<<<< notice the '0' instead of 'i'
DoSort (array, pivot + 1, j)
}
So you always start from the very beginning of the original array which will most likely take a while

Instead of direct recoursive call like this
partitionSortRecursive(array, 0, lastS1);
partitionSortRecursive(array, firstS3, end);
Organize internal stack where you can save index pairs. While the stack is not empty get the next pair from the stack. In the end of function don't call the same function but put in the stack 2 pairs (0, lastS1) and (firstS3, end)

Related

sort array using recursive method

I am working on trying to write a program where a user will enter 6 strings and then it will sort the array in reverse alphabetical order using a recursive method. This is one concept I do not understand despite multiple videos, readings and attempts. Any support and insight is greatly appreciated. Thank you.
import java.util.Arrays;
import java.util.Scanner;
public class SRecusion {
public static void sort2 (String[] sort2) {
int i;
int min = 0;
int max;
for (i = 0; i <sort2.length -1; i++) {
if (sort2[i].charAt(0)> sort2[i=1].charAt(0)) {
sort2[i] = sort2[min];
}
else {
min = (sort2(sort2[i-1]));
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String [] test = new String[6];
Scanner scnr = new Scanner(System.in);
String userEntry = "";
for(int i = 0; i <= test.length - 1; i++) {
System.out.println("Please enter a word:");
test[i] = scnr.nextLine();
}
sort2(test);
System.out.println("your list is" + Arrays.asList(test));
System.out.println();
}
}
Sorting is a pretty broad topic as there are many different sorting methods (quicksort, merge sort, etc.) However, a pretty basic and simple sorting method is bubble sort. Although it isn't the fastest one, it's pretty easy to understand and code using recursion.
Essentially, bubble sort with iterate through the elements in pairs of 2 and swap the two elements if they're in the wrong order.
For example, let's sort (3, 2, 5, 4, 1) using bubble sort.
(2, 3, 5, 4, 1) First, it'll look at the first two elements swap them if needed. Since 3 is greater than 2, it'll swap them.
(2, 3, 5, 4, 1) Next, it'll look at 3 and 5. Since 3 is less than 5, there is no need to swap
(2, 3, 4, 5, 1) It now looks at 5 and 4 and swaps them.
(2, 3, 4, 1, 5) Finally, it looks at 5 and 1 and swaps them.
Now start from the beginning and repeat the whole process. The sorting ends if exactly 0 swaps are made during an iteration.
If you're still a bit confused, try watching a tutorial on bubble sort or visit this link.
So from what I was asking above as to why you need a recursive sorting algorithm Here it goes I will try to explain how recursive sorting works. It took my some time to figure it out as I am sure it does for most people who first come in contact with it.
public static void Qsort(int[] array, int start, int end)
{
//find the current center of the whole or parital array part I am working on.
int center = (start+end)/2;
///System.out.println("\n This is the center : " + center);
int pivot, i, pivotplace;
i = 0;
pivot = 0;
pivotplace = 0;
//if start = end then we are at a single element. just return to the previous iterative call.
if(start == end)
{
// System.out.println("\n Inside base case return :");
return;
}
//find the pivot value we are using. using a 3 prong selection we are assured to at least get some type of median value and avoid the N^2 worst case.
pivot = getpivot(array[start], array[center], array[end]); //gets median value of start, center and end values in the array.
// System.out.println("\n pivotvalue is : " + pivot);
//find where the current pivot is located and swap it with the last element in the current portion of the array.
if(array[start] == pivot)
{
//System.out.print("\n Inside pivot at start");
swap(array, start, end);
}
else
{
if(array[center] == pivot)
{
//System.out.print("\n Inside pivot at center");
swap(array, center, end);
}
}
//due to iteration the pivot place needs to start at the passed in value of 'start' and not 0.
pivotplace = start;
//due to iteration the loop needs to go from the passed in value of start and not 0 and needs to go
//until it reaches the end value passed in.
for(i = start; i < end; i++)
{
//if the current slot of the array is less than then pivot swap it with the current pivotplace holder
//since the pivotplace keeps getting iterated up be each swap the final place of pivot place
//is where the pivot will actually be swapped back to after the loop cpompletes.
if(array[i] < pivot)
{
//System.out.print("\n Swapping");
swap(array, i, pivotplace);
pivotplace++;
}
}
//loop is finished, swap the pivot into the spot it belongs in.
swap(array, pivotplace, end);
//there are 2 cases for recursive iteration.
//The first is from the start to the slot before the pivot
if(start < pivotplace){Qsort(array, start, pivotplace-1);}
//the second is from the slot after the pivot to the end.
if(pivotplace+1 < end){Qsort(array, pivotplace+1, end);}
}
public static int getpivot(int a, int b, int c)
{
if((a > b) && (a < c))
{
return a;
}
if((b > a) && (b < c))
{
return b;
}
return c;
}
public static void swap(int[] array, int posa, int posb)
{
int temp;
temp = array[posa];
array[posa] = array[posb];
array[posb] = temp;
}
This is a basic Quick Sort or recursive sort I wrote this while in programming classes. You will probably not need to use the getpivot code as you are dealing with a small set of strings, but if you do some research you will see using a possible sample of 3 drastically speeds up the recursion due to balanced work load of the recursion tree.
Sort Array using recursion in kotlin
fun main() {
print(sortArray(arrayListOf(1,3,2,6,8,3)))
}
fun sortArray(arr: MutableList<Int>): MutableList<Int>{
if(arr.size==1) {
return arr
}
val lastValue = arr.last()
arr.removeLast()
sortArray(arr)
insert(arr, lastValue)
return arr
}
fun insert (arr: MutableList<Int>, value: Int): MutableList<Int> {
if(arr.size == 0 || arr.last() < value) {
arr.add(value)
return arr
}
val lastValue = arr.last()
arr.removeLast()
insert(arr, value)
arr.add(lastValue)
return arr
}

Recursive Selection sort Java

I've been looking for a recursive selection sort, using only 2 parameters:
The array that has to be sorted
a value k, which indicates till which
element it has to be sorted.
Example: SelectionSort(array[] a, int k) with a being {6,3,5,7,2} and k being 2 will sort the first 3 elements, and will keep the last elements untouched.
I was thinking about starting with an if-statement for k being 0, and if that was the case, it would just return the array as it is, since you cant sort an array of size 1.
Something like:
public int[] sort(int[] a){
a = selectionSort(a, n-1);
return a;
}
public int[] selectionSort(int[] a, int k){
if (k = 0){
return a;
}
else{
selectionSort(a, k-1 );
... (part i really don't know)
}
I have no clue how to do the 'else' part since I only know that it has to call the method again.
I'm not allowed to create other methods. I also need to make sure I use exactly 2 parameters, nothing more, nothing less.
I have to work it out in pseudocode, but I understand some Java, so if someone could help me by using either pseudo, or Java, it would be so helpful
First some remarks to your code:
Your methods sort and selectionSort don't need to return an int[] array,
since the array object a stays the same all the time.
It is only the content within this array which changes.
Hence, you can use void as return-type.
In your if use (k == 0) instead of (k = 0)
You already figured out the first part.
Here it is how you can do the second part in pseudo code:
public void selectionSort(int[] a, int k) {
if (k == 0) {
return;
}
else {
selectionSort(a, k-1 );
find x such that a[x] is the smallest of a[k] ... a[a.length - 1]
if (a[k-1] > a[x]) {
swap a[k-1] and a[x]
}
}
}
I'm sure you are able to refine the pseudo code to real Java code.
By doing a simple google search, I found the biggest part of the code below on this site. I added the selectionSort method myself to suit your parameters.
public void selectionSort(int a[], int n)
{
recurSelectionSort(a, n, 0);
}
// Recursive selection sort. n is size of a[] and index
// is index of starting element.
static void recurSelectionSort(int a[], int n, int index)
{
// Return when starting and size are same
if (index == n)
return;
// calling minimum index function for minimum index
int k = minIndex(a, index, n-1);
// Swapping when index nd minimum index are not same
if (k != index){
// swap
int temp = a[k];
a[k] = a[index];
a[index] = temp;
}
// Recursively calling selection sort function
recurSelectionSort(a, n, index + 1);
}
// Return minimum index
static int minIndex(int a[], int i, int j)
{
if (i == j)
return i;
// Find minimum of remaining elements
int k = minIndex(a, i + 1, j);
// Return minimum of current and remaining.
return (a[i] < a[k])? i : k;
}

Java - StackOverflowError when executing QuickSort with large size (100,000) array

Working on an assignment measuring QuickSort efficiency in System.nanoTime. I have the code working for other array sizes (e.g. 100; 1,000; 10,000), however, when I attempt the method using an array of size 100,000, I am receiving a StackOverflowError. It is also good to note that I have this working for and array of size 100,000 for InsertionSort and BubbleSort.
The goal is to run through QuickSort 105 times, measuring the 100 times following the first 5, with the array, measuring the runtime in nanoTime.
First, I am creating a random int array of the predetermined size. Next, I am cloning that int array and passing the clone to the desired sort method (QuickSort in this instance). Finally, I have created a method to run through the required number of times using QuickSort. The method is as follows:
public static void quickSort(int[] unsortedArray, int size, int max) {
long averageTime = 0;
for (int i = 0; i < RUN_TIMES; i++) {
if (i < 5) {
QuickSort.quickSort(unsortedArray);
} else {
long startTime = System.nanoTime();
QuickSort.quickSort(unsortedArray);
long stopTime = System.nanoTime();
long runTime = stopTime - startTime;
averageTime = averageTime + runTime;
}
}
System.out.println("Quicksort time for size " + size +
" and max " + max + ": " + averageTime / DIVISOR);
}
RUN_TIMES is set to 105 and DIVISOR is set to 100. The instructor-supplied QuickSort code is as follows:
public static void quickSort(int[] list) {
quickSort(list, 0, list.length - 1);
}
private static void quickSort(int[] list, int first, int last) {
if (last > first) {
int pivotIndex = partition(list, first, last);
quickSort(list, first, pivotIndex - 1);
quickSort(list, pivotIndex + 1, last);
}
}
private static int partition(int[] list, int first, int last) {
int pivot = list[first]; // Choose the first element as the pivot
int low = first + 1; // Index for forward search
int high = last; // Index for backward search
while (high > low) {
// Search forward from left
while (low <= high && list[low] <= pivot)
low++;
// Search backward from right
while (low <= high && list[high] > pivot)
high--;
// Swap two elements in the list
if (high > low) {
int temp = list[high];
list[high] = list[low];
list[low] = temp;
}
}
while (high > first && list[high] >= pivot)
high--;
// Swap pivot with list[high]
if (pivot > list[high]) {
list[first] = list[high];
list[high] = pivot;
return high;
}
else {
return first;
}
What am I doing wrong? One final thought, I am using NetBeans on a newer MacBook Pro. Just wanted to be sure I shared everything. Any help would be greatly appreciated!
UPDATE: This is the code I used to generate array:
private static int[] createArray(int size, int maxValue) {
int arraySize = size;
/*
Create array of variable size
Random int 1 - 999
*/
// Constructor for random and variable declaration
Random random = new Random();
int x;
// Constructor for ArrayList<Integer>
ArrayList<Integer> tempArrayList = new ArrayList<>();
// While loop used to create ArrayList w/100 unique values
while (tempArrayList.size() < arraySize) {
// Creates int value between 1 and 999
x = random.nextInt(maxValue) + 1;
// Checks if generated value already exists within ArrayList
if(!tempArrayList.contains(x)) {
// Add to ArrayList
//System.out.println("Added: " + x);
tempArrayList.add(x);
} else {
// Do nothing
} // end if - else
} // end while loop
// Convert ArrayList<Integer> to int[]
int[] arrayList = tempArrayList.stream().mapToInt(i -> i).toArray();
return arrayList;
} // end createArray method
Short version: Your list is sorted and QS requires a lot of recursion for sorted lists.
QuickSort is a recursive algorithm with a worst case time complexity of O(n), which is attained, for your pivot choice (first item), when the list is sorted.
Which it is, after the first iteration, because QuickSort sorts in place, i.e. it modifies the input array.
After your first iteration, the QuickSort on that array requires n recursions, or 100000. That's a lot. Consider a better sorting algorithm, or one that does not use recursion, or consider re-writing it without recursion.
You have coded a recursive algorithm, which puts heavy pressure on the stack, then given it a large data set, which given that stack pressure is proportionate to the square of the problem size leaves no mystery as to why you have a stack overflow. Recode your algorithm as a loop and don't leave so much litter on the stack.

Java Quicksort quadratic runtime behaviour

I tried to implement an efficient sorting algorithm in Java. For this reason, I also implemented quicksort and use the following code:
public class Sorting {
private static Random prng;
private static Random getPrng() {
if (prng == null) {
prng = new Random();
}
return prng;
}
public static void sort(int[] array) {
sortInternal(array, 0, array.length - 1);
}
public static void sortInternal(int[] array, int start, int end) {
if (end - start < 50) {
insertionSortInternal(array, start, end);
} else {
quickSortInternal(array, start, end);
}
}
private static void insertionSortInternal(int[] array, int start, int end) {
for (int i=start; i<end - 1; ++i) {
for (int ptr=i; ptr>0 && array[ptr - 1] < array[ptr]; ptr--) {
ArrayUtilities.swap(array, ptr, ptr - 1);
}
}
}
private static void quickSortInternal(int[] array, int start, int end) {
int pivotPos = getPrng().nextInt(end - start);
int pivot = array[start + pivotPos];
ArrayUtilities.swap(array, start + pivotPos, end - 1);
int left = start;
int right = end - 2;
while (left < right) {
while (array[left] <= pivot && left < right) {
++left;
}
if (left == right) break;
while (array[right] >= pivot && left < right) {
right--;
}
if (left == right) break;
ArrayUtilities.swap(array, left, right);
}
ArrayUtilities.swap(array, left, end - 1);
sortInternal(array, start, left);
sortInternal(array, left + 1, end);
}
}
ArrayUtilities.swap just swaps the two given elements in the array. From this code, I expect O(n log(n)) runtime behaviour. But, some different lengths of arrays to sort gave the following results:
10000 elements: 32ms
20000 elements: 128ms
30000 elements: 296ms
The test ran 100 times in each case, and then the arithmetic mean of the running times was calculated. But clearly, as opposed to the expected behaviour, the runtime is O(n^2). What's wrong with my algorithm?
In your insertion-sort implementation your array will be sorted in descending order, while in your quick-sort the array is sorted in ascending order. So replace(for descending order):
for (int ptr=i; ptr>0 && array[ptr - 1] < array[ptr]; ptr--)
with
for (int ptr=i; ptr>0 && array[ptr - 1] > array[ptr]; ptr--)
It also seems like your indexing is not correct.
Try to replace:
sortInternal(array, 0, array.length - 1);
with:
sortInternal(array, 0, array.length);
And in the insertions sort first for loop you don't need to do end - 1, i.e. use:
for (int i=start; i<end; ++i)
Finally, add if (start >= end) return; at the beginning of the quick-sort method.
And as #ljeabmreosn mentioned, 50 is a little bit too large, I would have chosen something between 5 and 20.
Hope that helps!
The QuickSort "optimized" with Insertion Sort for arrays with length less than 50 elements seems to be a problem.
Imagine I had an array of size 65, and the pivot happened to be the median of that array. If I ran the array through your code, your code would use Insertion Sort on the two 32 length subarrays to the left and right of the pivot. This would result in ~O(2*(n/2)^2 + n) = ~O(n^2) average case. Using quick sort and implementing a pivot picking strategy for the first pivot, the time average case would be ~O((nlog(n)) + n) = ~O(n(log(n) + 1)) = ~O(n*log(n)). Don't use Insertion Sort as it is only used when the array is almost sorted. If you are using Insertion Sort solely because of the real running time of sorting small arrays might run faster than the standard quick sort algorithm (deep recursion), you can always utilize a non-recursive quick sort algorithm which runs faster than Insertion Sort.
Maybe change the "50" to "20" and observe the results.

finding kth largest element in an array implemented bag

We have a collection of Comparables held in a bag and have to find the kth largest element. I copied the collection to a HashSet to remove duplicates, then converted the HashSet to an array to be sorted and consequently the kth element accessed. The code compiles, but fails the testing, and I can't figure out what's wrong. Any ideas?
public E kth(int k) {
uniqueSet();
Object[] uniqueArr = hashSet.toArray();
startQuick(uniqueArr);
return (E) uniqueArr[k - 1];
}
private void startQuick(Object[] uniqueArr) {
int i = 0, j = uniqueArr.length;
quickSort(uniqueArr, 0, j);
}
private void quickSort(Object[] uniqueArr, int i, int j) {
int index = partition(uniqueArr, i, j);
if (i < index - 1) {
quickSort(rankBagArr, index - 1, j);
}
if (index < j) {
quickSort(rankBagArr, i, index - 1);
}
}
private int partition(Object[] uniqueArr, int i, int j) {
E tmp;
E pivot = (E) rankBagArr[(i + j) / 2];
while (i <= j) {
while (rankBagArr[i].compareTo(pivot) < 0) {
i++;
}
while (rankBagArr[j].compareTo(pivot) > 0) {
j--;
}
if (i <= j) {
tmp = (E) rankBagArr[i];
rankBagArr[i] = rankBagArr[j];
rankBagArr[j] = tmp;
i++;
j--;
}
}
return i;
}
For a start this part is highly suspect:
if (i < index - 1)
quickSort(rankBagArr, index-1 ,j);
if (index < j)
quickSort(rankBagArr, i, index-1);
Don't you mean:
if (i < index - 1)
quickSort(rankBagArr, i, index-1);
if (index + 1 < j)
quickSort(rankBagArr, index + 1, j);
?
I'm not familiar with your approach to partitioning, so I don't know whether that's correct or not. I think I understand it, and it looks okay on inspection, but it's very easy to get off-by-one errors which are hard to see without careful study.
Here's a partition method I wrote in C# recently - you should be able to translate it into Java quite easily if you want to.
private static int Partition<T>(T[] array, int left, int right,
IComparer<T> comparer) {
// Pivot on the rightmost element to avoid an extra swap
T pivotValue = array[right];
int storeIndex = left;
for (int i = left; i < right; i++) {
if (comparer.Compare(array[i], pivotValue) < 0) {
Swap(array, i, storeIndex);
storeIndex++;
}
}
Swap(array, right, storeIndex);
return storeIndex;
}
static void Swap<T>(T[] array, int x, int y) {
T tmp = array[x];
array[x] = array[y];
array[y] = tmp;
}
Any reason for not just using Arrays.sort though?
If you want to solve the problem by sorting, then
Use sorting methods from API (Arrays.sort or Collections.sort). Reinventing the wheel is pointless.
Sort contents of your collection once, not every time you look for k-th element.
The quicksort partitioning is good for finding k-th element without sorting entire collection - you partition, if lowest range is larger then k, you recurrently go with partition to lower range, if it's smaller then k, you go to higher range and look for (k - size of lower range)-th element. It has better complexity than sorting whole collection. You can read more about it here
Anyway, your methods have parameter named uniqueArr, but some operations you perform on rankBagArr. Is it a typo? There is no definition of rankBagArr in your code.
May you could have a bit less of manipulations (and improve performance), and correct the default you are seeing...
Starting with a List (ArrayList), you could ask to sort it (using the comparator, and Collections.sort(list)). Then you could loop down and:
memorizing the last element
if you find the new element is not equals, increment a counter
when your counter reaches the k value, the current element is your target

Categories