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.)
Related
Please refer to this problem from Hackerrank
HackerLand National Bank has a simple policy for warning clients about possible fraudulent account activity. If the amount spent by a client on a particular day is greater than or equal to the client's median spending for a trailing number of days, they send the client a notification about potential fraud. The bank doesn't send the client any notifications until they have at least that trailing number of prior days' transaction data.
I have written the following code. However, the code is working for some of the test cases and is getting 'terminated due to timeout' for some. Can anyone please tell how can I improve the code?
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;
public class Solution {
// Complete the activityNotifications function below.
static int activityNotifications(int[] expenditure, int d) {
//Delaring Variables
int iterations,itr,length,median,midDummy,midL,midR, midDummy2,i,i1,temp,count;
float mid,p,q;
length = expenditure.length;
iterations = length-d;
i=0;
i1=0;
itr=0;
count = 0;
int[] exSub = new int[d];
while(iterations>0)
{
// Enter the elements in the subarray
while(i1<d)
{
exSub[i1]=expenditure[i+i1];
//System.out.println(exSub[i1]);
i1++;
}
//Sort the exSub array
for(int k=0; k<(d-1); k++)
{
for(int j=k+1; j<d; j++)
{
if(exSub[j]<exSub[k])
{
temp = exSub[j];
exSub[j] = exSub[k];
exSub[k] = temp;
}
}
}
//Printing the exSub array in each iteration
for(int l = 0 ; l<d ; l++)
{
System.out.println(exSub[l]);
}
i1=0;
//For each iteration claculate the median
if(d%2 == 0) // even
{
midDummy = d/2;
p= (float)exSub[midDummy];
q= (float)exSub[midDummy-1];
mid = (p+q)/2;
//mid = (exSub[midDummy]+exSub [midDummy-1])/2;
//System.out.println(midDummy);
}
else // odd
{
midDummy2 =d/2;
mid=exSub[midDummy2];
//System.out.println(midDummy2);
}
if(expenditure[itr+d]>=2*mid)
{
count++;
}
itr++;
i++;
iterations--;
System.out.println("Mid:"+mid);
System.out.println("---------");
}
System.out.println("Count:"+count);
return count;
}
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
String[] nd = scanner.nextLine().split(" ");
int n = Integer.parseInt(nd[0]);
int d = Integer.parseInt(nd[1]);
int[] expenditure = new int[n];
String[] expenditureItems = scanner.nextLine().split(" ");
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
for (int i = 0; i < n; i++) {
int expenditureItem = Integer.parseInt(expenditureItems[i]);
expenditure[i] = expenditureItem;
}
int result = activityNotifications(expenditure, d);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
bufferedWriter.close();
scanner.close();
}
}
The first rule on performance improvement is: Don't improve the performance if it's not needed.
Performance improvements usually lead to code that is less readable and therefore it should only be done when it's really needed.
The second rule is: Improve algorithms and data-structures before low-level improvements.
If you need to improve the performance of your code always try to use more efficient algorithms and data-structures before going to low-level improvement. In your code example that would be: Don't use BubbleSort, but try to use more efficient algorithms like Quicksort or Mergesort, because they use time complexity of O(n*log(n) while Bubble sort has a time complexity of O(n^2) which is much slower when you have to sort big arrays. You can use Arrays.sort(int[]) to do this.
Your data-structures are only arrays so this can't be improved in your code.
This will give your code quite some speedup, and will not lead to a code that can't be read anymore. Improvements like changing simple calculations to slightly faster calculations using bitshifts and other fast calculations (that are pretty hard to understand if used to often) will almost always lead to a code that is only slightly faster but no one will be able to easily understand it anymore.
Some improvements that could be applied to your code (that will also only slightly improve the performance) are:
Replace while loops with for loops if possible (they can be improved by the compiler)
Don't use System.out.println for many texts if it's not totaly needed (because it's quite slow for big texts)
Try to copy arrays using System.arraycopy which usually is faster than copying using while loops
So an improved code of yours could look like this (I marked the changed parts with comments):
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
public class Solution {
// Complete the activityNotifications function below.
static int activityNotifications(int[] expenditure, int d) {
//Delaring Variables
int iterations, itr, length, median, midDummy, midL, midR, midDummy2, i, i1, temp, count;
float mid, p, q;
length = expenditure.length;
iterations = length - d;
i = 0;
i1 = 0;
itr = 0;
count = 0;
int[] exSub = new int[d];
//EDIT: replace while loops with for loops if possible
//while (iterations > 0) {
for (int iter = 0; iter < iterations; iter++) {
//EDIT: here you can again use a for loop or just use System.arraycopy which should be (slightly) fasters
// Enter the elements in the subarray
/*while (i1 < d) {
exSub[i1] = expenditure[i + i1];
//System.out.println(exSub[i1]);
i1++;
}*/
System.arraycopy(expenditure, i, exSub, 0, d);
//EDIT: Don't use bubble sort!!! It's one of the worst sorting algorithms, because it's really slow
//Bubble sort uses time complexity O(n^2); others (like merge-sort or quick-sort) only use O(n*log(n))
//The easiest and fastest solution is: don't implement sorting by yourself, but use Arrays.sort(int[]) from the java API
//Sort the exSub array
/*for (int k = 0; k < (d - 1); k++) {
for (int j = k + 1; j < d; j++) {
if (exSub[j] < exSub[k]) {
temp = exSub[j];
exSub[j] = exSub[k];
exSub[k] = temp;
}
}
}*/
Arrays.sort(exSub);
//Printing the exSub array in each iteration
//EDIT: printing many results also takes much time, so only print the results if it's really needed
/*for (int l = 0; l < d; l++) {
System.out.println(exSub[l]);
}*/
i1 = 0;
//For each iteration claculate the median
if (d % 2 == 0) // even
{
midDummy = d / 2;
p = (float) exSub[midDummy];
q = (float) exSub[midDummy - 1];
mid = (p + q) / 2;
//mid = (exSub[midDummy]+exSub [midDummy-1])/2;
//System.out.println(midDummy);
}
else // odd
{
midDummy2 = d / 2;
mid = exSub[midDummy2];
//System.out.println(midDummy2);
}
if (expenditure[itr + d] >= 2 * mid) {
count++;
}
itr++;
i++;
//iterations--;//EDIT: don't change iterations anymore because of the for loop
System.out.println("Mid:" + mid);
System.out.println("---------");
}
System.out.println("Count:" + count);
return count;
}
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
String[] nd = scanner.nextLine().split(" ");
int n = Integer.parseInt(nd[0]);
int d = Integer.parseInt(nd[1]);
int[] expenditure = new int[n];
String[] expenditureItems = scanner.nextLine().split(" ");
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
for (int i = 0; i < n; i++) {
int expenditureItem = Integer.parseInt(expenditureItems[i]);
expenditure[i] = expenditureItem;
}
int result = activityNotifications(expenditure, d);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
bufferedWriter.close();
scanner.close();
}
}
Edit:
You can make the solution even faster if you don't sort the complete (sub-)array in every iteration, but instead only remove one value (the first day that is not used anymore) and add a new value (the new day that is now used) in the correct position (like #Vojtěch Kaiser mentioned in his answer)
This will make it even faster, because sorting an array takes the time O(d*log(d)), while adding a new value into an array, that is already sorted only takes the time O(log(d)) if you are using a search tree. When using an array (like I did in the example below) it takes the time O(d) because when using an array you need to copy the array values which takes linear time (like #dyukha mentioned in the comments). So the improvement (again) can be done by using a better algorithm (This solution could also be improved by using a search tree instead of an array).
So the new solution could look like this:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
public class Solution {
// Complete the activityNotifications function below.
static int activityNotifications(int[] expenditure, int d) {
//Delaring Variables
int iterations, length, midDummy, midDummy2, count;//EDIT: removed some unused variables here
float mid, p, q;
length = expenditure.length;
iterations = length - d;
count = 0;
//EDIT: add the first d values to the sub-array and sort it (only once)
int[] exSub = new int[d];
System.arraycopy(expenditure, 0, exSub, 0, d);
Arrays.sort(exSub);
for (int iter = 0; iter < iterations; iter++) {
//EDIT: don't sort the complete array in every iteration
//instead remove the one value (the first day that is not used anymore) and add the new value (of the new day) into the sorted array
//sorting is done in O(n * log(n)); deleting and inserting a new value into a sorted array is done in O(log(n))
if (iter > 0) {//not for the first iteration
int remove = expenditure[iter - 1];
int indexToRemove = find(exSub, remove);
//remove the index and move the following values one index to the left
exSub[indexToRemove] = 0;//not needed; just to make it more clear what's happening
System.arraycopy(exSub, indexToRemove + 1, exSub, indexToRemove, exSub.length - indexToRemove - 1);
exSub[d - 1] = 0;//not needed again; just to make it more clear what's happening
int newValue = expenditure[iter + d - 1];
//insert the new value to the correct position
insertIntoSortedArray(exSub, newValue);
}
//For each iteration claculate the median
if (d % 2 == 0) // even
{
midDummy = d / 2;
p = exSub[midDummy];
q = exSub[midDummy - 1];
mid = (p + q) / 2;
//mid = (exSub[midDummy]+exSub [midDummy-1])/2;
//System.out.println(midDummy);
}
else // odd
{
midDummy2 = d / 2;
mid = exSub[midDummy2];
//System.out.println(midDummy2);
}
if (expenditure[iter + d] >= 2 * mid) {
count++;
}
}
System.out.println("Count:" + count);
return count;
}
/**
* Find the position of value in expenditure
*/
private static int find(int[] array, int value) {
int index = -1;
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
index = i;
}
}
return index;
}
/**
* Find the correct position to insert value into the array by bisection search
*/
private static void insertIntoSortedArray(int[] array, int value) {
int[] indexRange = new int[] {0, array.length - 1};
while (indexRange[1] - indexRange[0] > 0) {
int mid = indexRange[0] + (indexRange[1] - indexRange[0]) / 2;
if (value > array[mid]) {
if (mid == indexRange[0]) {
indexRange[0] = mid + 1;
}
else {
indexRange[0] = mid;
}
}
else {
if (mid == indexRange[1]) {
indexRange[1] = mid - 1;
}
else {
indexRange[1] = mid;
}
}
}
System.arraycopy(array, indexRange[0], array, indexRange[0] + 1, array.length - indexRange[0] - 1);
array[indexRange[0]] = value;
}
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
String[] nd = scanner.nextLine().split(" ");
int n = Integer.parseInt(nd[0]);
int d = Integer.parseInt(nd[1]);
int[] expenditure = new int[n];
String[] expenditureItems = scanner.nextLine().split(" ");
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
for (int i = 0; i < n; i++) {
int expenditureItem = Integer.parseInt(expenditureItems[i]);
expenditure[i] = expenditureItem;
}
int result = activityNotifications(expenditure, d);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
bufferedWriter.close();
scanner.close();
//Just for testing; can be deleted if you don't need it
/*int[] exp = new int[] {2, 3, 4, 2, 3, 6, 8, 4, 5};
int d = 5;
activityNotifications(exp, d);
int[] exp2 = new int[] {1, 2, 3, 4, 4};
d = 4;
activityNotifications(exp2, d);*/
}
}
Your main concern is that you are sorting the partial array in every iteration, costing you total complexity of the problem O(n d log(d)), which can get pretty hairy for large d values.
What you want is to keep the array sorted between iterations and sort in/out changed values. For that you would implement binary search tree (BST) or some other balanced option (AVL, ...), perform O(log(d)) removal of oldest value, then perform O(log(d)) insertion of new value, and simply look in the middle for median. Total asymptotic complexity would be O(n log(d)) which is as far as I know the best you can get - rest of the optimization is low level dirty work.
Take a look at java https://docs.oracle.com/javase/10/docs/api/java/util/TreeSet.html, which should take care of the most of the work, but keep in mind that underlying structure is made out of objects that will be slower than arrays.
In theory it should be possible to write every recursive method into an iterative one.
I know it's pretty easy when the recursive function is tail-recursive. For example, calculating a factorial can be done like this recursive:
// Recursive:
long fact(long n){
return fact(n, 1);
}
long fact(long n, long r){
return n==1 ?
r
:
fact(n-1, r*n);
}
Try it online.
or like this iterative:
// Iterative:
int fact(int n){
int r = 1;
for(; n>1; n--)
r *= n;
return r;
}
Try it online.
This is pretty easy, because we don't use the result of the recursive method for something else, and we only have a single recursive call, where we decrease n by 1 ever iteration.
I also know you should keep a stack for most recursive to iterative conversions. For example, doing a quick-sort can be done like this recursive:
// Recursive:
void quicksort(int[] array, int left, int right){
if(left >= right) return;
int index = partition(array, left, right);
quicksort(array, left, index-1);
quicksort(array, index+1, right);
}
Try it online.
or like this iterative:
// Iterative:
void quicksort(int[] array, int left, int right){
int[] stack = new int[1024]; // Example size, alternative an actual java.util.Stack could be used
int i=0;
stack[i++] = left;
stack[i++] = right;
while(i>0){
right = stack[--i];
left = stack[--i];
if(left >= right) continue;
int index = partition(array, left, right);
stack[i++] = left;
stack[i++] = index-1;
stack[i++] = index+1;
stack[i++] = right;
}
}
Try it online.
But I now want to convert the following recursive method into its iterative form as well:
// Recursive:
int f(int n){
return n<1 ?
0
:
n%2+1 + 3*f(n/2);
}
Try it online.
In this case it uses the recursive result, and multiply that result by 3. I'm not entirely sure how to make this iterative as well. I tried something like this:
// Iterative:
int f(int n){
int[] stack = new int[1024]; // Example size, alternative an actual java.util.Stack could be used
int i=0;
stack[i++] = n;
while(i > 0){
n = stack[--i];
if(n < 1)
stack[i++] = 0;
stack[i++] = n%2+1 + 3*stack[--i];
}
return stack[0];
}
Which obviously it not going to work, since i=1 before it enters the while, then it becomes 0 at n = stack[--i], then 1 again at stack[i++] = n%2+1 + ..., and then 0 again at 3*stack[--i], so it stops after the first iteration, and simply returns stack[0]. How should I convert this recursive method above to an iterative one when I use the result of the recursive-call with other calculations (multiplying by 3 in this case)?
As for the reason why: I want to port this recursive method above to a stack-based language which doesn't have any functions and therefore I'll need the iterative approach (with stack).
Ok, I've been able to figure it out. I first need to fill the entire stack with the n%2+1 parts, dividing 2 every iteration. And as soon as n < 1, I need an inner loop to calculate the result.
Here is what I ended up with:
// Recursive:
int f(int n){ // i.e. n=2
int[] stack = new int[1024]; // Example size, alternative an actual java.util.Stack could be used
int i=0;
while(i >= 0){
stack[i++] = n%2+1;
n/=2;
if(n < 1){
while(i > 0){
n = 3*n + stack[--i];
}
return n;
}
}
return -1; // It should never come here, but the method need a return-statement
}
Try it online.
I ended up with something quiet similar (saw your self-answer only after)
int iterf(int n){
//can also be implemented with an array. easier with list.
List<Integer> sum = new ArrayList<>();
while (true) {
if( n < 1 ) { //equivalent to if(n<1) return 0;
sum.add(0);
break;
}
//for each n, fill list with the fixed argument part: (n%2)+1
sum.add((n%2)+1);
n=n/2;
}
//add to each list element 3 time the next element
//equivalent to 3*f(n/2);
for(int i = sum.size()-2; i >=0; i--) {
sum.set(i, sum.get(i) + (3*(sum.get(i+1))));
}
return sum.get(0);
}
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).
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
I'm looking to make this much quicker. I've contemplated using a tree, but I'm not sure if that would actually help much.
I feel like the problem is for most cases you don't need to calculate all the possible maximums only a hand full, but I'm not sure where to draw the line
Thanks so much for the input,
Jasper
public class SpecialMax {
//initialized to the lowest possible value of j;
public static int jdex = 0;
//initialized to the highest possible value of i;
public static int idex;
//will hold possible maximums
public static Stack<Integer> possibleMaxs = new Stack<Integer> ();
public static int calculate (int[] a){
if (isPositive(a)){
int size = a.length;
int counterJ;
counterJ = size-1;
//find and return an ordered version of a
int [] ordered = orderBySize (a);
while (counterJ>0){
/* The first time this function is called, the Jvalue will be
* the largest it can be, similarly, the Ivalue that is found
* is the smallest
*/
int jVal = ordered[counterJ];
int iVal = test (a, jVal);
possibleMaxs.push(jVal-iVal);
counterJ--;
}
int answer = possibleMaxs.pop();
while (!possibleMaxs.empty()){
if (answer<possibleMaxs.peek()){
answer = possibleMaxs.pop();
} else {
possibleMaxs.pop();
}
}
System.out.println("The maximum of a[j]-a[i] with j>=i is: ");
return answer;
} else {
System.out.println ("Invalid input, array must be positive");
return 0; //error
}
}
//Check to make sure the array contains positive numbers
public static boolean isPositive(int[] a){
boolean positive = true;
int size = a.length;
for (int i=0; i<size; i++){
if (a[i]<0){
positive = false;
break;
}
}
return positive;
}
public static int[] orderBySize (int[] a){
//orders the array into ascending order
int [] answer = a.clone();
Arrays.sort(answer);
return answer;
}
/*Test returns an Ival to match the input Jval it accounts for
* the fact that jdex<idex.
*/
public static int test (int[] a, int jVal){
int size = a.length;
//initialized to highest possible value
int tempMin = jVal;
//keeps a running tally
Stack<Integer> mIndices = new Stack<Integer> ();
//finds the index of the jVal being tested
for (int i=0; i<size; i++) {
if (jVal==a[i]){
//finds the highest index for instance
if (jdex<i){
jdex = i;
}
}
}
//look for the optimal minimal below jdex;
for (int i=0; i<jdex; i++){
if (a[i]<tempMin){
tempMin = a[i];
mIndices.push(i);
}
}
//returns the index of the last min
if (!mIndices.empty()){
idex = mIndices.pop();
}
return tempMin;
}
}
It can be done in linear time and linear memory. The idea is: find the minimum over each suffix of the array and maximum over each prefix, then find the point where the difference between the two is the highest. You'll also have to store the index on which the maximum/minimum for each prefix is reached if you need the indices, rather than just the difference value.
Pre-sorting a[] makes the procedure complicated and impairs performance. It is not necessary, so we leave a[] unsorted.
Then (EDITED, because I had read j>=i in the body of your code, rather than i>=j in the problem description/title, which I now assume is what is required (I didn't go over your coding details); The two varieties can easily be derived from each other anyway.)
// initialize result(indices)
int iFound = 0;
int jFound = 0;
// initialize a candidate that MAY replace jFound
int jAlternative = -1; // -1 signals: no candidate currently available
// process the (remaining) elements of the array - skip #0: we've already handled that one at the initialization
for (int i=1; i<size; i++)
{
// if we have an alternative, see if that combines with the current element to a higher "max".
if ((jAlternative != -1) && (a[jAlternative]-a[i] > a[jFound]-a[iFound]))
{
jFound = jAlternative;
iFound = i;
jAlternative = -1;
}
else if (a[i] < a[iFound]) // then we can set a[iFound] lower, thereby increasing "max"
{
iFound = i;
}
else if (a[i] > a[jFound])
{ // we cannot directly replace jFound, because of the condition iFound>=jFound,
// but when we later may find a lower a[i], then it can jump in:
// set it as a waiting candidate (replacing an existing one if the new one is more promising).
if ((jAlternative = -1) || (a[i] > a[jAlternative]))
{
jAlternative = i;
}
}
}
double result = a[jFound] - a[iFound];