Recently I was testing two variations of the merge method in Mergesort and one turns our to be slightly faster than the other. For a large enough input (say, an array of 10-100 million or more randomly ordered elements), one merge method takes around 100ms longer than the other.
Here's the one taking more time:
private static void merge(int[] a, int low, int mid, int hi) {
int temp[] = new int[(hi - low) + 1];
int cLeft = low;
int cRight = mid + 1;
int cTemp = 0;
while (cLeft <= mid && cRight <= hi) {
if (a[cLeft] <= a[cRight]) {
temp[cTemp++] = a[cLeft++];
} else {
temp[cTemp++] = a[cRight++];
}
}
//copy the remaining left elements to the right end
System.arraycopy(a, cLeft, a, low + cTemp, mid - cLeft + 1);
//copy temp to a
System.arraycopy(temp, 0, a, low, cTemp);
}
...and this is the faster one
private static void merge(int[] list, int lowIndex, int midIndex, int highIndex) {
int[] L = new int[midIndex - lowIndex + 2];
for (int i = lowIndex; i <= midIndex; i++) {
L[i - lowIndex] = list[i];
}
L[midIndex - lowIndex + 1] = Integer.MAX_VALUE;
int[] R = new int[highIndex - midIndex + 1];
for (int i = midIndex + 1; i <= highIndex; i++) {
R[i - midIndex - 1] = list[i];
}
R[highIndex - midIndex] = Integer.MAX_VALUE;
int i = 0, j = 0;
for (int k = lowIndex; k <= highIndex; k++) {
if (L[i] <= R[j]) {
list[k] = L[i];
i++;
} else {
list[k] = R[j];
j++;
}
}
}
Both variations of MergeSort are given different arrays of same length with same elements at identical positions as their input. In other words, input of one algorithm is a copy of input of the other.
Although the difference in running time is negligible (the average running time doesn't change, i.e. remains 100ms, no matter how much we increase the size after 1 million mark.), I am eager to know what makes the faster merge faster. For me, the former method is cleaner and easier to implement. However, if the other one remains faster, I probably will switch to that.
Related
I tried to solve this problem but I,m getting the Wrong Answer
Inversion Count: For an array, inversion count indicates how far (or close) the array is from being sorted. If the array is already sorted then the inversion count is 0. If an array is sorted in the reverse order then the inversion count is the maximum.
Formally, two elements a[i] and a[j] form an inversion if a[i] > a[j] and i < j.
enter link description here
class Solution
{
// arr[]: Input Array
// N : Size of the Array arr[]
//Function to count inversions in the array.
static long count = 0;
static long inversionCount(long arr[], long n)
{
// Your Code Here
long merged[] = mergeSort(arr, 0, n - 1);
return count;
}
static long[] merge(long left[], long right[])
{
long res[] = new long[left.length + right.length];
long i = 0, j = 0, k = 0;
while (i < left.length && j < right.length)
{
if (left[(int)i] <= right[(int)j])
{
res[(int)k] = left[(int)i];
k++;
i++;
}
else
{
count = count + (left.length - i);
res[(int)k] = right[(int)j];
k++;
j++;
}
}
while (i < left.length)
{
res[(int)k] = left[(int)i];
i++;
k++;
}
while (j < right.length)
{
res[(int)k] = right[(int)j];
k++;
j++;
}
while (i < left.length)
{
res[(int)k] = left[(int)i];
i++;
k++;
}
return res;
}
static long[] mergeSort(long a[], long lo, long hi)
{
if (hi == lo)
{
long temp[] = {a[(int)hi], };
return temp;
}
long mid = (hi + lo) / 2;
long left[] = mergeSort(a, lo, mid);
long right[] = mergeSort(a, mid + 1, hi);
long merged[] = merge(left, right);
return merged;
}
}
As gfg uses many test cases to check your code , the answer that you get on the first test cases gets hold to the static variable(count) and will never be reset.
So only the first test case will be correct and the rest of the test cases gets the wrong answer.
So try declaring the count variable outside the method and initiate it within the method.Whenever a new test case is executed your count variable will be reset to zero.
static long count;
static long inversionCount(long arr[], long n)
{
// Your Code Here
count = 0;
long merged[] = mergeSort(arr, 0, n - 1);
return count;
}
This an algorithm to count inversions in an array, a divide and conquer algorithm. I want to know what is the basic operation of the this algorithm and how do I set up a recurrence relation to analyze the number of executions of the basic step?
The definition of basic step: The operation contributing the most to the total running time of an algorithm. It is typically the most time consuming operation in the algorithm’s innermost loop.
Examples: Key comparison operation; arithmetic operation (division being the most time-consuming, followed by multiplication).
This is the algorithm
public static int merge(int[] arr, int[] aux, int low, int mid, int high)
{
int k = low, i = low, j = mid + 1;
int inversionCount = 0;
// while there are elements in the left and right runs
while (i <= mid && j <= high)
{
if (arr[i] <= arr[j]) {
aux[k++] = arr[i++];
}
else {
aux[k++] = arr[j++];
inversionCount += (mid - i + 1); // NOTE
}
}
// copy remaining elements
while (i <= mid) {
aux[k++] = arr[i++];
}
// no need to copy the second half
// copy back to the original array to reflect sorted order
for (i = low; i <= high; i++) {
arr[i] = aux[i];
}
return inversionCount;
}
// Sort array `arr[low…high]` using auxiliary array `aux`
public static int mergeSort(int[] arr, int[] aux, int low, int high)
{
// Base case
if (high == low) { // if run size == 1
return 0;
}
// find midpoint
int mid = (low + ((high - low) >> 1));
int inversionCount = 0;
// recursively split runs into two halves until run size == 1,
// then merges them and return up the call chain
// split/merge left half
inversionCount += mergeSort(arr, aux, low, mid);
// split/merge right half
inversionCount += mergeSort(arr, aux, mid + 1, high);
// merge the two half runs
inversionCount += merge(arr, aux, low, mid, high);
return inversionCount;
}
How do I set up a recurrence relation to analyze number of executions of the basic steps? Please also provide explanation.
I have an implementation of the mergesort algorithm. How do I calculate the height of the tree?
So far I can get the number of recursive calls, but not the height of the tree:
static int swaps=0;
static long comparisons=0;
static int recursionsdepth=0;
public static int[] sort(int[] array) {
recursionsdepth++;
if (array.length > 1) {
int middle = (int)(array.length / 2);
int[] left = new int[middle];
for (int i = 0; i <= left.length - 1; i++) {
left[i] = array[i];
}
int[] right = new int[array.length - middle];
for (int i = middle; i <= array.length - 1; i++) {
right[i - middle] = array[i];
}
left = sort(left);
right = sort(right);
return merge(left, right);
}
else
{
recursionsdepth--;
return array;
}
}
For {1,5,7,9} the recursive calls are 3 ( 1 for {1,5,7,9} ,1 for {1,5} and 1 for {7,9}), but the height of the tree is 2.
Merge Sort repeatedly divides the array into two equal (almost) parts as long as the array size is greater than 1. It doesn't care about the initial state of the array, i.e. it would do so even if the array is already sorted.
Now, there is only one way to do so for any given array of length n. And therefore, the height of the merge-sort tree will be constant with respect to n. That is the height will be ceil(log n) where base is 2. You don't need to actually run your program to find this out.
Since the OP is hell-bent on calculating the height while actually running the sorting code, here it is:
Pass an additional variable to the sort function that would store the depth of the current node. And use a global variable to store the maximum depth that has been achieved until now. Below code is slight modification of the one posted in the question:
static int swaps=0;
static long comparisons=0;
static int recursionsdepth=0;
public static int[] sort(int[] array, int depth) { // at first call depth = 0
recursiondepth = Math.max(recursiondepth, depth);
if (array.length > 1) {
int middle = (int)(array.length / 2);
int[] left = new int[middle];
for (int i = 0; i <= left.length - 1; i++) {
left[i] = array[i];
}
int[] right = new int[array.length - middle];
for (int i = middle; i <= array.length - 1; i++) {
right[i - middle] = array[i];
}
left = sort(left, depth+1);
right = sort(right, depth+1);
return merge(left, right);
}
else
{
return array;
}
}
I have the below merge sort code in my application. I am very confused on how the recursive method gets called again after it comes out of the if block when the if condition is not met.
I debugged my code, but I am still not getting it. The sort method that calls mergesort(0, number - 1) starts first at mergesort(0, 5). low is less than high, middle is 2, so mergesort(0, 2) is run next. This goes on until we have mergesort(0, 0) in which case low is not less than high,
so it comes out of the if block. But when I debug, the method returns, and it starts again after mergesort(0, 0) case. How does the call happen again?
public class MergeSort {
private int[] numbers;
private int[] helper;
private int number;
public int[] sort(int[] values) {
this.numbers = values;
number = values.length;
this.helper = new int[number];
return mergesort(0, number - 1);
}
private int[] 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);
}
return numbers;
}
private int[] merge(int low, int middle, int high) {
// Copy both parts into the helper array
for (int i = low; i <= high; i++) {
helper[i] = numbers[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[i] <= helper[j]) {
numbers[k] = helper[i];
i++;
} else {
numbers[k] = helper[j];
j++;
}
k++;
}
// Copy the rest of the left side of the array into the target array
while (i <= middle) {
numbers[k] = helper[i];
k++;
i++;
}
return numbers;
}
}
This is because the mergesort method calls itself twice. You can print out the stack to see what happens.
For example, when call mergesort(0,1), the method will call mergesort(0,0) first and then mergesort(1,1).
I'm trying to implement multi-threading using merge sort. I have it making new threads at the point where it cuts an array in half.
The array is sorted depending on the:
[size of the array] vs [how many times I create new threads]
For instance: the array will be sorted if I let it create merely two threads on an array of size 70, but if I let it create 6, it will come back unsorted. One thing I thought it might be is that the threads weren't sync'd, but I used threadName.join()
here is some code: merge.java
import java.util.Random;
public class merge implements Runnable {
int[] list;
int length;
int countdown;
public merge(int size, int[] newList, int numberOfThreadReps, int firstMerge) {
length = size;
countdown = numberOfThreadReps;
list = newList;
if (firstMerge == 1)
threadMerge(0, length - 1);
}
public void run() {
threadMerge(0, length - 1);
}
public void printList(int[] list, int size) {
for (int i = 0; i < size; i++) {
System.out.println(list[i]);
}
}
public void regMerge(int low, int high) {
if (low < high) {
int middle = (low + high) / 2;
regMerge(low, middle);
regMerge(middle + 1, high);
mergeJoin(low, middle, high);
}
}
public void mergeJoin(int low, int middle, int high) {
int[] helper = new int[length];
for (int i = low; i <= high; i++) {
helper[i] = list[i];
}
int i = low;
int j = middle + 1;
int k = low;
while (i <= middle && j <= high) {
if (helper[i] <= helper[j]) {
list[k] = helper[i];
i++;
} else {
list[k] = helper[j];
j++;
}
k++;
}
while (i <= middle) {
list[k] = helper[i];
k++;
i++;
}
helper = null;
}
public void threadMerge(int low, int high) {
if (countdown > 0) {
if (low < high) {
countdown--;
int middle = (low + high) / 2;
int[] first = new int[length / 2];
int[] last = new int[length / 2 + ((length % 2 == 1) ? 1 : 0)];
for (int i = 0; i < length / 2; i++)
first[i] = list[i];
for (int i = 0; i < length / 2 + ((length % 2 == 1) ? 1 : 0); i++)
last[i] = list[i + length / 2];
merge thread1 = new merge(length / 2, first, countdown, 0);// 0
// is
// so
// that
// it
// doesn't
// call
// threadMerge
// twice
merge thread2 = new merge(length / 2
+ ((length % 2 == 1) ? 1 : 0), last, countdown, 0);
Thread merge1 = new Thread(thread1);
Thread merge2 = new Thread(thread2);
merge1.start();
merge2.start();
try {
merge1.join();
merge2.join();
} catch (InterruptedException ex) {
System.out.println("ERROR");
}
for (int i = 0; i < length / 2; i++)
list[i] = thread1.list[i];
for (int i = 0; i < length / 2 + ((length % 2 == 1) ? 1 : 0); i++)
list[i + length / 2] = thread2.list[i];
mergeJoin(low, middle, high);
} else {
System.out.println("elsd)");
}
} else {
regMerge(low, high);
}
}
}
proj4.java
import java.util.Random;
public class proj4 {
public static void main(String[] args) {
int size = 70000;
int threadRepeat = 6;
int[] list = new int[size];
list = fillList(list, size);
list = perm(list, size);
merge mergy = new merge(size, list, threadRepeat, 1);
// mergy.printList(mergy.list,mergy.length);
for (int i = 0; i < mergy.length; i++) {
if (mergy.list[i] != i) {
System.out.println("error)");
}
}
}
public static int[] fillList(int[] list, int size) {
for (int i = 0; i < size; i++)
list[i] = i;
return list;
}
public static int[] perm(int[] list, int size) {
Random generator = new Random();
int rand = generator.nextInt(size);
int temp;
for (int i = 0; i < size; i++) {
rand = generator.nextInt(size);
temp = list[i];
list[i] = list[rand];
list[rand] = temp;
}
return list;
}
}
so TL;DR my array isn't getting sorted by a multithreaded merge sort based on the size of the array and the number of times I split the array by using threads...why is that?
Wow. This was an interesting exercise in masochism. I'm sure you've moved on but I thought for posterity...
The bug in the code is in mergeJoin with the middle argument. This is fine for regMerge but in threadMerge the middle passed in is (low + high) / 2 instead of (length / 2) - 1. Since in threadMerge low is always 0 and high is length - 1 and the first array has (length / 2) size. This means that for lists with an odd number of entries, it will often fail depending on randomization.
There are also a number of style issues which makes this program significantly more complicated and error prone:
The code passes around a size of the arrays when Java has a convenient list.length call which would be more straightforward and safer.
The code duplicates calculations (see length/2) in a number of places.
The code should be able to sort inside the array without creating sub-arrays.
Classes should start with an uppercase letter (Merge instead of merge)
firstMerge should be a boolean
The code names the Thread variable merge1 and the merge variable thread1. Gulp.
The merge constructor calling threadMerge(0,length -1) is strange. I would just put that call after the new call back in proj4. Then firstMerge can be removed.
I would consider switching to having high be one past the maximum value instead of the maximum. We tend to think like for (int i = 0; i < 10; i++) more than i <= 9. Then the code can have j go from low to < middle and k from middle to < high. Better symmetry.
Best of luck.