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();
}
}
}
Related
I am trying to solve the problem of calculating the number of inversions where the problem is:
`An inversion of a sequence 𝑎0, 𝑎1, . . . , 𝑎𝑛−1 is a pair of indices 0 ≤ 𝑖 < 𝑗 < 𝑛 such
that 𝑎𝑖 > 𝑎𝑗. The number of inversions of a sequence in some sense measures how
close the sequence is to being sorted. For example, a sorted (in non-descending
order) the sequence contains no inversions at all, while in a sequence sorted in descending
order any two elements constitute an inversion (for a total of 𝑛(𝑛 − 1)/2
inversions).
Sample Input is:
6
9 8 7 3 2 1
The output will be:
15
`
Now for this, I am trying to merge the sort algorithm and the idea is whenever I will see nextNo. greater than prevNo. I will add count which is 0 initially.
This is the Merge algorithm:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class MergeSort {
static void Merge(int arr[],int l,int m, int r){
int n1 = m-l+1;
int n2 = r-m;
int L[] = new int[n1];
int R[] = new int[n2];
for(int i=0;i<n1;i++)
L[i] = arr[i+l];
for(int i=0;i<n2;i++)
R[i] = arr[m+1+i];
int i=0,j=0;
int k=l;
while(i<n1&&j<n2){
if(L[i]<R[j]){
arr[k]=L[i];
i++;
}
else{
arr[k]=R[j];
j++;
}
k++;
}
while(i<n1){
arr[k] =L[i];
i++;
k++;
}
while(j<n2){
arr[k] =R[j];
j++;
k++;
}
}
static void MergeSortBasic(int arr[],int l,int r) {
if(l<r){
int m = (l+r)/2;
MergeSortBasic(arr,l,m);
MergeSortBasic(arr,m+1,r);
Merge(arr,l,m,r);
}
}
public static void main(String[] args) {
QuickSortAlgo.FastScanner scanner = new QuickSortAlgo.FastScanner(System.in);
int n = scanner.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = scanner.nextInt();
}
MergeSortBasic(a,0,n-1);
for (int i = 0; i < n; i++) {
System.out.print(a[i] + " ");
}
}
static class FastScanner {
BufferedReader br;
StringTokenizer st;
FastScanner(InputStream stream) {
try {
br = new BufferedReader(new InputStreamReader(stream));
} catch (Exception e) {
e.printStackTrace();
}
}
String next() {
while (st == null || !st.hasMoreTokens()) {
try {
st = new StringTokenizer(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
return st.nextToken();
}
int nextInt() {
return Integer.parseInt(next());
}
}
}
This I understand but here is the solution to this problem that I came across which I could not understand. Can please someone help me in understanding the solution of this algorithm especially the midpoint/ave part.
Solution:
import java.util.*;
public class Inversions {
private static long merge(int[] a, int[] b, int left, int ave, int right) {
int i = left, j = ave, k = left;
long inv_count = 0;
while (i <= ave - 1 && j <= right) {
if (a[i] <= a[j]) {
b[k] = a[i];
i++;
} else {
b[k] = a[j];
inv_count += ave - i;
j++;
}
k++;
}
while (i <= ave - 1) {
b[k] = a[i];
i++;
k++;
}
while (j <= right) {
b[k] = a[j];
j++;
k++;
}
for (i = left; i <= right; i++) {
a[i] = b[i];
}
return inv_count;
}
private static long getNumberOfInversions(int[] a, int[] b, int left, int right) {
long inv_count = 0;
if (right <= left) {
return inv_count;
}
int ave = left + (right - left) / 2;
inv_count += getNumberOfInversions(a, b, left, ave);
inv_count += getNumberOfInversions(a, b, ave + 1, right);
inv_count += merge(a, b, left, ave + 1, right);
return inv_count;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = scanner.nextInt();
}
int[] b = new int[n];
System.out.println(getNumberOfInversions(a, b, 0, a.length - 1));
}
}
My question is Why do we have
inv_count += ave - i;
Instead of simply:
inv_count++;
Like what is the difference between these two programs?? How is this ave variable working? Also, any idea how can I learn this effectively in the future?
why: inv_count += ave - i;
The two sub-arrays being merged are already sorted from prior recursions (or an end case where sub-array size is 1 element). Each time an element from the right sub-array is found to be less than the current element in the left sub-array (a[j] < a[i]), the number of elements remaining in the left sub-array (ave - i) is added to the inversion count, because a[j] is less than all of the elements from a[i] through a[ave-1].
You are given an array A of integers and an integer k. Implement an algorithm that determines, in linear time, the smallest integer that appears at least k times in A.
I have been struggling with this problem for awhile, coding in Java, I need to use a HashTable to find the smallest integer that appears at least k times, it also must be in linear time.
This is what I attempted but it does not pass any of the tests
private static int problem1(int[] arr, int k)
{
// Implement me!
HashMap<Integer, Integer> table = new HashMap<Integer, Integer>();
int ans = Integer.MAX_VALUE;
for (int i=0; i < arr.length; i++) {
if(table.containsKey(arr[i])) {
table.put(arr[i], table.get(arr[i]) + 1);
if (k <= table.get(arr[i])) {
ans = Math.min(ans, arr[i]);
}
}else{
table.put(arr[i], 1);
}
}
return ans;
}
Here is the empty code with all of the test cases:
import java.io.*;
import java.util.*;
public class Lab5
{
/**
* Problem 1: Find the smallest integer that appears at least k times.
*/
private static int problem1(int[] arr, int k)
{
// Implement me!
return 0;
}
/**
* Problem 2: Find two distinct indices i and j such that A[i] = A[j] and |i - j| <= k.
*/
private static int[] problem2(int[] arr, int k)
{
// Implement me!
int i = -1;
int j = -1;
return new int[] { i, j };
}
// ---------------------------------------------------------------------
// Do not change any of the code below!
private static final int LabNo = 5;
private static final String quarter = "Fall 2020";
private static final Random rng = new Random(123456);
private static boolean testProblem1(int[][] testCase)
{
int[] arr = testCase[0];
int k = testCase[1][0];
int answer = problem1(arr.clone(), k);
Arrays.sort(arr);
for (int i = 0, j = 0; i < arr.length; i = j)
{
for (; j < arr.length && arr[i] == arr[j]; j++) { }
if (j - i >= k)
{
return answer == arr[i];
}
}
return false; // Will never happen.
}
private static boolean testProblem2(int[][] testCase)
{
int[] arr = testCase[0];
int k = testCase[1][0];
int[] answer = problem2(arr.clone(), k);
if (answer == null || answer.length != 2)
{
return false;
}
Arrays.sort(answer);
// Check answer
int i = answer[0];
int j = answer[1];
return i != j
&& j - i <= k
&& i >= 0
&& j < arr.length
&& arr[i] == arr[j];
}
public static void main(String args[])
{
System.out.println("CS 302 -- " + quarter + " -- Lab " + LabNo);
testProblems(1);
testProblems(2);
}
private static void testProblems(int prob)
{
int noOfLines = prob == 1 ? 100000 : 500000;
System.out.println("-- -- -- -- --");
System.out.println(noOfLines + " test cases for problem " + prob + ".");
boolean passedAll = true;
for (int i = 1; i <= noOfLines; i++)
{
int[][] testCase = null;
boolean passed = false;
boolean exce = false;
try
{
switch (prob)
{
case 1:
testCase = createProblem1(i);
passed = testProblem1(testCase);
break;
case 2:
testCase = createProblem2(i);
passed = testProblem2(testCase);
break;
}
}
catch (Exception ex)
{
passed = false;
exce = true;
}
if (!passed)
{
System.out.println("Test " + i + " failed!" + (exce ? " (Exception)" : ""));
passedAll = false;
break;
}
}
if (passedAll)
{
System.out.println("All test passed.");
}
}
private static int[][] createProblem1(int testNo)
{
int size = rng.nextInt(Math.min(1000, testNo)) + 5;
int[] numbers = getRandomNumbers(size, size);
Arrays.sort(numbers);
int maxK = 0;
for (int i = 0, j = 0; i < size; i = j)
{
for (; j < size && numbers[i] == numbers[j]; j++) { }
maxK = Math.max(maxK, j - i);
}
int k = rng.nextInt(maxK) + 1;
shuffle(numbers);
return new int[][] { numbers, new int[] { k } };
}
private static int[][] createProblem2(int testNo)
{
int size = rng.nextInt(Math.min(1000, testNo)) + 5;
int[] numbers = getRandomNumbers(size, size);
int i = rng.nextInt(size);
int j = rng.nextInt(size - 1);
if (i <= j) j++;
numbers[i] = numbers[j];
return new int[][] { numbers, new int[] { Math.abs(i - j) } };
}
private static void shuffle(int[] arr)
{
for (int i = 0; i < arr.length - 1; i++)
{
int rndInd = rng.nextInt(arr.length - i) + i;
int tmp = arr[i];
arr[i] = arr[rndInd];
arr[rndInd] = tmp;
}
}
private static int[] getRandomNumbers(int range, int size)
{
int numbers[] = new int[size];
for (int i = 0; i < size; i++)
{
numbers[i] = rng.nextInt(2 * range) - range;
}
return numbers;
}
}
private static int problem1(int[] arr, int k) {
// Implement me!
Map<Integer, Integer> table = new TreeMap<Integer, Integer>();
for (int i = 0; i < arr.length; i++) {
if (table.containsKey(arr[i])) {
table.put(arr[i], table.get(arr[i]) + 1);
} else {
table.put(arr[i], 1);
}
}
for (Map.Entry<Integer,Integer> entry : table.entrySet()) {
//As treemap is sorted, we return the first key with value >=k.
if(entry.getValue()>=k)
return entry.getKey();
}
//Not found
return -1;
}
As others have pointed out, there are a few mistakes. First, the line where you initialize ans,
int ans = 0;
You should initialize ans to Integer.MAX_VALUE so that when you find an integer that appears at least k times for the first time that ans gets set to that integer appropriately. Second, in your for loop, there's no reason to skip the first element while iterating the array so i should be initialized to 0 instead of 1. Also, in that same line, you want to iterate through the entire array, and in your loop's condition right now you have i < k when k is not the length of the array. The length of the array is denoted by arr.length so the condition should instead be i < arr.length. Third, in this line,
if (k < table.get(arr[i])){
where you are trying to check if an integer has occurred at least k times in the array so far while iterating through the array, the < operator should be changed to <= since the keyword here is at least k times, not "more than k times". Fourth, k should never change so you can get rid of this line of code,
k = table.get(arr[i]);
After applying all of those changes, your function should look like this:
private static int problem1(int[] arr, int k)
{
// Implement me!
HashMap<Integer, Integer> table = new HashMap<Integer, Integer>();
int ans = Integer.MAX_VALUE;
for (int i=0; i < arr.length; i++) {
if(table.containsKey(arr[i])) {
table.put(arr[i], table.get(arr[i]) + 1);
if (k <= table.get(arr[i])) {
ans = Math.min(ans, arr[i]);
}
}else{
table.put(arr[i], 1);
}
}
return ans;
}
Pseudo code:
collect frequencies of each number in a Map<Integer, Integer> (number and its count)
set least to a large value
iterate over entries
ignore entry if its value is less than k
if entry key is less than current least, store it as least
return least
One line implementation:
private static int problem1(int[] arr, int k) {
return Arrays.stream(arr).boxed()
.collect(groupingBy(identity(), counting()))
.entrySet().stream()
.filter(entry -> entry.getValue() >= k)
.map(Map.Entry::getKey)
.reduce(MAX_VALUE, Math::min);
}
This was able to pass all the cases! Thank you to everyone who helped!!
private static int problem1(int[] arr, int k)
{
// Implement me!
HashMap<Integer, Integer> table = new HashMap<Integer, Integer>();
int ans = Integer.MAX_VALUE;
for (int i=0; i < arr.length; i++) {
if(table.containsKey(arr[i])) {
table.put(arr[i], table.get(arr[i]) + 1);
}else{
table.put(arr[i], 1);
}
}
Set<Integer> keys = table.keySet();
for(int i : keys){
if(table.get(i) >= k){
ans = Math.min(ans,i);
}
}
if(ans != Integer.MAX_VALUE){
return ans;
}else{
return 0;
}
}
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");
}
}
I am trying to write the Count inversion algorithm. It works on an array of size 10. However, things went terribly wrong, when I tried to test on an array of size 100000. I was still able to sort the array but the number of inversions is NEGATIVE, which is wrong. I don't understand which part of my logic went wrong.
My main logic: I created an array object called myArray and it has a CountSplitInv instance method that is suppose to sort the sub-arrays and return number of inversions involved.
Please help me out. I have been stuck in this for a long time now and I still wasn't able to figure out what went wrong.
I have a feeling that I don't understand Recursion concept fully.
import java.io.*;
import java.util.*;
import java.math.*;
class myArray{
private int[] input_array;
private int nElems;
public myArray(int max){
input_array = new int[max];
nElems = 0;
}
public void insert(int value){
input_array[nElems++] = value;
}
public void display(){
for(int j = 0; j < nElems; j++){
System.out.print(input_array[j] + " ");
}
System.out.println("");
}
public void SortAndCount(){
int[] output_array = new int[nElems];
int ans = SortNInversionCounts(output_array,0, nElems -1);
System.out.println("number of inversions IS: " + ans);
}
public int SortNInversionCounts(int[] output_array, int lowerBound, int upperBound){
if(lowerBound == upperBound){
return 0;
} else{
int mid = (lowerBound + upperBound)/2;
int x, y , z;
x = SortNInversionCounts(output_array, lowerBound, mid);
y = SortNInversionCounts(output_array, mid+1, upperBound);
z = CountSplitInv(output_array, lowerBound, mid+1, upperBound);
return x + y + z;
}
}
public int CountSplitInv(int[] output_array, int lowPtr, int highPtr, int upperBound){
int j = 0;
int lowerBound = lowPtr;
int mid = highPtr - 1;
int n = upperBound - lowerBound + 1;
int numOfInversions = 0;
while(lowPtr <= mid && highPtr <= upperBound){
if( input_array[lowPtr] < input_array[highPtr]){
output_array[j++] = input_array[lowPtr++];
} else{
output_array[j++] = input_array[highPtr++];
// WHERE I count number of inversions
numOfInversions = numOfInversions + (mid - lowPtr + 1);
}
}
while(lowPtr <= mid){
output_array[j++] = input_array[lowPtr++];
}
while(highPtr <= upperBound){
output_array[j++] = input_array[highPtr++];
}
for(j = 0; j < n; j++){
input_array[lowerBound+j] = output_array[j];
}
return numOfInversions;
}
}
class NumOfInversionsApp{
public static void main(String[] args) throws IOException{
// Read input file
FileInputStream fil = new FileInputStream("IntegerArray.txt");
BufferedReader br = new BufferedReader( new InputStreamReader(fil));
myArray in_array = new myArray(100000);
String element = null;
while( ( element = br.readLine()) != null){
in_array.insert( Integer.parseInt(element) );
}
// input_array.display();
in_array.SortAndCount();
// input_array.display();
}
}
without fail, array size 5000, 4998 comparisons. [32] = 30 comparisons, etc.
(edit for different condition:)
same for odd sized arrays: [35] makes count 33.
I could go on and on.
Why is this? Var is qcount.
I don't believe there are any comparisons in the partition though perhaps I am wrong on that. My classmate is getting numbers about 80 times as large at times, so there is a huge difference in our counts.
My code works though. So that's good. Just the counts are vastly different.
Thanks for reading. SO made me write more when I really don't need to.
public class QuickSortTest
{
static int numcalls = 0;
static int qcount = 0;
public static void main(String[] args)
{
Random gen = new Random();
int[] array = new int[5000];
int i;
for (i = 0; i < array.length; i++)
{
array[i] = gen.nextInt(5000) + 1;
}
System.out.println("Initial array:");
for (i = 0; i < array.length; i++)
{
System.out.println(array[i] + " ");
}
System.out.println();
quicksort(array, 0, array.length - 1);
System.out.println("Sorted array:");
for (i = 0; i < array.length; i++)
{
System.out.println(array[i] + " ");
}
System.out.println("Done!");
System.out.println("qcount " + qcount);
}
public static int partition(int stuff[], int a, int z)
{
int left = a, right = z;
int saveStuff;
int pivot = stuff[(a + z) / 2];
while (left <= right)
{
while (stuff[left] < pivot)
{
left++;
}
while (stuff[right] > pivot)
{
right--;
}
if (left <= right)
{
saveStuff = stuff[left];
stuff[left] = stuff[right];
stuff[right] = saveStuff;
left++;
right--;
}
};
return left;
}
public static void quicksort(int stuff[], int a, int z)
{
int index = partition(stuff, a, z);
if (a < index - 1)
{
quicksort(stuff, a, index - 1);
qcount++;
}
if (index < z)
{
quicksort(stuff, index, z);
qcount++;
}
}
}