Java MergeSort speedup using Multi Threading - java

I'm trying to implement a version of Mergesort using Multi Threading. First off, I know there's a billion threads (give or take...) on here, and I've read a few to no avail! I'm trying to show that using threads in parallel speeds the process up. The issue I'm having however is that my code does not display and speed up whatsoever, in fact, rather the opposite.
With one thread, my times is in the 10's of thousands. With two, my time increases to a few hundred thousand, then with 4 threads my time borders on 7 figures. My initial thoughts were to play around with the join() method and ensure that was in the right place, and have done so, but to no success.
Any help would be greatly appreciated, and an example command line argument is something like;
java working 16 4 (For 4 threads).
Apologies for the lack of comments too throughout!
import java.util.*;
class working
{
private static int sizeVector;
private static int noThreads;
static void sort(int[] input)
{
mergeSort(input, 0, input.length - 1, noThreads);
}
static void mergeSort(int[] array, int low, int high, int noThreadsUp)
{
//private int noThreadsUp;
if (low < high)
{
int mid = (low+high)/2;
if (noThreadsUp > 1)
{
NewThread td = new NewThread(array, low, mid, noThreadsUp/2);
td.start();
/*try{
td.join();
}catch(Exception e){}*/
mergeSort(array, mid+1, high, noThreadsUp/2);
try{
td.join();//UNSURE WHERE THIS SHOULD BE
}catch(Exception e){}
merge(array, low, mid, high);
}
else
{
mergeSort(array, low, mid, noThreadsUp/2);
mergeSort(array, mid+1, high, noThreadsUp/2);
merge(array, low, mid, high);
}
}
}
static void merge(int[] array, int low, int mid, int high)
{
int[] temp = new int[high - low + 1];
int left = low;
int right = mid+1;
int k = 0;
while (left <= mid && right <= high)
{
if(array[left] < array[right])
{
temp[k] = array[left];
left = left+1;
}
else
{
temp[k] = array[right];
right = right + 1;
}
k = k + 1;
}
if (left <= mid)
{
while(left <= mid)
{
temp[k] = array[left];
left = left + 1;
k = k + 1;
}
}
else if (right <= high)
{
while(right <= high)
{
temp[k] = array[right];
right = right + 1;
k = k + 1;
}
}
for (int m = 0; m < temp.length; m++)
{
array[low+m] = temp[m];
}
}
static int[] readInputArray()
{
int[] a = new int[sizeVector];
for (int i = 0; i < sizeVector; i++)
{
Random generator = new Random();
a[i] = generator.nextInt();
}
return a;
}
static void printArray(int[] array)
{
for(int i = 0; i<array.length; i++)
System.out.println(array[i]);
}
public static void main(String[] args)
{
sizeVector = Integer.parseInt(args[0]);
noThreads = Integer.parseInt(args[1]);
int[] inputArray = readInputArray();
System.out.println("INPUT ARRAY: ");
printArray(inputArray);
long startTime = System.nanoTime();
sort(inputArray);
long endTime = System.nanoTime();
long finalTime = endTime - startTime;
System.out.println("SORTED ARRAY: ");
printArray(inputArray);
System.out.println("Time: " + finalTime);
}
static class NewThread extends Thread
{
private int low;
private int mid;
private int[] array;
private int noThreadsDown;
//private int threads;
public NewThread(int[] array, int low, int mid, int noThreadsDown)
{
this.low = low;//Ensure using the right start
this.mid = mid;//Ensure using the right end
this.array = array;
this.noThreadsDown = noThreadsDown;
//this.threads = threads;
}
public void run()
{
mergeSort(array, low, mid, noThreadsDown/2);
System.out.println(noThreadsDown);
}
}//End NewThread
}

It could be best using RecurssionAcition class in java 7, which split up into two parts left and right.
Sum left = new Sum(array, low, mid);
Sum right = new Sum(array, mid, high);
left.fork();
long rightAns = right.compute();
long leftAns = left.join();
return leftAns + rightAns;

Here some code that works.
public class TestMergeSort{
public static void main(String[] args){
// Threaded merge sort (and printing)
int[] toSort = {191,2,3,5,6,7,5,3,21,3,4};
printArr(toSort);
concurrentMergeSort(toSort);
printArr(toSort);
}
public static void concurrentMergeSort(int[] toSort){
int[] tmpArray = new int[toSort.length];
try{
// Start the mergesort
ConcurrentMergeSort sort = new ConcurrentMergeSort(toSort, tmpArray, 0, toSort.length - 1);
sort.start();
sort.join();
} catch(InterruptedException e){
e.printStackTrace();
}
}
public static void printArr(int[] a){
for(int i = 0; i < a.length; i++){
System.out.print(a[i] + " ,");
}
System.out.println();
}
}
public class ConcurrentMerge extends Thread{
private int[] a;
private int[] tmpArray;
private int leftPos;
private int rightPos;
private int rightEnd;
public ConcurrentMerge(int[] a, int[] tmpArray, int leftPos, int rightPos, int rightEnd){
this.a = a;
this.tmpArray = tmpArray;
this.leftPos = leftPos;
this.rightPos = rightPos;
this.rightEnd = rightEnd;
}
public void run(){
int leftEnd = rightPos - 1;
int tmpPos = leftPos;
int numElements = rightEnd - leftPos + 1;
// Main loop
while( leftPos <= leftEnd && rightPos <= rightEnd )
if( a[ leftPos ] <= a[ rightPos ] )
tmpArray[ tmpPos++ ] = a[ leftPos++ ];
else
tmpArray[ tmpPos++ ] = a[ rightPos++ ];
// Copy rest of the left half
while( leftPos <= leftEnd )
tmpArray[ tmpPos++ ] = a[ leftPos++ ];
// Copy rest of the right half
while( rightPos <= rightEnd )
tmpArray[ tmpPos++ ] = a[ rightPos++ ];
// Copy tmpArray back
for( int i = 0; i < numElements; i++){
a[ rightEnd ] = tmpArray[ rightEnd-- ];
}
}
}
import java.util.Arrays;
public class ConcurrentMergeSort extends Thread{
private int[] a;
private int[] tmpArray;
private int left;
private int right;
public ConcurrentMergeSort(int[] a, int[] tmpArray, int left, int right){
this.a = a;
this.tmpArray = tmpArray;
this.left = left;
this.right = right;
}
public void run(){
if(this.left < this.right){
try{
int center = ( this.left + this.right ) / 2;
ConcurrentMergeSort p = new ConcurrentMergeSort(this.a, this.tmpArray, this.left, center);
ConcurrentMergeSort q = new ConcurrentMergeSort(this.a, this.tmpArray, center + 1, this.right);
ConcurrentMerge r = new ConcurrentMerge(this.a, this.tmpArray, this.left, center + 1, this.right);
// Sort
p.start();
q.start();
p.join();
q.join();
// Merge
r.start();
r.join();
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
public int[] getA(){
return this.a;
}
}

Related

Hybrid sorting using insertion sort and merge sort

I wish to implement a hybrid algorithm that switches from insertion sort to merge sort once the input array size becomes too big.
This is my main function (I fixed my input array size at 30 currently as I wish to test my merge sort function) :
public static void main(String[] args) {
int[] a = genrandarray(30);
long bgn, end;
bgn = System.currentTimeMillis();
imsort(a, 0, a.length, 30);
end = System.currentTimeMillis();
for(int i = 1; i < a.length; i++){
if(a[i-1] > a[i]){
System.out.println("failed");
break;
}
}
System.out.println("milliseconds " + (end-bgn));
}
Insertion sort is called when the array size is <20, else merge sort is called:
public static void imsort(int [] slot, int b, int e, int size) {
//if smaller than 20, use insertion sort
if (e-b<=20) {
insertionSort(slot, e); //e is the length of slot[]
System.out.println("imsort entered!");
}
else {
mergesort(b, e, slot);
System.out.println("mergesort entered!");
}
}
There is a Index 30 out of bounds error for my merge sort function currently.
public static int merge(int n, int m, int[] slot) {
int mid = (n+m)/2;
int a = n, b = mid+1, i, tmp, cmp=0, comp=0;
//sanity check
if (m-n <= 0) return -1000;
while (a <= mid && b <= m) {
cmp = compare(slot[a], slot[b]);
comp++;
if (cmp > 0) { //slot[a] > slot[b]
tmp = slot[b++];
for (i = ++mid; i > a; i--)
slot[i] = slot[i-1];
slot[a++] = tmp;
}
else if (cmp < 0) //slot[a] < slot[b]
a++;
else {
//slot[a] == slot[b]
if (a == mid && b == m) break;
tmp = slot[b++];
a++;
for (i = ++mid; i > a; i--)
slot[i] = slot[i-1]; slot[a++] = tmp;
}
} // end of while loop;
return comp;
} // end of merge
public static int mergesort(int s, int e, int[] slot) {
//int comp =0;
int mid = (s+e)/2;
int right=0, left=0;
if(e-s>0) {
//comp++;
left = mergesort(s,mid, slot);
//comp++;
right = mergesort(mid+1,e, slot);
}
return right + left + merge(s,e,slot);
}
I am unsure of the error in my merge / mergesort function. Can I get some further advice?
a.length returns you 30 which is the length of your random array from the genrandarray method i believe. And your array is indexed 0 through 29. Try changing the main method like this and it will work out
public static void main(String[] args) {
int[] a = genrandarray(30);
long bgn, end;
bgn = System.currentTimeMillis();
imsort(a, 0, a.length-1, 30);
end = System.currentTimeMillis();
for(int i = 1; i < a.length; i++){
if(a[i-1] > a[i]){
System.out.println("failed");
break;
}
}
System.out.println("milliseconds " + (end-bgn));
}
First, let me congratulate on a very good post for someone new. You didn't post the line of code where the error is happening and code isn't complete, so I filled in some blanks and executed it:
import java.util.Arrays;
import java.util.Random;
public class Test {
public static int[] genrandarray(int n)
{
int[] a = new int[n];
Random r = new Random();
for(int i=0;i<n;i++) a[i] = r.nextInt();
return a;
}
public static void main(String[] args) {
int[] a = genrandarray(30);
long bgn, end;
bgn = System.currentTimeMillis();
imsort(a, 0, a.length, 30);
end = System.currentTimeMillis();
for(int i = 1; i < a.length; i++){
if(a[i-1] > a[i]){
System.out.println("failed");
break;
}
}
System.out.println("milliseconds " + (end-bgn));
}
public static void imsort(int [] slot, int b, int e, int size) {
//if smaller than 20, use insertion sort
if (e-b<=20) {
Arrays.sort(slot, 0, e);
System.out.println("imsort entered!");
}
else {
mergesort(b, e, slot);
System.out.println("mergesort entered!");
}
}
public static int merge(int n, int m, int[] slot) {
int mid = (n+m)/2;
int a = n, b = mid+1, i, tmp, cmp=0, comp=0;
//sanity check
if (m-n <= 0) return -1000;
while (a <= mid && b <= m) {
cmp = slot[a] - slot[b];
comp++;
if (cmp > 0) { //slot[a] > slot[b]
tmp = slot[b++];
for (i = ++mid; i > a; i--)
slot[i] = slot[i-1];
slot[a++] = tmp;
}
else if (cmp < 0) //slot[a] < slot[b]
a++;
else {
//slot[a] == slot[b]
if (a == mid && b == m) break;
tmp = slot[b++];
a++;
for (i = ++mid; i > a; i--)
slot[i] = slot[i-1]; slot[a++] = tmp;
}
} // end of while loop;
return comp;
} // end of merge
public static int mergesort(int s, int e, int[] slot) {
//int comp =0;
int mid = (s+e)/2;
int right=0, left=0;
if(e-s>0) {
//comp++;
left = mergesort(s,mid, slot);
//comp++;
right = mergesort(mid+1,e, slot);
}
return right + left + merge(s,e,slot);
}
}
The error is caused by setting variable a to n in your merge function and then in the line cmp = slot[a] - slot[b]. Because arrays go from 0 to n-1, n will cause an out of bounds exception.

Sort array into 3 buckets depending on numbers

Given a collection of numbers, I'm trying to sort it into 3 different buckets equally to see how the numbers are relative to one another.
This is the solution I came up with, but I was wondering if there's any case this may fail (positive numbers though). Also, is there a better way for me to do this/improve/look cleaner.
Set<Long> values = api.GetValues();
Set<Long> lowBucket = new HashSet<>();
Set<Long> midBucket = new HashSet<>();
Set<Long> highBucket = new HashSet<>();
Long min = Collections.min(values);
Long max = Collections.max(values);
double lowThreshold = (max - min)/3;
double midThreshold = lowThreshold*2;
for(Long i : values){
if(i < lowThreshold){
lowBucket.add(i);
}else if(i >= lowThreshold && i < midThreshold){
midBucket.add(i);
}else{
highBucket.add(i);
}
}
It seems like you are looking for order statistics. These can be found efficiently using Selection Algorithm.
Once you found the order statistic of the 1/3'th element and 2/3'th element, it's pretty simply to split the elements into the buckets.
For the fun of it, I implemented both sorting and selection algorithms, and compared them.
For relatively small arrays (smaller than ~100), sorting is superior. For larger arrays, selection algorithm is superior. The results are statistical significant according to wilcoxon test.
Code available at ideOne (and appendix of this answer)
import java.util.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
// experiment variables:
public static final int NUM_ELEMENTS = 150;
public static final int NUM_WARMUP_ROUNDS = 100000;
public static final int NUM_EXPERIMENTS = 200;
// aux variables:
private static Random rand = new Random();
private static List<Integer> list;
// selection method implementation:
private static void swap(int[] arr, int idx1, int idx2) {
int temp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = temp;
}
private static int partition(int[] arr, int left, int right) {
int pivotIdx = left + rand.nextInt(right-left);
swap(arr, pivotIdx, right - 1);
right = pivotIdx = right-1;
int pivot = arr[pivotIdx];
while (left < right) {
while (arr[left] < pivot && left < right) left++;
while (arr[right] >= pivot && right > left) right--;
if (left >= right) break;
swap(arr, left, right);
}
// now, left is the first element bigger than pivot.
swap(arr, pivotIdx, left);
return left;
}
public static int quickSelect(int[] arr, int n) {
return quickSelect(arr, n, 0, arr.length);
}
private static int quickSelect(int[] arr, int n, int l, int r) {
int p = partition(arr, l, r);
if (n == p) return arr[p];
if (n < p) return quickSelect(arr, n, l, p + 1);
return quickSelect(arr, n, p, r);
}
// we are assuming arr.length % 3 == 0 for simplicty;
public static int[][] getThreeBucketsSelection(int[] arr) {
int[] first_bucket = new int[arr.length / 3];
int i1 = 0;
int[] second_bucket = new int[arr.length / 3];
int i2 = 0;
int[] third_bucket = new int[arr.length / 3];
int i3 = 0;
int p1 = quickSelect(arr, arr.length / 3);
int p2= quickSelect(arr, (arr.length / 3) * 2);
for (int x : arr) {
if (x < p1) first_bucket[i1++] = x;
else if (x < p2) second_bucket[i2++] = x;
else third_bucket[i3++] = x;
}
return new int[][] {first_bucket, second_bucket, third_bucket};
}
// sorting implementation:
public static int[][] getThreeBucketsSort(int[] arr) {
Arrays.sort(arr);
int[] first_bucket = new int[arr.length / 3];
int i1 = 0;
int[] second_bucket = new int[arr.length / 3];
int i2 = 0;
int[] third_bucket = new int[arr.length / 3];
int i3 = 0;
int i = 0;
while (i < arr.length / 3) first_bucket[i1++] = arr[i++];
while (i < 2 * arr.length / 3) second_bucket[i2++] = arr[i++];
while (i < arr.length) third_bucket[i3++] = arr[i++];
return new int[][] {first_bucket, second_bucket, third_bucket};
}
// experiment methods:
public static int[] createRandomPermutation() {
Collections.shuffle(list);
int[] arr = new int[list.size()];
int i = 0;
for (int x : list) arr[i++] = x;
return arr;
}
public static List<Integer> populateOriginalList(int numElements) {
List<Integer> result = new ArrayList<>();
for (int i = 0; i < numElements; i++) result.add(i);
return result;
}
public static void main (String[] args) throws java.lang.Exception
{
list = populateOriginalList(NUM_ELEMENTS);
long sumWarmUpTime = 0;
for (int i = 0; i < NUM_WARMUP_ROUNDS; i++) {
int[] arr1 = createRandomPermutation();
int[] arr2 = Arrays.copyOf(arr1, arr1.length);
long warmupIter = System.nanoTime();
int[][] buckets1 = getThreeBucketsSelection(arr1);
int[][] buckets2 = getThreeBucketsSort(arr2);
sumWarmUpTime += System.nanoTime() - warmupIter;
}
System.out.println("Done warm up. Took: " + sumWarmUpTime + " nanos");
List<Long> selectionTimes = new ArrayList<>();
List<Long> sortTimes = new ArrayList<>();
long quickSelectTotal = 0;
long sortTotal = 0;
for (int i = 0 ; i < NUM_EXPERIMENTS/2; i++) {
int[] arr1 = createRandomPermutation();
int[] arr2 = Arrays.copyOf(arr1, arr1.length);
// selection:
long quickSelectTime = System.nanoTime();
int[][] buckets1 = getThreeBucketsSelection(arr1);
quickSelectTime = (System.nanoTime() - quickSelectTime);
quickSelectTotal += quickSelectTime;
selectionTimes.add(quickSelectTime);
// sort:
long sortTime = System.nanoTime();
int[][] buckets2 = getThreeBucketsSort(arr2);
sortTime = (System.nanoTime() - sortTime);
sortTotal += sortTime;
sortTimes.add(sortTime);
}
// and flip their order, to make sure no bias:
for (int i = 0 ; i < NUM_EXPERIMENTS/2; i++) {
int[] arr1 = createRandomPermutation();
int[] arr2 = Arrays.copyOf(arr1, arr1.length);
// sort:
long sortTime = System.nanoTime();
int[][] buckets1 = getThreeBucketsSort(arr1);
sortTime = (System.nanoTime() - sortTime);
sortTotal += sortTime;
sortTimes.add(sortTime);
// selection:
long quickSelectTime = System.nanoTime();
int[][] buckets2 = getThreeBucketsSelection(arr2);
quickSelectTime = (System.nanoTime() - quickSelectTime);
quickSelectTotal += quickSelectTime;
selectionTimes.add(quickSelectTime);
}
System.out.println("values for wilcoxon test");
System.out.println("sort times: " + sortTimes);
System.out.println("selection times: " + selectionTimes);
System.out.println("Bottom lime results: ");
System.out.println("sort: " + sortTotal);
System.out.println("selection: " + quickSelectTotal);
}
}/* package whatever; // don't place package name! */
Note: The above code is a simplification. Specifically, it does not handle dupe elements well, and assumes input array is a multiple of 3.
These both can be easily solved with very little performance penalty.
Looks fine to me, but you could simplify the check a bit:
if(i < lowThreshold){
lowBucket.add(i);
}else if(i < midThreshold){
midBucket.add(i);
}else{
highBucket.add(i);
}

Multithreaded merge sort, adding additional threads

I am facing one problem in multithreaded merge sort algorithm in java.
I should modify the code into 3,4,5,6,7,8 threaded merge sorting by dividing original array into subArrays. Currently it has 2 subArrays.
How can I split original array into 3, 4 ,5,6,7,8 subArrays to achive my goal?
Moreover, I should write some more methods because mergeSort method calls lefthalf and righthalf methods at the moment. So for 3,4,5,6,7,8 threads I should write additional methods.
How can i handle this?
two_threaded_merge_sort.java
public class two_threaded_merge_sort {
public static void finalMerge(int[] a, int[] b) {
int[] result = new int[a.length + b.length];
int i=0;
int j=0;
int r=0;
while (i < a.length && j < b.length) {
if (a[i] <= b[j]) {
result[r]=a[i];
i++;
r++;
} else {
result[r]=b[j];
j++;
r++;
}
if (i==a.length) {
while (j<b.length) {
result[r]=b[j];
r++;
j++;
}
}
if (j==b.length) {
while (i<a.length) {
result[r]=a[i];
r++;
i++;
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Random rand = new Random();
int[] original = new int[9000000];
for (int i=0; i<original.length; i++) {
original[i] = rand.nextInt(1000);
}
long startTime = System.currentTimeMillis();
int[] subArr1 = new int[original.length/2];
int[] subArr2 = new int[original.length - original.length/2];
System.arraycopy(original, 0, subArr1, 0, original.length/2);
System.arraycopy(original, original.length/2, subArr2, 0, original.length - original.length/2);
Worker runner1 = new Worker(subArr1);
Worker runner2 = new Worker(subArr2);
runner1.start();
runner2.start();
runner1.join();
runner2.join();
finalMerge (runner1.getInternal(), runner2.getInternal());
long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
System.out.println("2-thread MergeSort takes: " + (float)elapsedTime/1000 + " seconds");
}
}
Worker.java
class Worker extends Thread {
private int[] internal;
public int[] getInternal() {
return internal;
}
public void mergeSort(int[] array) {
if (array.length > 1) {
int[] left = leftHalf(array);
int[] right = rightHalf(array);
mergeSort(left);
mergeSort(right);
merge(array, left, right);
}
}
public int[] leftHalf(int[] array) {
int size1 = array.length / 2;
int[] left = new int[size1];
for (int i = 0; i < size1; i++) {
left[i] = array[i];
}
return left;
}
public int[] rightHalf(int[] array) {
int size1 = array.length / 2;
int size2 = array.length - size1;
int[] right = new int[size2];
for (int i = 0; i < size2; i++) {
right[i] = array[i + size1];
}
return right;
}
public void merge(int[] result, int[] left, int[] right) {
int i1 = 0;
int i2 = 0;
for (int i = 0; i < result.length; i++) {
if (i2 >= right.length || (i1 < left.length && left[i1] <= right[i2])) {
result[i] = left[i1];
i1++;
} else {
result[i] = right[i2];
i2++;
}
}
}
Worker(int[] arr) {
internal = arr;
}
public void run() {
mergeSort(internal);
}
}
Thanks very much!
There needs to be a sort function that separates the array into k parts, then create k threads to sort each part, using either top down or bottom up approach, (bottom up would slightly faster), and wait for all threads to complete.
At this point there are k sorted parts. These could be merged all at once using a k-way merge (complicated), or merged a pair of parts at a time (2 way merge), perhaps using multiple threads, but at this point the process is probably memory bandwidth limited, so multi-threading may not help much.
When separating the array into k parts, something like this can be used to keep the sizes similar:
int r = n % k;
int s = n / k;
int t;
for each part{
t = r ? 1 : 0;
r -= t;
size = s + t;
}
or
int r = n % k;
int s = n / k + 1;
while(r--){
next part size = s; // n / k + 1
}
s -= 1;
while not done{
next part size = s; // n / k
}
From my point of view, your hard work is done. now you must parametrize the algorithm with number of threads.
Your algorithm has two parts
split the work.
merge the k-parts.
And two components:
Main algorithm
Workers.
About the threads
In my opinion, Start/join method aren't useful in this case, because last merging can't start until all threads are finish. I prefer '2 way merge' (#rcgldr answer) and a thread pool (ExecutorService).
You must be careful with threads synchronization and shared memory.
To sum up, I propose a little different solution:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class MultithreadedMergeSort {
private int[] array;
private int numThreads;
private List<int[]> sortedFragments;
private MultithreadedMergeSort(int numThreads, int[] array) {
this.numThreads = numThreads;
this.array = array;
}
// Basic algorithm: it sort recursively a fragment
private static void recursiveMergeSort(int[] array, int begin, int end) {
if (end - begin > 1) {
int middle = (begin + end) / 2;
recursiveMergeSort(array, begin, middle);
recursiveMergeSort(array, middle, end);
merge(array, begin, middle, end);
}
}
// Basic algorithm: it merges two consecutives sorted fragments
private static void merge(int[] array, int begin, int middle, int end) {
int[] firstPart = Arrays.copyOfRange(array, begin, middle);
int i = 0;
int j = middle;
int k = begin;
while (i < firstPart.length && j < end) {
if (firstPart[i] <= array[j]) {
array[k++] = firstPart[i++];
} else {
array[k++] = array[j++];
}
}
if (i < firstPart.length) {
System.arraycopy(firstPart, i, array, k, firstPart.length - i);
}
}
public static void sort(int[] array, int numThreads) throws InterruptedException {
if (array != null && array.length > 1) {
if (numThreads > 1) {
new MultithreadedMergeSort(numThreads, array).mergeSort();
} else {
recursiveMergeSort(array, 0, array.length);
}
}
}
private synchronized void mergeSort() throws InterruptedException {
// A thread pool
ExecutorService executors = Executors.newFixedThreadPool(numThreads);
this.sortedFragments = new ArrayList<>(numThreads - 1);
int begin = 0;
int end = 0;
// it split the work
for (int i = 1; i <= (numThreads - 1); i++) {
begin = end;
end = (array.length * i) / (numThreads - 1);
// sending the work to worker
executors.execute(new MergeSortWorker(begin, end));
}
// this is waiting until work is done
wait();
// shutdown the thread pool.
executors.shutdown();
}
private synchronized int[] notifyFragmentSorted(int begin, int end) {
if (begin > 0 || end < array.length) {
// the array is not completely sorted
Iterator<int[]> it = sortedFragments.iterator();
// searching a previous or next fragment
while (it.hasNext()) {
int[] f = it.next();
if (f[1] == begin || f[0] == end) {
// It found a previous/next fragment
it.remove();
return f;
}
}
sortedFragments.add(new int[]{begin, end});
} else {
// the array is sorted
notify();
}
return null;
}
private class MergeSortWorker implements Runnable {
int begin;
int end;
public MergeSortWorker(int begin, int end) {
this.begin = begin;
this.end = end;
}
#Override
public void run() {
// Sort a fragment
recursiveMergeSort(array, begin, end);
// notify the sorted fragment
int[] nearFragment = notifyFragmentSorted(begin, end);
while (nearFragment != null) {
// there's more work: merge two consecutives sorted fragments, (begin, end) and nearFragment
int middle;
if (nearFragment[0] < begin) {
middle = begin;
begin = nearFragment[0];
} else {
middle = nearFragment[0];
end = nearFragment[1];
}
merge(array, begin, middle, end);
nearFragment = notifyFragmentSorted(begin, end);
}
}
}
public static void main(String[] args) throws InterruptedException {
int numThreads = 5;
Random rand = new Random();
int[] original = new int[9000000];
for (int i = 0; i < original.length; i++) {
original[i] = rand.nextInt(1000);
}
long startTime = System.currentTimeMillis();
MultithreadedMergeSort.sort(original, numThreads);
long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
// warning: Take care with microbenchmarks
System.out.println(numThreads + "-thread MergeSort takes: " + (float) elapsedTime / 1000 + " seconds");
}
}

Algorithm comparison in java: test program does not work

I'm trying to compare the execution of the java implementation of QuickSort and its hybrid version (using InsertionSort for those partitions which are smaller than an integer k). I wrote a test class to analyze the behaviour of the algorithms for some values ok k (1 <= k <= 25). For each value of k the class compares for different sizes of the input array the two algorithms.
I can't run the program for some values of the size of the array, for instance for values greater than 4000. The execution reach some different values and then freeze, after a while it will finish but I have no output of the computation. (I'm using eclipse).
What could be the problem? I wish to perform the comparation of the two algoritms for an array size from 10 to 10000 (at least). The code is listed below:
public class Main {
private static final int MAX_K = 25;
private static final int MAX_SIZE = 4500;
private static final int ADD_SIZE = 100;
private static int size = 10;
private static QuickSort qSort;
private static HybridSort hSort;
private static void initArray(int[] A) {
Random rand = new Random();
for (int i = 0; i < A.length; i++) {
// A[i] = (int)(Math.random()*100000);
A[i] = rand.nextInt();
}
}
private static int[] A = new int[10];
private static int[] B = new int[10];
public static void main(String[] args) {
try {
FileWriter fstream = new FileWriter("out.txt");
BufferedWriter out = new BufferedWriter(fstream);
out.write("Init file");
qSort = new QuickSort();
hSort = new HybridSort();
/************************************************/
/* Comparison */
/************************************************/
for (int i = 1; i <= MAX_K; i++) {
hSort.setK(i);
int p = 0;
for (int j = size; j <= MAX_SIZE; j = j + ADD_SIZE) {
A = new int[j];
B = new int[j];
initArray(A);
initArray(B);
long sTime = System.nanoTime();
qSort.quickSort(A, 0, A.length - 1);
long qDuration = System.nanoTime() - sTime;
sTime = System.nanoTime();
hSort.hybridSort(B, 0, B.length - 1);
long hDuration = System.nanoTime() - sTime;
out.append(/* "\nA: " +printArray(A)+ */"K: " + i + " A["
+ j + "]\tQ = " + qDuration + " H = " + hDuration
+ "\n");
String h = Long.toString(hDuration);
String q = Long.toString(qDuration);
if (h.length() < q.length()) {
p++;
out.append("\t#OUTPERM for K: "
+ i
+ "\t\t"
+ hDuration
+ "\t\t < \t\t "
+ qDuration
+ "\t\t\t\t| A[]\t\t"
+ A.length
+ ((q.length() - h.length()) == 2 ? "\t Magn. 2"
: "") + "\n");
}
}
if (p > 0)
out.append("#P= " + p + " for K= " + i + "\n\n");
}
out.append("Close file");
out.close();
} catch (IOException e) {
}
}
}
The algorithm classes:
public class QuickSort {
public void quickSort(int[] A, int left, int right){
if (left < right) {
int m = Partition(A, left, right);
quickSort(A, left, m-1);
quickSort(A, m, right);
}
}
private int Partition(int[] A, int left, int right){
int pivot = A[right];
int i = left;
int j = right;
while (true) {
while ( (A[j] > pivot)) {
j--;
}
while ((A[i] < pivot)) {
i++;
}
if (i < j){
int swap = A[j];
A[j] = A[i];
A[i] = swap;
}else{
return i;
}
}
}
}
public class HybridSort {
int k;
int m;
InsertionSort iSort;
public HybridSort() {
k = 3;
iSort = new InsertionSort();
}
public void hybridSort(int[] A, int left, int right) {
if (left < right) {
if ((right - left) < k) {
iSort.sort(A,left,right);
} else {
m = Partition(A, left, right);
hybridSort(A, left, m - 1);
hybridSort(A, m, right);
}
}
}
private int Partition(int[] A, int left, int right) {
int pivot = A[right];
int i = left;
int j = right;
while (true) {
while ((A[j] > pivot) && (j >= 0)) {
j--;
}
while ((A[i] < pivot) && (i < A.length)) {
i++;
}
if (i < j) {
int swap = A[j];
A[j] = A[i];
A[i] = swap;
} else {
return i;
}
}
}
public void setK(int k) {
this.k = k;
}
}
Your implementation of Partition is not correct. Consider the small test below (I made Partition static for my convenience).
Both while loops won't be executed, because A[i] == A[j] == pivot. Moreover, i<j, so the two elements will be swapped, resulting in exactly the same array. Therefore, the outer while loop becomes infinite.
The same problem occurs for any array for which the first and last element are the same.
public class Test {
public static void main(String[] args) {
int[] A = {1, 1};
Partition(A, 0, 1);
}
private static int Partition(int[] A, int left, int right){
int pivot = A[right];
int i = left;
int j = right;
while (true) {
while ( (A[j] > pivot)) {
j--;
}
while ((A[i] < pivot)) {
i++;
}
if (i < j){
int swap = A[j];
A[j] = A[i];
A[i] = swap;
}else{
return i;
}
}
}
}
Have you tried increasing memory settings for your code to run in eclipse.
You may find this Setting memory of Java programs that runs from Eclipse helpful.
Some tips / possible solution?:
I haven't read your implementation of QuickSort or HybridSort but I am assuming they are correct.
If you are comparing the performance of two algorithms you should most definitely compare their performance to indentical inputs. Currently you are generating two random arrys (albeit of the same size). This isn't necessarily going to be an accurate test as I can easily find a test case where one algorithm will outperform the other if the random generator is out to troll you.
Your logic for comparing the two algorithms is a bit weird and incorrect according to me. Why do you compare the lengths of the strings of the times? according to your logic 1 is the same as 9 and 1,000,000,000 is the same as 9,999,999,999 which is clearly incorrect. One algorithm is almost 10 times faster than the other.
Moreover, one reason for no output might be the reason that you are only outputing when hybridsort is better than quicksort and not the other way around. I am sure there are other reasons as well but this could be one easily noticable reason (if your implementations are incorrect).
I do notice that you close your outputstream which is good as that is a very common reason why there is no output. You should however, close steams in the finally section of the try-catch as then they are guaranteed to close. You could be getting an IOException and in your case this would also not close the outputsteam and consequently lead to no ouput in your file.
Here is a sample structure that I would follow for doing any comparitive testing. It is easy to read and easy to debug with enough output for you to figure out which algorithm performs better. This is merely a suggestion.
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Random;
public class Tester {
private static int[] initArray(int size) {
Random rand = new Random();
int[] arr = new int[size];
for (int i = 0; i < arr.length; i++) {
arr[i] = rand.nextInt();
}
return arr;
}
public static void main(String[] args) {
final int MAX_ITERATIONS = 25;
final int INITIAL_ARRAY_SIZE = 10;
final int MAX_ARRAY_SIZE = 4500;
final int ARRAY_SIZE_INCREMENT = 100;
long start;
int[] score = null;
PrintWriter out = null;
try {
out = new PrintWriter(new FileOutputStream("out.txt"));
for (int arraySize = INITIAL_ARRAY_SIZE; arraySize <= MAX_ARRAY_SIZE; arraySize += ARRAY_SIZE_INCREMENT) {
// score[0] is for quickSort and score[1] is for hybridSort
score = new int[2];
for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
int[] testArray = initArray(arraySize);
int[] testArrayCopy = new int[arraySize];
System.arraycopy(testArray, 0, testArrayCopy, 0, arraySize);
start = System.nanoTime();
// Do quicksort here using testArray
long qSortfinish = System.nanoTime() - start;
System.arraycopy(testArray, 0, testArrayCopy, 0, arraySize);
start = System.nanoTime();
// Do hybridsort here using testArrayCopy
long hybridSortfinish = System.nanoTime() - start;
// Keep score
if (qSortfinish < hybridSortfinish)
score[0]++;
else if (qSortfinish > hybridSortfinish) {
score[1]++;
} else {
score[0]++;
score[1]++;
}
}
out.println("Array Size: " + arraySize + " QuickSort: " + score[0] + " HybridSort: " + score[1]);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (out != null)
out.close();
}
}
}

Merge Sort Java

I am trying to make a merge sort method, but it keeps on giving the wrong sorts. Where do I have change to make it actually sort the array? What part of the code has to be different? Thank you for your time.
public static void mergeSort(int[] array, int left, int lHigh, int right, int rHigh) {
int elements = (rHigh - lHigh +1) ;
int[] temp = new int[elements];
int num = left;
while ((left <= lHigh) && (right <= rHigh)){
if (a[left] <= array[right]) {
temp[num] = array[left];
left++;
}
else {
temp[num] = array[right];
right++;
}
num++;
}
while (left <= right){
temp[num] = array[left]; // I'm getting an exception here, and is it because of the num???
left += 1;
num += 1;
}
while (right <= rHigh) {
temp[num] = array[right];
right += 1;
num += 1;
}
for (int i=0; i < elements; i++){
array[rHigh] = temp[rHigh];
rHigh -= 1;
}
EDIT: now the mergeSort doesn't really sort the numbers, can someone tell me where it specifically is? especially when I print the "Testing merge sort" part.
First of all, I'm assuming this is academic rather than practical, since you're not using a built in sort function. That being said, here's some help to get you moving in the right direction:
Usually, one can think of a merge sort as two different methods: a merge() function that merges two sorted lists into one sorted list, and mergeSort() which recursively breaks the list into single element lists. Since a single element list is sorted already, you then merge all the lists together into one big sorted list.
Here's some off-hand pseudo-code:
merge(A, B):
C = empty list
While A and B are not empty:
If the first element of A is smaller than the first element of B:
Remove first element of A.
Add it to the end of C.
Otherwise:
Remove first element of B.
Add it to the end of C.
If A or B still contains elements, add them to the end of C.
mergeSort(A):
if length of A is 1:
return A
Split A into two lists, L and R.
Q = merge(mergeSort(L), mergeSort(R))
return Q
Maybe that'll help clear up where you want to go.
If not, there's always MergeSort at wikipedia.
Additional:
To help you out, here are some comments inline in your code.
public static void mergeSort(int[] array, int left, int lHigh, int right, int rHigh) {
// what do lHigh and rHigh represent?
int elements = (rHigh - lHigh +1) ;
int[] temp = new int[elements];
int num = left;
// what does this while loop do **conceptually**?
while ((left <= lHigh) && (right <= rHigh)){
if (a[left] <= a[right]) {
// where is 'pos' declared or defined?
temp[pos] = a[left];
// where is leftLow declared or defined? Did you mean 'left' instead?
leftLow ++;
}
else {
temp[num] = a[right];
right ++;
}
num++;
}
// what does this while loop do **conceptually**?
while (left <= right){
// At this point, what is the value of 'num'?
temp[num] = a[left];
left += 1;
num += 1;
}
while (right <= rHigh) {
temp[num] = a[right];
right += 1;
num += 1;
}
// Maybe you meant a[i] = temp[i]?
for (int i=0; i < elements; i++){
// what happens if rHigh is less than elements at this point? Could
// rHigh ever become negative? This would be a runtime error if it did
a[rHigh] = temp[rHigh];
rHigh -= 1;
}
I'm purposefully being vague so you think about the algorithm. Try inserting your own comments into the code. If you can write what is conceptually happening, then you may not need Stack Overflow :)
My thoughts here are that you are not implementing this correctly. This is because it looks like you're only touching the elements of the array only once (or close to only once). This means you have a worst case scenario of O(N) Sorting generally takes at least O(N * log N) and from what I know, the simpler versions of merge sort are actually O(N^2).
More:
In the most simplistic implementation of merge sort, I would expect to see some sort of recursion in the mergeSort() method. This is because merge sort is generally defined recursively. There are ways to do this iteratively using for and while loops, but I definitely don't recommend it as a learning tool until you get it recursively.
Honestly, I suggest taking either my pseudo-code or the pseudo-code you may find in a wikipedia article to implement this and start over with your code. If you do that and it doesn't work correctly still, post it here and we'll help you work out the kinks.
Cheers!
And finally:
// Precondition: array[left..lHigh] is sorted and array[right...rHigh] is sorted.
// Postcondition: array[left..rHigh] contains the same elements of the above parts, sorted.
public static void mergeSort(int[] array, int left, int lHigh, int right, int rHigh) {
// temp[] needs to be as large as the number of elements you're sorting (not half!)
//int elements = (rHigh - lHigh +1) ;
int elements = rHigh - left;
int[] temp = new int[elements];
// this is your index into the temp array
int num = left;
// now you need to create indices into your two lists
int iL = left;
int iR = right;
// Pseudo code... when you code this, make use of iR, iL, and num!
while( temp is not full ) {
if( left side is all used up ) {
copy rest of right side in.
make sure that at the end of this temp is full so the
while loop quits.
}
else if ( right side is all used up) {
copy rest of left side in.
make sure that at the end of this temp is full so the
while loop quits.
}
else if (array[iL] < array[iR]) { ... }
else if (array[iL] >= array[iR]) { ... }
}
}
public class MergeSort {
public static void main(String[] args) {
int[] arr = {5, 4, 7, 2, 3, 1, 6, 2};
print(arr);
new MergeSort().sort(arr, 0, arr.length - 1);
}
private void sort(int[] arr, int lo, int hi) {
if (lo < hi) {
int mid = (lo + hi) / 2;
sort(arr, lo, mid); // recursive call to divide the sub-list
sort(arr, mid + 1, hi); // recursive call to divide the sub-list
merge(arr, lo, mid, hi); // merge the sorted sub-lists.
print(arr);
}
}
private void merge(int[] arr, int lo, int mid, int hi) {
// allocate enough space so that the extra 'sentinel' value
// can be added. Each of the 'left' and 'right' sub-lists are pre-sorted.
// This function only merges them into a sorted list.
int[] left = new int[(mid - lo) + 2];
int[] right = new int[hi - mid + 1];
// create the left and right sub-list for merging into original list.
System.arraycopy(arr, lo, left, 0, left.length - 1);
System.arraycopy(arr, mid + 1, right, 0, left.length - 1);
// giving a sentinal value to marking the end of the sub-list.
// Note: The list to be sorted is assumed to contain numbers less than 100.
left[left.length - 1] = 100;
right[right.length - 1] = 100;
int i = 0;
int j = 0;
// loop to merge the sorted sequence from the 2 sub-lists(left and right)
// into the main list.
for (; lo <= hi; lo++) {
if (left[i] <= right[j]) {
arr[lo] = left[i];
i++;
} else {
arr[lo] = right[j];
j++;
}
}
}
// print the array to console.
private static void print(int[] arr) {
System.out.println();
for (int i : arr) {
System.out.print(i + ", ");
}
}
}
Here's another!
private static int[] mergeSort(int[] input){
if (input.length == 1)
return input;
int length = input.length/2;
int[] left = new int[length];
int[] right = new int[input.length - length];
for (int i = 0; i < length; i++)
left[i] = input[i];
for (int i = length; i < input.length; i++)
right[i-length] = input[i];
return merge(mergeSort(left),mergeSort(right));
}
private static int[] merge(int[] left, int[] right){
int[] merged = new int[left.length+right.length];
int lengthLeft = left.length;
int lengthRight = right.length;
while (lengthLeft > 0 && lengthRight > 0){
if (left[left.length - lengthLeft] < right[right.length - lengthRight]){
merged[merged.length -lengthLeft-lengthRight] = left[left.length - lengthLeft];
lengthLeft--;
}else{
merged[merged.length - lengthLeft-lengthRight] = right[right.length - lengthRight];
lengthRight--;
}
}
while (lengthLeft > 0){
merged[merged.length - lengthLeft] = left[left.length-lengthLeft];
lengthLeft--;
}
while (lengthRight > 0){
merged[merged.length - lengthRight] = right[right.length-lengthRight];
lengthRight--;
}
return merged;
}
static void mergeSort(int arr[],int p, int r) {
if(p<r) {
System.out.println("Pass "+k++);
int q = (p+r)/2;
mergeSort(arr,p,q);
mergeSort(arr,q+1,r);
//System.out.println(p+" "+q+" "+r);
merge(arr,p,q,r);
}
}
static void merge(int arr[],int p,int q,int r) {
int temp1[],temp2[];
//lower limit array
temp1 = new int[q-p+1];
//upper limit array
temp2 = new int[r-q];
for(int i=0 ; i< (q-p+1); i++){
temp1[i] = arr[p+i];
}
for(int j=0; j< (r-q); j++){
temp2[j] = arr[q+j+1];
}
int i = 0,j=0;
for(int k=p;k<=r;k++){
// This logic eliminates the so called sentinel card logic mentioned in Coreman
if(i!= temp1.length
&& (j==temp2.length || temp1[i] < temp2[j])
) {
arr[k] = temp1[i];
// System.out.println(temp1[i]);
i++;
}
else {
//System.out.println(temp2[j]);
arr[k] = temp2[j];
j++;
}
}
}
>
Merge Sort Using Sentinel
This codes works perfectly fine.
public void mergeSort(int a[], int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
mergeSort(a, low, mid);
mergeSort(a, mid + 1, high);
merge(a, low, mid, high);
}
}
public void merge(int a[], int low, int mid, int high) {
int n1 = mid - low + 1;// length of an array a1
int n2 = high - mid; // length of an array a2
int a1[] = new int[n1 + 1];
int a2[] = new int[n2 + 1];
int lowRange = low;
for (int i = 0; i < n1; i++) {
a1[i] = a[lowRange];
lowRange++;
}
for (int j = 0; j < n2; j++) {
a2[j] = a[mid + j + 1];
}
a1[n1] = Integer.MAX_VALUE; // inserting sentinel at the end of array a1
a2[n2] = Integer.MAX_VALUE; // inserting sentinel at the end of array a2
int i = 0;
int j = 0;
int k = low;
for (k = low; k <= high; k++) {
if (a1[i] >= a2[j]) {
a[k] = a2[j];
j++;
} else {
a[k] = a1[i];
i++;
}
}
if (a2.length >= a1.length) {
for (int ab = k; ab < a2.length; ab++) {
a[k] = a2[ab];
k++;
}
} else if (a1.length >= a2.length) {
for (int ab = k; ab < a1.length; ab++) {
a[k] = a1[ab];
k++;
}
}
}
Here's another alternative:
public class MergeSort {
public static void merge(int[]a,int[] aux, int f, int m, int l) {
for (int k = f; k <= l; k++) {
aux[k] = a[k];
}
int i = f, j = m+1;
for (int k = f; k <= l; k++) {
if(i>m) a[k]=aux[j++];
else if (j>l) a[k]=aux[i++];
else if(aux[j] > aux[i]) a[k]=aux[j++];
else a[k]=aux[i++];
}
}
public static void sort(int[]a,int[] aux, int f, int l) {
if (l<=f) return;
int m = f + (l-f)/2;
sort(a, aux, f, m);
sort(a, aux, m+1, l);
merge(a, aux, f, m, l);
}
public static int[] sort(int[]a) {
int[] aux = new int[a.length];
sort(a, aux, 0, a.length-1);
return a;
}
}
Here is a simple merge sort algorithm in Java:
Good Tip: Always use int middle = low + (high-low)/2 instead of int middle = (low + high)/2.
public static int[] mergesort(int[] arr) {
int lowindex = 0;
int highindex = arr.length-1;
mergesort(arr, lowindex, highindex);
return arr;
}
private static void mergesort(int[] arr, int low, int high) {
if (low == high) {
return;
} else {
int midIndex = low + (high-low)/2;
mergesort(arr, low, midIndex);
mergesort(arr, midIndex + 1, high);
merge(arr, low, midIndex, high);
}
}
private static void merge(int[] arr, int low, int mid, int high) {
int[] left = new int[mid-low+2];
for (int i = low; i <= mid; i++) {
left[i-low] = arr[i];
}
left[mid-low+1] = Integer.MAX_VALUE;
int[] right = new int[high-mid+1];
for (int i = mid+1; i <= high; i++) {
right[i-mid-1] = arr[i];
}
right[high - mid] = Integer.MAX_VALUE;
int i = 0;
int j = 0;
for (int k = low; k <= high; k++) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
}
}
package com.sortalgo;
import java.util.Arrays;
public class MyMSort {
private static void merge(int[] array, int[] result, int low, int mid, int high) {
int k =low, i=low; int j=mid+1;
while(i<=mid && j<=high) {
if(array[i]<= array[j]) {
result[k++]=array[i++];
}else {
result[k++]=array[j++];
}
}
while(i<=mid) {
result[k++]=array[i++];
}
while(j<=high) {
result[k++]=array[j++];
}
for(i=low;i<=high;i++) {
array[i]=result[i];
}
}
private static void mergeSort(int[] array, int[] result, int low, int high) {
if(high == low) {
return ;
}
int mid = (low + high)/2;
mergeSort(array,result, low, mid );
mergeSort(array,result, mid+1, high );
merge(array, result, low, mid, high);
}
public static void main(String[] args) {
int[] array = {8,4,3,12,25,6,13,10};
int[] result = new int[array.length];
mergeSort(array, result, 0, array.length-1 );
for(int i=0; i<=array.length-1;i++) {
System.out.println(array[i]);
}
}
}

Categories

Resources