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);
}
Related
I'm confused on why tim sort which makes my suffix array implement O(nlognlogn) faster than LSD radix sort which should make the implementation O(nlogn). Perhaps is it that radix sort simply works better with larger string lengths?
here is my radix sort implementation in java and I don't know if I'm just not making it as efficient as it could be:
class Suffix {
int index;
int[] rank = new int[2];
public Suffix(int i, int r1, int r2){
index = i;
rank[0] = r1;
rank[1] = r2;
}
}
public class Main {
public static int getMax(Suffix[] suffixArray, int index){
int max = Integer.MIN_VALUE;
for(Suffix i : suffixArray){
if(i.rank[index] > max)max = i.rank[index];
}
return max;
}
public static void countingSort(Suffix[] suffixes, int exp, int index){
int[] count = new int[10];
Suffix[] sorted = new Suffix[suffixes.length];
for(Suffix i : suffixes)count[(i.rank[index]/exp) % 10]++;
for(int i = 1; i < 10; i++)count[i] += count[i - 1];
for(int i = suffixes.length - 1; i >= 0; i--){
Suffix current = suffixes[i];
int position = count[(current.rank[index]/exp) % 10] - 1;
sorted[position] = current;
count[(current.rank[index]/exp) % 10]--;
}
for(int i = 0; i < suffixes.length; i++)suffixes[i] = sorted[i];
}
public static void radixSort(Suffix[] suffixes, int index){
int max = getMax(suffixes, index);
for(int exp = 1; max/exp > 0; exp *= 10)countingSort(suffixes, exp, index);
}
public static void sort(Suffix[] suffixes){
int positiveCounter = 0;
for(Suffix i : suffixes){
if(i.rank[1] >= 0)positiveCounter++;
}
Suffix[] positives = new Suffix[positiveCounter];
int positivesIndex = 0;
int tempIndex = 0;
for(Suffix i : suffixes){
if( i.rank[1] >= 0){
positives[positivesIndex] = i;
positivesIndex++;
}
else {
suffixes[tempIndex] = i;
tempIndex++;
}
}
radixSort(positives, 1);
for(int i = 0; i < positives.length; i++){
suffixes[tempIndex] = positives[i];
tempIndex++;
}
radixSort(suffixes, 0);
}
public static void mergeSort(int[] data) {
int[] left = firstHalf(data);
int[] right = secondHalf(data);
if (data.length > 1) {
mergeSort(left);
mergeSort(right);
merge(data, left, right);
}
}
public static void merge(int[] data, int[] left, int[] right) {
int tempArraySize = data.length;
int mergedNumbers[] = new int[tempArraySize]; //Temp array to take the sorted array
int mergePos;
int leftPos;
int rightPos;
int middle = data.length / 2;
mergePos = 0;
leftPos = 0; // 0 index
rightPos = middle + 1; //j is middle index
while (leftPos <= middle && rightPos <= data.length - 1) {
if (left[leftPos] < right[rightPos]) {
mergedNumbers[mergePos] = left[leftPos];
leftPos++;
} else {
mergedNumbers[mergePos] = right[rightPos];
rightPos++;
}
mergePos++;
}
// when the right half array finishes sorting
while (leftPos <= middle) {
mergedNumbers[mergePos] = left[leftPos];
leftPos++;
mergePos++;
}
// when the left half array finishes sorting
while (rightPos <= data.length - 1) {
mergedNumbers[mergePos] = right[rightPos];
rightPos++;
mergePos++;
}
// give value to the original array
for (mergePos = 0; mergePos < tempArraySize; ++mergePos) {
data[leftPos + mergePos] = mergedNumbers[mergePos];
}
}
public static int[] firstHalf(int[] data) {
int[] tempFirst = new int[(data.length / 2) + 1];
for (int i = 0; i <= data.length / 2; i++) {
tempFirst[i] = data[i];
}
return tempFirst;
}
public static int[] secondHalf(int[] data) {
int[] tempSecond = new int[(data.length / 2) + 1];
for (int i = (data.length / 2) + 1; i < data.length; i++) { // Middle to the end
for (int j = 0; j <= data.length / 2; j++) {
tempSecond[j] = data[i];
}
}
return tempSecond;
}
This is what I made.
My mergeSort method makes an error java.lang.StackOverflowError
What mistakes I made?
I made the firstHalf and secondHalf methods to get the index 0 ~ middle and middle+1 ~ end.
Those methods are made to get the value from the original 'data' Array.
The merge method is as same as the common MergeSort code.
Do I have to build a base case in the mergeSort method?
With this approach, it is simpler to return merged arrays. It would be faster to do a one time allocation of a temporary array, and use indexing to merge data between the two arrays rather than creating temporary arrays and copy data. Fixes noted in comments.
public static int[] mergeSort(int[] data) { // fix
int[] left = firstHalf(data);
int[] right = secondHalf(data);
if(data.length < 2) // change
return data; // fix
left = mergeSort(left); // fix
right = mergeSort(right); // fix
return merge(left, right); // fix
}
public static int[] merge(int[] left, int[] right) { // fix
int mergedNumbers [] = new int[left.length+right.length]; // fix
int mergePos = 0; // fix
int leftPos = 0; // fix
int rightPos = 0; // fix
while (leftPos < left.length && rightPos < right.length) { // fix
if (left[leftPos] < right[rightPos]) {
mergedNumbers[mergePos] = left[leftPos];
leftPos++;
} else {
mergedNumbers[mergePos] = right[rightPos];
rightPos++;
}
mergePos++;
}
while (leftPos < left.length) { // fix
mergedNumbers[mergePos] = left[leftPos];
leftPos++;
mergePos++;
}
while (rightPos < right.length) { // fix
mergedNumbers[mergePos] = right[rightPos];
rightPos++;
mergePos++;
}
return mergedNumbers; // fix
}
public static int[] firstHalf(int[] data) {
int j = (data.length/2); // fix
int[] tempFirst = new int[j]; // fix
for(int i = 0; i < tempFirst.length; i++) // fix
tempFirst[i] = data[i];
return tempFirst;
}
public static int[] secondHalf(int[] data) {
int j = (data.length/2); // fix
int[] tempSecond = new int[data.length-j]; // fix
for(int i = 0; i < tempSecond.length; i++) // fix
tempSecond[i] = data[i+j]; // fix
return tempSecond;
}
The wiki article has a somewhat optimized approach for top down merge sort:
https://en.wikipedia.org/wiki/Merge_sort#Top-down_implementation
The problems in your code come from the confusing convention to specify ranges with included boundaries. You should instead consider the upper bound to be excluded: this would avoid the numerous +1/-1 adjustments required for the included convention, some of which are inconsistent in your code:
leftHalf creates an array with length (data.length / 2) + 1, including the element at offset mid = data.length / 2. This is what the merge method expects, but is not optimal as it would make unbalanced halves for even sized arrays, and more problematically would return a 2 element slice for a 2 element array, which causes an infinite recursion and explains the Stack Overflow exception you get.
rightHalf also creates an array with length (data.length / 2) + 1, which is incorrect, the length should be array with lengthdata.length - ((data.length / 2) + 1)`, which would be an empty array for a 2 element array.
Furthermore, rightHalf uses two nested for loops to copy the values from the argument array, which is incorrect.
your index range into the right array is incorrect in the merge method.
the index into data is incorrect in data[leftPos + mergePos] = mergedNumbers[mergePos]; it should be just:
data[mergePos] = mergedNumbers[mergePos];
Here is a modified version, with a less error prone convention:
// sort elements in data in place
public static void mergeSort(int[] data) {
if (data.length > 1) {
int[] left = firstHalf(data);
int[] right = secondHalf(data);
mergeSort(left);
mergeSort(right);
merge(data, left, right);
}
}
public static void merge(int[] data, int[] left, int[] right) {
int leftLength = left.length;
int rightLength = right.length;
int length = leftLength + rightLength;
int mergedNumbers[] = new int[length]; //Temp array to received the merged array
int leftPos = 0;
int rightPos = 0;
int mergePos = 0;
while (leftPos < leftLength && rightPos < rightLength) {
if (left[leftPos] <= right[rightPos]) {
mergedNumbers[mergePos] = left[leftPos];
leftPos++;
mergePos++;
} else {
mergedNumbers[mergePos] = right[rightPos];
rightPos++;
mergePos++;
}
}
// copy the remaining entries in the left half
while (leftPos < leftLength) {
mergedNumbers[mergePos] = left[leftPos];
leftPos++;
mergePos++;
}
// copy the remaining entries in the right half
while (rightPos < rightLength) {
mergedNumbers[mergePos] = right[rightPos];
rightPos++;
mergePos++;
}
// copy the values back to the original array
for (mergePos = 0; mergePos < length; mergePos++) {
data[mergePos] = mergedNumbers[mergePos];
}
}
public static int[] firstHalf(int[] data) {
int leftLength = data.length / 2;
int[] tempFirst = new int[leftLength];
for (int i = 0; i < leftLength; i++) {
tempFirst[i] = data[i];
}
return tempFirst;
}
public static int[] secondHalf(int[] data) {
int leftLength = data.length / 2;
int rightLength = data.length - leftLength;
int[] tempSecond = new int[rightLength];
for (int i = 0; i < rightLength; i++) {
tempSecond[i] = data[LeftLength + i];
}
return tempSecond;
}
I have a simple rotation function which takes an array and a number to rotate the numbers left
e.g. [1,2,3,4,5] & 2 - output: [3,4,5,1,2].
I want to know the most efficient way of completing this function, whether it would be to convert the int array into a string a splice it or whether to copy the array or to convert to an List<Integer>.
If anyone wants additional information please ask!
my solution at the moment:
static int[] rotLeft(int[] a, int d) {
int lengthOfArray = a.length;
int[] temp = new int[lengthOfArray];
for(int i = 0; i < lengthOfArray; i++){
int newLocation = (i + (lengthOfArray - d)) % lengthOfArray;
temp[newLocation] = a[i];
}
return temp;
}
Simple way to do it with O(n) complexity is as below along with handling of valid shifts int[] arr: is an int array, n=length of an array, d=how many shifts required.
public int[] leftRotate(int[] arr, int n, int d) {
int rot = 0;
int[] marr = new int[n];
if (d < 0 || d == 0 || d>n) {
return arr;
}
else {
for (int i = 0; i < n; i++) {
if (i < n - d) {
marr[i] = arr[i + d];
} else {
marr[i] = arr[rot];
rot++;
}
}
return marr;
}
}
public void GetArray(int[] arr, int n, int d) {
int[] arr1 = leftRotate(arr, n, d);
for (int j : arr1) {
System.out.println(j);
}
}
public static void main(String args[]) {
int[] arr = { 1,2,3,4,5 };
int n = arr.length;
Test2 obj = new Test2();
obj.GetArray(arr, n, 2);
}
Why don't you try this one
void Rotate(int arr[], int d, int n)
{
for (int i = 0; i < d; i++)
leftRotatebyOne(arr, n);
}
void leftRotatebyOne(int arr[], int n)
{
int i, temp;
temp = arr[0];
for (i = 0; i < n - 1; i++)
arr[i] = arr[i + 1];
arr[i] = temp;
}
and to call this invoke method like below
int arr[] = { 1, 2, 3, 4, 5 };
Rotate(arr, 2, 5);
I have a program that generates an array based on the user input (the array can be descending, ascending and two types of random) and then it calculates the maximum subarray sum using bruteforce, divide and conquer, and dynamic programming. It seems to work fine and dandy up to values of 65535. After that, each sum is different, which shouldn't happen. 35535 is 2 to the power of 16 minus 1, so I was wondering if I'm hitting some limit. When I print the array it seems to print fine, so I don't think that the problem is that the array isn't generating properly.
This is the main class:
public class MainClass {
public static void main(String[] args) {
int n = Integer.parseInt(args[1]);
int[] maxsubarray1;
maxsubarray1 = new Generator(n,args[3]).getArray();
int[] maxsubarray2 = Arrays.copyOf(maxsubarray1,maxsubarray1.length);
int[] maxsubarray3 = Arrays.copyOf(maxsubarray1,maxsubarray1.length);
System.out.println(Arrays.toString(maxsubarray1));
solver solver = new solver();
int solution;
//if (args[5].equalsIgnoreCase("bruteforce")){
long startTime = System.currentTimeMillis();
solution = solver.bruteforce(maxsubarray1, n);
System.out.println("__________BRUTE FORCE________\nThe sum of the array is "+solution);
long endTime = System.currentTimeMillis() - startTime;
System.out.println(endTime);
//}
//if (args[5].equalsIgnoreCase("divideconquer")){
long startTime2 = System.currentTimeMillis();
int solutiondivideconquer = solver.divideconquer(maxsubarray2, 0, n);
System.out.println("__________DIVIDE AND CONQUERE________\nThe sum of the array is "+ solutiondivideconquer);
long endTime2 = System.currentTimeMillis() - startTime2;
System.out.println(endTime2);
//}
//if (args[5].equalsIgnoreCase("dynprog")){
long startTime3 = System.currentTimeMillis();
int solutiondynprog = solver.dynprog(maxsubarray3, n);
System.out.println("__________DYNAMIC PROGRAMMING________\nThe sum of the array is "+ solutiondynprog);
long endTime3 = System.currentTimeMillis() - startTime3;
System.out.println(endTime3);
//}
}
}
This is the generator code:
import java.util.concurrent.ThreadLocalRandom;
public class Generator {
int size;
String type;
int[] generatedArray;
public Generator(int mysize, String mytype){
size = mysize;
type = mytype;
generatedArray = new int[size];
}
public void ascending(){
for(int i = 0; i < this.size; i++)
generatedArray[i] = i+1;
}
public void descending(){
for(int i = this.size -1; i >= 0; i--)
generatedArray[i] = i+1;
}
public void random(){
for(int i = 0; i <= this.size -1; i++)
generatedArray[i] = ThreadLocalRandom.current().nextInt(-10*this.size, 10*this.size);
}
public void randominter(){
for(int i = 0; i <= this.size -1; i++)
if (i % 2 == 0)
generatedArray[i] = ThreadLocalRandom.current().nextInt(1, 10*this.size);
else if (i % 2 == 1)
generatedArray[i] = ThreadLocalRandom.current().nextInt(-10*this.size, -1);
}
public int[] getArray(){
if (type.equalsIgnoreCase("descending")){
this.descending();
return generatedArray;
}
if (type.equalsIgnoreCase("ascending")){
this.ascending();
return generatedArray;
}
if (type.equalsIgnoreCase("random")){
this.random();
return generatedArray;
}
if (type.equalsIgnoreCase("randominter")){
this.randominter();
return generatedArray;
}
return null;
}
}
And this is the solver class:
public class solver {
//brute force algorithm with complexity O(n^2)
int bruteforce(int array[], int n){
int max = Integer.MIN_VALUE;
//We go throght all the elements of the list and we try all the
//posible combinations with all the other elements
for (int i = 0; i < n; i++){
int sum = 0;
for (int j = i; j < n ; j++){
//we add the an element in the sum
sum += array[j];
//we check if the sum with the new element is greater that the value we had before
if(sum > max){
//if it's greater, it becomes the new value
max = sum;
}
}
}
//we return the maximum value we have found
return max;
}
//to implement the divide and conquer algorithm we have to take into account the
// maximum subarray can have elements in the right subarray and in the left subarray
int maxCrossingSum(int array[], int l, int m, int h){
int sum = 0;
int left_sum = Integer.MIN_VALUE;
//Has the elements on the left part of the arrray
for ( int i = (int)m; i >= l; i--){
sum = sum + array[i];
if( sum > left_sum ){
left_sum = sum;
}
}
sum = 0;
int right_sum = 0;
//Has the elements in the right part of the array
for ( int j = (int)m+1; j <= h; j++){
sum = sum + array [j];
if (sum > right_sum){
right_sum = sum;
}
}
//returns the sun of the elements on the left and the right of the array
return left_sum + right_sum;
}
//returns the sum of the maximum subarray
int maxSubarraySum(int array[], int l, int h){
if(l == h)
return array[1];
int m = (l + h)/2;
//checks which is the maximum between left and right
int maxBetweenLeftRight = max(maxSubarraySum(array, l, m), maxSubarraySum(array, m+1,h));
int crossing = maxCrossingSum(array, l, m,h-1);
//retrns the maximum between one of the sides and the crossing sum
return max(maxBetweenLeftRight, crossing);
}
//divide and conquere algorithm with complexity O(nlogn)
//only made to make it more understandable from the main
//can call maxSubarraySum and it would be the same
int divideconquer (int array[], int l, int h){
return maxSubarraySum(array, l, h);
}
//dynamic programming algorithm with complexity O(n)
int dynprog(int array[], int n){
int a = array[0];
int b = array[0];
//for all the elements checks if the sum was better until the
//step before or adding the element
for (int i = 1 ; i < n; i++){
a= max (a+ array[i], array[i]);
b= max(b, a);
}
return b;
}
}
Changing all the ints to longs didn't help either.
I've copied your code, changed all ints to longs and it's working fine. Also changed n = Integer.parseInt(args[0]) instead of n = Integer.parseInt(args[1]).
Then I called my program like program_name 1000 random.
I've checked in excel, and only bruteforce was wrong. I've changed Integer.MIN_VALUE to Long.MIN_VALUE. And int sum to long sum.
Main.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class Main {
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
long[] maxsubarray1;
maxsubarray1 = new Generator(n, args[1]).getArray();
long[] maxsubarray2 = Arrays.copyOf(maxsubarray1, maxsubarray1.length);
long[] maxsubarray3 = Arrays.copyOf(maxsubarray1, maxsubarray1.length);
System.out.println(Arrays.toString(maxsubarray1));
solver solver = new solver();
long solution;
//if (args[5].equalsIgnoreCase("bruteforce")){
long startTime = System.currentTimeMillis();
solution = solver.bruteforce(maxsubarray1, n);
System.out.println("__________BRUTE FORCE________\nThe sum of the array is " + solution);
long endTime = System.currentTimeMillis() - startTime;
System.out.println(endTime);
//}
//if (args[5].equalsIgnoreCase("divideconquer")){
long startTime2 = System.currentTimeMillis();
long solutiondivideconquer = solver.divideconquer(maxsubarray2, 0, n);
System.out.println("__________DIVIDE AND CONQUERE________\nThe sum of the array is " + solutiondivideconquer);
long endTime2 = System.currentTimeMillis() - startTime2;
System.out.println(endTime2);
//}
//if (args[5].equalsIgnoreCase("dynprog")){
long startTime3 = System.currentTimeMillis();
long solutiondynprog = solver.dynprog(maxsubarray3, n);
System.out.println("__________DYNAMIC PROGRAMMING________\nThe sum of the array is " + solutiondynprog);
long endTime3 = System.currentTimeMillis() - startTime3;
System.out.println(endTime3);
//}
}
}
solver.java
public class solver {
//brute force algorithm with complexity O(n^2)
long bruteforce(long array[], int n){
long max = Long.MIN_VALUE;
//We go throght all the elements of the list and we try all the
//posible combinations with all the other elements
for (int i = 0; i < n; i++){
long sum = 0;
for (int j = i; j < n ; j++){
//we add the an element in the sum
sum += array[j];
//we check if the sum with the new element is greater that the value we had before
if(sum > max){
//if it's greater, it becomes the new value
max = sum;
}
}
}
//we return the maximum value we have found
return max;
}
//to implement the divide and conquer algorithm we have to take into account the
// maximum subarray can have elements in the right subarray and in the left subarray
long maxCrossingSum(long array[], long l, long m, long h){
long sum = 0;
long left_sum = Integer.MIN_VALUE;
//Has the elements on the left part of the arrray
for ( int i = (int)m; i >= l; i--){
sum = sum + array[i];
if( sum > left_sum ){
left_sum = sum;
}
}
sum = 0;
long right_sum = 0;
//Has the elements in the right part of the array
for ( int j = (int)m+1; j <= h; j++){
sum = sum + array [j];
if (sum > right_sum){
right_sum = sum;
}
}
//returns the sun of the elements on the left and the right of the array
return left_sum + right_sum;
}
//returns the sum of the maximum subarray
long maxSubarraySum(long array[], long l, long h){
if(l == h)
return array[1];
long m = (l + h)/2;
//checks which is the maximum between left and right
long maxBetweenLeftRight = max(maxSubarraySum(array, l, m), maxSubarraySum(array, m+1,h));
long crossing = maxCrossingSum(array, l, m,h-1);
//retrns the maximum between one of the sides and the crossing sum
return max(maxBetweenLeftRight, crossing);
}
//divide and conquere algorithm with complexity O(nlogn)
//only made to make it more understandable from the main
//can call maxSubarraySum and it would be the same
long divideconquer (long array[], int l, int h){
return maxSubarraySum(array, l, h);
}
//dynamic programming algorithm with complexity O(n)
long dynprog(long array[], int n){
long a = array[0];
long b = array[0];
//for all the elements checks if the sum was better until the
//step before or adding the element
for (int i = 1 ; i < n; i++){
a= max (a+ array[i], array[i]);
b= max(b, a);
}
return b;
}
private long max(long a, long b) {
if (a > b ) return a;
else return b;
}
}
Generator.java
import java.util.concurrent.ThreadLocalRandom;
public class Generator {
int size;
String type;
long[] generatedArray;
public Generator(int mysize, String mytype) {
size = mysize;
type = mytype;
generatedArray = new long[size];
}
public void ascending() {
for (int i = 0; i < this.size; i++)
generatedArray[i] = i + 1;
}
public void descending() {
for (int i = this.size - 1; i >= 0; i--)
generatedArray[i] = i + 1;
}
public void random() {
for (int i = 0; i <= this.size - 1; i++)
generatedArray[i] = ThreadLocalRandom.current().nextInt(-10 * this.size, 10 * this.size);
}
public void randominter() {
for (int i = 0; i <= this.size - 1; i++)
if (i % 2 == 0)
generatedArray[i] = ThreadLocalRandom.current().nextInt(1, 10 * this.size);
else if (i % 2 == 1)
generatedArray[i] = ThreadLocalRandom.current().nextInt(-10 * this.size, -1);
}
public long[] getArray() {
if (type.equalsIgnoreCase("descending")) {
this.descending();
return generatedArray;
}
if (type.equalsIgnoreCase("ascending")) {
this.ascending();
return generatedArray;
}
if (type.equalsIgnoreCase("random")) {
this.random();
return generatedArray;
}
if (type.equalsIgnoreCase("randominter")) {
this.randominter();
return generatedArray;
}
return null;
}
}
I was wrong, it CAN BE an overflow error if Array[i] = i + 1, since the sum is length * (min + max) / 2 > Integer.MAX_VALUE
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");
}
}