Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I am trying to implement a version of quicksort with test classes that takes float. When i try to generate arrays with the size of 10⁸ I get stack overflow when running my testclass.
I tried with array size of 10⁷ and that worked fine
In my testclass i generate two arrays that are exactly the same, one is sorted with my algoritm and one is sorted with javas Arrays.sort().
Here is how my testclass looks like.
package Quicksort;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.*;
public class QuickSortTest {
private static float[] quickSort, javaSort;
private final static int SIZE = (int) 1e7;
#Before
public void init(){
System.gc();
quickSort = new float[SIZE];
javaSort = new float[SIZE];
for(int i = 0; i < SIZE; i++){
int temp = (int) (Math.random() * 1000) + 1;
quickSort[i] = temp;
}
javaSort = quickSort;
}
#Test
public void testQuickSort(){
QuickSort.sort(quickSort);
Arrays.sort(javaSort, 0, SIZE);
assertArrayEquals(quickSort, javaSort, .0f);
}
}
Quicksort implementation:
private static void quickSort(float[] table, int first, int last){
if(first < last){
int pivotIndex = partition(table, first, last);
quickSort(table, first, pivotIndex - 1);
quickSort(table, pivotIndex + 1, last);
}
}
public static int partition(float[] table, int first, int last){
sort3(table, first, last);
swap(table, first, (first + last) / 2);
float pivot = table[first];
int up = first;
int down = last;
do{
while((up < last) && table[up] <= pivot){
up++;
}
while(table[down] > pivot){
down--;
}
if(up < down){
swap(table, up, down);
}
}while(up < down);
swap(table, first, down);
return down;
}
A StackOverflowError is usually caused by a bad recursive call. Your QuickSort class has a recursive functions that keeps calling itself beyond the stack size when you pass in an array of length 10^8.
A way to solve this is to switch your implementation to iterative approach rather than a recursive one.
based on your last update, it seems like partition() method calls itself recursively beyond the limitations of the Java heap space.
In this post, you can find an iterative partition() implementation. It's slightly more complicated but will be able to handle the size of your array.
import java.util.Arrays;
import java.util.Random;
// Java program for implementation of QuickSort
class QuickSort
{
public static void main(String[] args) {
QuickSort sort=new QuickSort();
int[] randomArray = createRandomArray((int) Math.pow(2, 20));
sort.qSort(randomArray);
//System.out.println(Arrays.toString(randomArray));
}
private void qSort(int[] arr) {
this.qSort(arr, 0, arr.length-1);
}
/* This function takes last element as pivot,
places the pivot element at its correct
position in sorted array, and places all
smaller (smaller than pivot) to left of
pivot and all greater elements to right
of pivot */
int partition(int arr[], int low, int high)
{
int pivot = arr[high];
int i = (low-1); // index of smaller element
for (int j=low; j<=high-1; j++)
{
// If current element is smaller than or
// equal to pivot
if (arr[j] <= pivot)
{
i++;
// swap arr[i] and arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// swap arr[i+1] and arr[high] (or pivot)
int temp = arr[i+1];
arr[i+1] = arr[high];
arr[high] = temp;
return i+1;
}
/* The main function that implements QuickSort()
arr[] --> Array to be sorted,
low --> Starting index,
high --> Ending index */
void qSort(int arr[], int low, int high)
{
if (low < high)
{
/* pi is partitioning index, arr[pi] is
now at right place */
int pi = partition(arr, low, high);
// Recursively sort elements before
// partition and after partition
qSort(arr, low, pi-1);
qSort(arr, pi+1, high);
}
}
private static int[] createRandomArray(int size){
Random randNumGenerator = new Random();
int[] arr = new int[size];
for (int i=0; i<arr.length; i++)
{
arr[i] = (randNumGenerator.nextInt(100)+1);
}
return arr;
}
}
It seems like you want to keep the following in your mind;
maximum heap size (changeable at JVM initiation),
maximum stack size (changeable at JVM initiation) and,
maximum array size in Java (mostly 2^31, some new JVMs limits it to 2^31-5).
Related
I'm writing a quicksort algorithm to sort numbers with random pivot. How can I change my quicksort methods from recursive to iterative? I have one sort method which is recursive, but I need the iterative method. Is it possible to change from recursive to iterative in just the sort method or do I have to change the whole code?
here is all my code
public class Main {
public static void main(String[] args) {
long start = System.currentTimeMillis();
ArrayList time = new ArrayList();
for (int k = 1; k < 1000000; k++) {
time.add(k);
}
int[] tall = new int[1000000];
int index = 0;
int n = 1000000;
File text = new File("/Users/sasan/IdeaProjects/File.txt");
try {
Scanner scan = new Scanner(text);
while (scan.hasNextLine() && index < 1000000) {
tall[index] = scan.nextInt();
index++;
}
scan.close();
} catch (IOException e) {
System.out.println("Problem with file");
e.printStackTrace();
}
int l = tall.length;
sort(tall, 0, l-1);
System.out.println("Sorted array");
printArray(tall);
System.out.println("");
long end = System.currentTimeMillis();
System.out.print("Execution Time is: ");
System.out.print((end - start));
}
static void random(int tall[],int low,int high)
{
Random rand= new Random();
int pivot = rand.nextInt(high-low)+low;
int temp1=tall[pivot];
tall[pivot]=tall[high];
tall[high]=temp1;
}
/* This function takes last element as pivot,
places the pivot element at its correct
position in sorted array, and places all
smaller (smaller than pivot) to left of
pivot and all greater elements to right
of pivot */
static int partition(int tall[], int low, int high)
{
// pivot is choosen randomly
random(tall,low,high);
int pivot = tall[high];
int i = (low-1); // index of smaller element
for (int j = low; j < high; j++)
{
// If current element is smaller than or
// equal to pivot
if (tall[j] < pivot)
{
i++;
// swap arr[i] and arr[j]
int temp = tall[i];
tall[i] = tall[j];
tall[j] = temp;
}
}
// swap arr[i+1] and arr[high] (or pivot)
int temp = tall[i+1];
tall[i+1] = tall[high];
tall[high] = temp;
return i+1;
}
/* The main function that implements QuickSort()
tall[] --> Array to be sorted,
low --> Starting index,
high --> Ending index */
static void sort(int tall[], int low, int high)
{
if (low < high)
{
/* pi is partitioning index, tall[pi] is
now at right place */
int pi = partition(tall, low, high);
// Recursively sort elements before
// partition and after partition
sort(tall, low, pi-1);
sort(tall, pi+1, high);
}
}
/* A utility function to print array of size n */
static void printArray(int tall[])
{
int n = tall.length;
for (int i = 0; i < n; ++i)
System.out.print(tall[i]+" ");
System.out.println();
}
// Driver Code
}
You can generally always turn a recursive method into an iterative method, though the point about using recursion is that you usually choose it when it's most convenient to do so. A divide-and-conquer algorithm such as quicksort is precisely a good candidate for recursion.
To turn a recursive method into an iterative method, you basically use a 'stack' object (and Java provides the Stack class to assist with this): this essentially stores what parameters need to be operated on on each iteration. Then, whereas before you would have called into your recursive method, you put on top of your stack the parameters that would have been used to call it. Each iteration does the equivalent of "returning" from the method by taking the latest parameters off the top of the stack. (Essentially, you're simulating the process that Java goes through under the hood when you call a method.)
So the pattern looks like this:
Stack<Paraneters> stack = new Stack();
stack.push(new Parameters(0, size);
while (!stack.empty()) {
Parameters params = stack.pop();
int lo = params.lo;
int hi = params.hi;
if (lo < hi) {
int pivot = partition(lo, hi);
stack.push(new Parameters(lo, pivot-1));
stack.push(new Parameters(pivot+1, hi));
}
}
For this simple example, the Parameters object could actually just be a 2-length int array, but for more complex combinations of parameters you may end up constructing a wrapper class. (Another reason why using recursion can be more convenient in practice.)
So I think my partition method works but I cannot understand or figure out how to do the kthSmallest method. I no longer get out of bounds errors with my partition method which leads me to think that it works and with testing it seems to work. However, my kthSmallest method often gets stuck in an infinite loop and when it does return a value, it is never the correct value.
I have seen examples online that place the pivot between the two subarrays however for our assignment the pivot is always at the end so I often get confused looking at these examples.
Here is what I have:
class Median{
static int kthSmallest(int[] arr, int left, int right, int k){
int divider = partition(arr, left, right);
if(divider == k-1){
return arr[right];
}
else if(divider > k-1){
return kthSmallest(arr, left, divider-1, k);
}
else{
return kthSmallest(arr, divider, right, (k - divider-1));
}
}
static int partition(int[] arr, int left, int right){
int pivot = arr[right];
int index = left;
for(int i = left; i < right-1; i++){
if(arr[i] < pivot){
swap(arr, index, i);
index++;
}
}
printArr(arr);
System.out.println("divider: " + index);
return index;
}
static void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static void printArr(int[] arr){
System.out.println();
for(int index = 0; index < arr.length - 1; index++){
System.out.print(arr[index] + ", ");
}
System.out.print(arr[arr.length-1]);
System.out.println();
}
public static void main(String[] args){
int[] arr = {34,-1,0,5,3};
printArr(arr);
//System.out.println(partition(arr, 0, arr.length-1));
//printArr(arr);
System.out.println(kthSmallest(arr, 0, arr.length - 1, 2));
}
}
It seems like what you are trying to do is implement the QuickSelect algorithm, so I recommend taking a look at the Wikipedia article.
Looking at your code, I think you have misinterpreted the "pivot at the end" bit. I believe that what your requirements want is to select the pivot from the end, and then place it in between the two sublists so that your list looks right. For example,
34 -1 0 5 3
should become
-1 0 3 34 5
not
-1 0 34 5 3, where your pivot is not doing its job correctly.
There are also a few problems with your kthSmallest method. Double check the values you pass along in your recursion and you also are missing a case which might cause infinite recursion. Spoilers for if you are absolutely stuck:
You since you don't re-index the list, you should change '(k - divider -1)' to just k.
If left == right then you get unnecessary recursion, so you should just return in that case.
In your partition method, make sure you iterate far enough in the part of the list you are partitioning. Again, just in case you're really stumped:
for(int i = left; i < right-1; i++) should become for(int i = left; i <= right-1; i++)
For refreshing some Java I tried to implement a quicksort (inplace) algorithm that can sort integer arrays. Following is the code I've got so far. You can call it by sort(a,0,a.length-1).
This code obviously fails (gets into an infinite loop) if both 'pointers' i,j point each to an array entry that have the same values as the pivot. The pivot element v is always the right most of the current partition (the one with the greatest index).
But I just cannot figure out how to avoid that, does anyone see a solution?
static void sort(int a[], int left, int right) {
if (right > left){
int i=left, j=right-1, tmp;
int v = a[right]; //pivot
int counter = 0;
do {
while(a[i]<v)i++;
while(j>0 && a[j]>v)j--;
if( i < j){
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
} while(i < j);
tmp = a[right];
a[right] = a[i];
a[i] = tmp;
sort(a,left,i-1);
sort(a,i+1,right);
}
}
When preforming a Quicksort I strongly suggest making a separate method for partitioning to make the code easier to follow (I'll show an example below). On top of this a good way of avoiding worst case run time is shuffling the array you're sorting prior to preforming the quick sort. Also I used the first index as the partitioning item instead of the last.
For example:
public static void sort (int[] a)
{
StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
}
private static void sort(int[] a, int lo, int hi)
{
if (hi <= lo) return;
int j = partition(a, lo, hi) // the addition of a partitioning method
sort(a, lo, j-1);
sort(a, j+1, hi);
}
private static int partition(int[] a, int lo, int hi)
{
int i = lo, j = hi + 1, tmp = 0;
int v = a[lo];
while (true)
{
while (a[i++] < v) if (i == hi) break;
while (v < a[j--]) if (j == lo) break;
if (i >= j) break;
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
tmp = a[lo];
a[lo] = a[j];
a[j] = temp;
return j;
}
On top of this if you want a really good example on how Quicksort works (as a refresher) see here.
This should work (will check for correctness in a bit, it works!):
EDIT: I previously made a mistake in error checking. I forgot to add 2 more conditions, here is the amended code.
public static void main (String[] args) throws java.lang.Exception
{
int b[] = {10, 9, 8, 7, 7, 7, 7, 3, 2, 1};
sort(b,0,b.length-1);
System.out.println(Arrays.toString(b));
}
static void sort(int a[], int left, int right) {
if (right > left){
int i=left, j=right, tmp;
//we want j to be right, not right-1 since that leaves out a number during recursion
int v = a[right]; //pivot
do {
while(a[i]<v)
i++;
while(a[j]>v)
//no need to check for 0, the right condition for recursion is the 2 if statements below.
j--;
if( i <= j){ //your code was i<j
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i++;
j--;
//we need to +/- both i,j, else it will stick at 0 or be same number
}
} while(i <= j); //your code was i<j, hence infinite loop on 0 case
//you had a swap here, I don't think it's needed.
//this is the 2 conditions we need to avoid infinite loops
// check if left < j, if it isn't, it's already sorted. Done
if(left < j) sort(a,left,j);
//check if i is less than right, if it isn't it's already sorted. Done
// here i is now the 'middle index', the slice for divide and conquer.
if(i < right) sort(a,i,right);
}
}
This Code in the IDEOne online compiler
Basically we make sure that we also swap the value if the value of i/j is the same as the pivot, and break out of the recursion.
Also there was a check in the pseudocode for the length, as if we have an array of just 1 item it's already sorted (we forgot the base case), I thought we needed that but since you pass in the indexes and the entire array, not the subarray, we just increment i and j so the algorithm won't stick at 0 (they're done sorting) but still keep sorting an array of 1. :)
Also, we had to add 2 conditions to check if the array is already sorted for the recursive calls. without it, we'll end up sorting an already sorted array forever, hence another infinite loop. see how I added checks for if left less than j and if i less than right. Also, at that point of passing in i and j, i is effectively the middle index we split for divide and conquer, and j would be the value right before the middle value.
The pseudocode for it is taken from RosettaCode:
function quicksort(array)
if length(array) > 1
pivot := select any element of array
left := first index of array
right := last index of array
while left ≤ right
while array[left] < pivot
left := left + 1
while array[right] > pivot
right := right - 1
if left ≤ right
swap array[left] with array[right]
left := left + 1
right := right - 1
quicksort(array from first index to right)
quicksort(array from left to last index)
Reference: This SO question
Also read this for a quick refresher, it's implemented differently with an oridnary while loop
This was fun :)
Heres some simple code I wrote that doesn't initialize to many pointers and gets the job done in a simple manner.
public int[] quickSort(int[] x ){
quickSortWorker(x,0,x.length-1);
return x;
}
private int[] quickSortWorker(int[] x, int lb, int ub){
if (lb>=ub) return x;
int pivotIndex = lb;
for (int i = lb+1 ; i<=ub; i++){
if (x[i]<=x[pivotIndex]){
swap(x,pivotIndex,i);
swap(x,i,pivotIndex+1);
pivotIndex++;
}
}
quickSortWorker(x,lb,pivotIndex-1);
quickSortWorker(x,pivotIndex+1,ub);
return x;
}
private void swap(int[] x,int a, int b){
int tmp = x[a];
x[a]=x[b];
x[b]=tmp;
}
I've been trying to optimize this mergesort version but it takes too long to sort around 3 million of registers. Where am I doing it wrong? I would appreciate some help, thanks.
Persona is a class that has a string and an Integer, just in case that you guys want to know in order to help me.
public class Mergesort {
private ArrayList<Persona> numbers = new ArrayList();
private ArrayList<Persona> helper;
private int number;
private boolean ascending;
public void sort(ArrayList<Persona> values, boolean ascending) {
this.numbers = values;
this.ascending = ascending;
number = values.size();
helper = new ArrayList();
mergesort(0, number - 1);
}
/**
* Determines the middle of the array to sort the left side and the right side
* Then it merges both arrays.
* #param low
* #param high
*/
private void mergesort(int low, int high) {
// check if low is smaller then high, if not then the array is sorted
if (low < high) {
// Get the index of the element which is in the middle
int middle = low + (high - low) / 2;
// Sort the left side of the array
mergesort(low, middle);
// Sort the right side of the array
mergesort(middle + 1, high);
// Combine them both
merge(low, middle, high);
}
}
/**
* Merges the arrays.
* #param low
* #param middle
* #param high
*/
private void merge(int low, int middle, int high) {
// Copy both parts into the helper array
for (int i = low; i <= high; i++) {
helper.add(i, numbers.get(i));
}
int i = low;
int j = middle + 1;
int k = low;
// Copy the smallest values from either the left or the right side back
// to the original array
while (i <= middle && j <= high) {
if ( helper.get(i).id <= helper.get(j).id) {
numbers.set(k, helper.get(i));
i++;
} else {
numbers.set(k,helper.get(j));
j++;
}
k++;
}
// Copy the rest of the left side of the array into the target array
while (i <= middle) {
numbers.set(k,helper.get(i));
k++;
i++;
}
}}
You never clear out the contents of helper (which should not be a global anyawy), which means each time you are merging more and more data. I'm really surprised you didn't get out-of-memory.
Is your code running and the o/p is alryt?
In the merge function there must be another loop,after the first while loop. The first while loop terminated because either j>high or i>middle. You just wrote j>high condition,dere is no i>middle condtn.After that loop thre must be something like dis
if(j>high)
{
while (i <= middle) {
numbers.set(k,helper.get(i));
k++;
i++;
}
}
else
{
while (j <= high) {
numbers.set(k,helper.get(j));
k++;
j++;
}
}
N clear the helper
So I am working on a project for my class and I am currently stuck on creating a QuickSort class to sort an Array of 1000 names. I have a template I am using from a previous Lab we did in class which we are supposed to base it off of; but in the lab we used an array of Integers and I am struggling with how to convert it so it will work with Strings; names. Thanks for your help or any suggestions, the code is below.
Updated post; So I made my comparison in my Name class
public int compareTo(Object other) {
int result;
if (name.equals(((Name) other).name))
result = name.compareTo(((Name) other).name);
else
result = name.compareTo(((Name) other).name);
return result;
}
And I've tried to re-work my QuickSort..I'm struggling with the swap method.
private ArrayList<Name> data;
public QuickSort(ArrayList<Name> initialValue){
data=initialValue;
}
public void sort(ArrayList<Name> namelist, int i, int j){
sort(0, data.size()-1);
}
public void sort(int from, int to){
if (from >= to)
return;
int p = partition(from, to);
sort(from, p);
sort( p + 1, to);
}
private int partition(int from, int to){
Name pivot = data.get(from);
int i = from - 1;
int j = to + 1;
while(i<j){
i++; while(data.get(i).compareTo(pivot) < 0) i++;
j--; while(data.get(j).compareTo(pivot) < 0) j--;
if(i<j) swap(i,j);
}
return j;
}
private void swap (int i, int j){
Name temp = data.get(i);
data.equals(i) = data.get(j);
data = temp;
}
particularly the "data.equals(i) = data.get(j) line and the data = temp; I am sure i'm doing something stupid and easy.
update;
private void swap (int i, int j){
Name temp = data.get(i);
data.get(j).equals(data.get(i));
data.get(j).equals(temp);
}
possibly?
Posting the code that will solve the problem would be easy but won't help you to learn the meaning of QuickSort (or another sorting algorithm).
The heart of the QuickSort is exchanging the elements here:
while(i<j){
i++; while(data[i] < pivot) i++;
j--; while(data[j] > pivot) j--;
if(i<j) swap(i,j);
}
As you can see, you're comparing the elements of the data array against the pivot variable. Since they're ints, you can easily compare them using <. Now, you have to do something similar but for Strings. Thankfully, Strings can be compared using String#compareTo method. I'll let you this implementation for String (otherwise I will present the homework assignment as mine =P).
For a more generic solution to the problem, you have two options:
Making your class implement the Comparable interface, so you will have a compareTo method. A basic
sample implementation:
public class Name implements Comparable<Name> {
#Override
public int compareTo(Name name) {
return ... //comparison logic...
}
}
Using it in your QuickSort
pivot.compareTo(...);
Using an instance of Comparator interface. You will use Comparator#compare for this. A basic sample implementation:
public class NameComparator implements Comparator<Name> {
#Override
public int compare(Name name1, Name name2) {
return ... //comparison logic...
}
}
Using it in your QuickSort
NameComparator nameComparator = new NameComparator();
nameComparator.compare(..., ...);
You can use a comparator: Comparator.compare(o1, o2) returns -1, 0 or 1 if the objects are less, equal or greater.
Strings in java are actually comparable, some sort of a companion interface:
int compareTo(T other);
API Do: http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html
Note that in string comparison is by equals method:
data.get(j) == pivot => data.get(j).equals(pivot)
You will have to set a String value to compare it to something. So by setting the pivot value to compare it to itself it will return zero. Since it is unlikely that all Strings will equal your pivot value then anything compared to pivot value will be returned as -1 OR 1. By doing this your if statement will determine which way the swap value is sent( Higher OR Lower) then your pivot value.
ObjectQuickSorter sortStrings = new ObjectQuickSorter();
sortStrings.sort(arrayHere);
class ObjectQuickSorter{
void sort(String[] array){
doQuickSort(array, 0, array.length -1);
}
private static void doQuickSort(String[] array,int start, int end){
int pivotPoint;
if(start < end){
pivotPoint = partition(array, start, end);
doQuickSort(array, start, pivotPoint -1);
doQuickSort(array, pivotPoint + 1, end);
}
}
private static int partition(String[] array, int start, int end){
String pivotValue;
int endOfLeftList;
int mid = (start + end)/2;
swap(array, start, mid);
pivotValue = array[start];
endOfLeftList = start;
for(int scan = start + 1; scan <= end; scan++){
// trying to compare pivot = string value to array[scan] position value
// doing this by setting pivot value compare to itself to return 0
// then also comparing pivot value to array[scan] to return -1, 0, 1
// if return is 0 or 1 then it ignores it
if( array[scan].compareTo(pivotValue) < array[start].compareTo(pivotValue)){
endOfLeftList++;
swap(array, endOfLeftList,scan);
}
}
swap(array, start, endOfLeftList);
return endOfLeftList;
}
private static void swap(String[] array, int a, int b){
String temp;
temp = array[a];
array[a] = array[b];
array[b] = temp;
}
}