Finding Index bounds for a recursive subsequence - java

I have a quick question about a maximum subsequence algorithm that uses divide and conquer shown below:
private static int maxSum3Recursive(int[] a, int left, int right)
{
if (left == right)
{
if (a[left] > 0)
{
return a[left];
}
else {
return 0;
}
}
int center = (left + right) / 2;
int maxLeftSum = maxSum3Recursive(a, left, center);
int maxRightSum = maxSum3Recursive(a, center + 1, right);
int maxLeftBorderSum = 0;
int leftBorderSum = 0;
for (int i = center; i >= left; i--)
{
leftBorderSum += a[i];
if (leftBorderSum > maxLeftBorderSum)
{
maxLeftBorderSum = leftBorderSum;
}
}
int maxRightBorderSum = 0;
int rightBorderSum = 0;
for (int i = center + 1; i <= right; i++)
{
rightBorderSum += a[i];
if (rightBorderSum > maxRightBorderSum)
{
maxRightBorderSum = rightBorderSum;
}
}
return Math.max(maxLeftSum, Math.max(maxRightSum, maxLeftBorderSum + maxRightBorderSum));
}
The algorithm works as expected and gives me the maximum sum, however, I'm having trouble finding the start and end index for the bounds for the maximum. For example, let's say we have an array: {5, -5, 2, 9}. The sum would be 2 + 9 = 11 and index would be [2 - 3] since the sum has to be consecutive. In other words, we can't just use 5 and 9 to make 14 because they are not next to each other.
I would appreciate any help figuring out the indexes.

Related

How to implement the medians of medians algorithm in Java

I am trying to implement the median of medians algorithm in Java. The algorithm shall determine the median of a set of numbers. I tried to implement the pseudo code on wikipedia:
https://en.wikipedia.org/wiki/Median_of_medians
I am getting a buffer overflow and don't know why. Due to the recursions it's quite difficult to keep track of the code for me.
import java.util.Arrays;
public class MedianSelector {
private static final int CHUNK = 5;
public static void main(String[] args) {
int[] test = {9,8,7,6,5,4,3,2,1,0,13,11,10};
lowerMedian(test);
System.out.print(Arrays.toString(test));
}
/**
* Computes and retrieves the lower median of the given array of
* numbers using the Median algorithm presented in the lecture.
*
* #param input numbers.
* #return the lower median.
* #throw IllegalArgumentException if the array is {#code null} or empty.
*/
public static int lowerMedian(int[] numbers) {
if(numbers == null || numbers.length == 0) {
throw new IllegalArgumentException();
}
return numbers[select(numbers, 0, numbers.length - 1, (numbers.length - 1) / 2)];
}
private static int select(int[] numbers, int left, int right, int i) {
if(left == right) {
return left;
}
int pivotIndex = pivot(numbers, left, right);
pivotIndex = partition(numbers, left, right, pivotIndex, i);
if(i == pivotIndex) {
return i;
}else if(i < pivotIndex) {
return select(numbers, left, pivotIndex - 1, i);
}else {
return select(numbers, left, pivotIndex + 1, i);
}
}
private static int pivot(int numbers[], int left, int right) {
if(right - left < CHUNK) {
return partition5(numbers, left, right);
}
for(int i=left; i<=right; i=i+CHUNK) {
int subRight = i + (CHUNK-1);
if(subRight > right) {
subRight = right;
}
int medChunk = partition5(numbers, i, subRight);
int tmp = numbers[medChunk];
numbers[medChunk] = numbers[(int) (left + Math.floor((double) (i-left)/CHUNK))];
numbers[(int) (left + Math.floor((double) (i-left)/CHUNK))] = tmp;
}
int mid = (right - left) / 10 + left +1;
return select(numbers, left, (int) (left + Math.floor((right - left) / CHUNK)), mid);
}
private static int partition(int[] numbers, int left, int right, int idx, int k) {
int pivotVal = numbers[idx];
int storeIndex = left;
int storeIndexEq = 0;
int tmp = 0;
tmp = numbers[idx];
numbers[idx] = numbers[right];
numbers[right] = tmp;
for(int i=left; i<right; i++) {
if(numbers[i] < pivotVal) {
tmp = numbers[i];
numbers[i] = numbers[storeIndex];
numbers[storeIndex] = tmp;
storeIndex++;
}
}
storeIndexEq = storeIndex;
for(int i=storeIndex; i<right; i++) {
if(numbers[i] == pivotVal) {
tmp = numbers[i];
numbers[i] = numbers[storeIndexEq];
numbers[storeIndexEq] = tmp;
storeIndexEq++;
}
}
tmp = numbers[right];
numbers[right] = numbers[storeIndexEq];
numbers[storeIndexEq] = tmp;
if(k < storeIndex) {
return storeIndex;
}
if(k <= storeIndexEq) {
return k;
}
return storeIndexEq;
}
//Insertion sort
private static int partition5(int[] numbers, int left, int right) {
int i = left + 1;
int j = 0;
while(i<=right) {
j= i;
while(j>left && numbers[j-1] > numbers[j]) {
int tmp = numbers[j-1];
numbers[j-1] = numbers[j];
numbers[j] = tmp;
j=j-1;
}
i++;
}
return left + (right - left) / 2;
}
}
Confirm n (in the pseudo code) or i (in my code) stand for the position of the median? So lets assume our array is number = {9,8,7,6,5,4,3,2,1,0}. I would call select{numbers, 0, 9,4), correct?
I don't understand the calculation of mid in pivot? Why is there a division by 10? Maybe there is a mistake in the pseudo code?
Thanks for your help.
EDIT: It turns out the switch from iteration to recursion was a red herring. The actual issue, identified by the OP, was in the arguments to the 2nd recursive select call.
This line:
return select(numbers, left, pivotIndex + 1, i);
should be
return select(numbers, pivotIndex + 1, right, i);
I'll leave the original answer below as I don't want to appear to be clever than I actually was.
I think you may have misinterpreted the pseudocode for the select method - it uses iteration rather than recursion.
Here's your current implementation:
private static int select(int[] numbers, int left, int right, int i) {
if(left == right) {
return left;
}
int pivotIndex = pivot(numbers, left, right);
pivotIndex = partition(numbers, left, right, pivotIndex, i);
if(i == pivotIndex) {
return i;
}else if(i < pivotIndex) {
return select(numbers, left, pivotIndex - 1, i);
}else {
return select(numbers, left, pivotIndex + 1, i);
}
}
And the pseudocode
function select(list, left, right, n)
loop
if left = right then
return left
pivotIndex := pivot(list, left, right)
pivotIndex := partition(list, left, right, pivotIndex, n)
if n = pivotIndex then
return n
else if n < pivotIndex then
right := pivotIndex - 1
else
left := pivotIndex + 1
This would typically be implemented using a while loop:
private static int select(int[] numbers, int left, int right, int i) {
while(true)
{
if(left == right) {
return left;
}
int pivotIndex = pivot(numbers, left, right);
pivotIndex = partition(numbers, left, right, pivotIndex, i);
if(i == pivotIndex) {
return i;
}else if(i < pivotIndex) {
right = pivotIndex - 1;
}else {
left = pivotIndex + 1;
}
}
}
With this change your code appears to work, though obviously you'll need to test to confirm.
int[] test = {9,8,7,6,5,4,3,2,1,0,13,11,10};
System.out.println("Lower Median: " + lowerMedian(test));
int[] check = test.clone();
Arrays.sort(check);
System.out.println(Arrays.toString(check));
Output:
Lower Median: 6
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13]

StackOverFlow occuring in divide and conquer problem : maximum subArray

I was trying to resolve the maximum subArray sum problem with the divide and conquer approach but a runTime error (StackOverFlow) occured and I have no idea how to handle it, I think it's occuring just because of my recursive calls. Here is my approach (the error occured at the first recursive line):
class Solution {
public int maxSubArray(int[] nums){
int length = nums.length;
int middle = length / 2;
if(length == 1) {
return nums[0];
}
int[] starting = Arrays.copyOfRange(nums, 0, middle+1);
int[] ending = Arrays.copyOfRange(nums, middle +1, length);
int left = maxSubArray(starting);
int right = maxSubArray(ending);
int crossing = computeCrossingSum(starting,ending);
int result = Math.max(left,right);
int finalResult = Math.max(result,crossing);
return finalResult;
}
public int computeCrossingSum (int[] left, int[]right){
int leftS =Integer.MIN_VALUE;
int rightS =Integer.MIN_VALUE;
int leftIndex;
int rightIndex;
int sumS = 0;
for(int i = left.length -1 ; i>=0 ; i--) {
sumS += left[i];
if (sumS > leftS) {
leftS = sumS;
leftIndex = i;
}
}
int sumA = 0;
for(int i = 0 ; i< right.length ; i++){
sumA+=right[i];
if (sumA > rightS){
rightS = sumA;
leftIndex = i;
}
}
int crossingSum = leftS+rightS;
return crossingSum;
}
}
The middle + 1 in the recursive call never allow the array size to be 1, so the stop condition is never met. Remove the + 1
int[] starting = Arrays.copyOfRange(nums, 0, middle);
int[] ending = Arrays.copyOfRange(nums, middle, length);

Maximum Sum SubArray

I am trying to find the contiguous subarray within an array which has the largest sum. So, for the array
{5, 15, -30, 10, -5, 40, 10}
the maximum sum possible using those numbers contiguously would be 55, or (10 + (-5) + 40 + 10) = 55. The program below outputs the maximum sum of 55, however, the problem I am trying to figure out is how to print the sequence that produces this 55. In other words, how can I print out the 10, -5, 40, and 10?
public static void main(String[] args) {
int[] arr = {5, 15, -30, 10, -5, 40, 10};
System.out.println(maxSubsequenceSum(arr));
}
public static int maxSubsequenceSum(int[] X) {
int max = X[0];
int sum = X[0];
for (int i = 1; i < X.length; i++) {
sum = Math.max(X[i], sum + X[i]);
max = Math.max(max, sum);
}
return max;
}
I was thinking of creating an ArrayList to store the sum values at every index of i, so the ArrayList would look like (5, 20, -10, 10, 5, 45, 55). And then I was planning on clearing the ArrayList from index 0 to the first negative number in the list, however, this only solves the problem for this specific example, but if I change the original array of numbers, this solution won't work.
You can replace Math.Max functions by if statements and update start and end index of the best subarray. Pascal version:
if X[i] > sum + X[i] then begin
sum := X[i];
start := i;
end
else
sum := sum + X[i];
if max < sum then begin
max := sum;
finish := i;
end;
You can track the starting and ending indexes of the current best subarray in your loop. Instead of using max() to compute sumand max, just do the following :
int sum_start = 0, sum_end = 0, start = 0, end = 0;
// In the for loop
if (X[i] > sum + X[i]) {
sum = X[i];
sum_start = i;
sum_end = i;
} else {
++sum_end;
}
if (sum > max) {
start = sum_start;
end = sum_end;
max = sum;
}
there is an o(n) solution, a single for loop through the array and reset your sub-sequence whenever your current total is below 0.
{5, 15, -30, 10, -5, 40, 10}
5 + 15 = 20
20 - 30 = -10 (reset sub-sequence)
10 -5 +40 +10 = 55
end. 55 is max sub-sequence
edit: to get subsequence...
whenever you change max, update your subsequence
current left index changes only when u reset
current right index changes every iteration
new max -> save current left and right index...
It can be done by capturing the start and end while identifying maximum sub-array as follows:
Code
package recursion;
import java.util.Arrays;
public class MaximumSubArray {
private static SubArray maxSubArray(int[] values, int low, int high) {
if (low == high) {
// base condition
return new SubArray(low, high, values[low]);
} else {
int mid = (int) (low + high) / 2;
// Check left side
SubArray leftSubArray = maxSubArray(values, low, mid);
// Check right side
SubArray rightSubArray = maxSubArray(values, mid + 1, high);
// Check from middle
SubArray crossSubArray = maxCrossSubArray(values, low, mid, high);
// Compare left, right and middle arrays to find maximum sub-array
if (leftSubArray.getSum() >= rightSubArray.getSum()
&& leftSubArray.getSum() >= crossSubArray.getSum()) {
return leftSubArray;
} else if (rightSubArray.getSum() >= leftSubArray.getSum()
&& rightSubArray.getSum() >= crossSubArray.getSum()) {
return rightSubArray;
} else {
return crossSubArray;
}
}
}
private static SubArray maxCrossSubArray(int[] values, int low, int mid,
int high) {
int sum = 0;
int maxLeft = low;
int maxRight = high;
int leftSum = Integer.MIN_VALUE;
for (int i = mid; i >= low; i--) {
sum = sum + values[i];
if (sum > leftSum) {
leftSum = sum;
maxLeft = i;
}
}
sum = 0;
int rightSum = Integer.MIN_VALUE;
for (int j = mid + 1; j <= high; j++) {
sum = sum + values[j];
if (sum > rightSum) {
rightSum = sum;
maxRight = j;
}
}
SubArray max = new SubArray(maxLeft, maxRight, (leftSum + rightSum));
return max;
}
static class SubArray {
private int start;
private int end;
private int sum;
public SubArray(int start, int end, int sum) {
super();
this.start = start;
this.end = end;
this.sum = sum;
}
public int getStart() { return start; }
public void setStart(int start) { this.start = start; }
public int getEnd() { return end; }
public void setEnd(int end) { this.end = end; }
public int getSum() { return sum; }
public void setSum(int sum) { this.sum = sum; }
#Override
public String toString() {
return "SubArray [start=" + start + ", end=" + end + ", sum=" + sum + "]";
}
}
public static final void main(String[] args) {
int[] values = { 5, 15, -30, 10, -5, 40, 10 };
System.out.println("Maximum sub-array for array"
+ Arrays.toString(values) + ": " + maxSubArray(values, 0, 6));
}
}
Output
Maximum sub-array for array[5, 15, -30, 10, -5, 40, 10]: SubArray [start=3, end=6, sum=55]
Solution can be downloaded from https://github.com/gosaliajigar/Programs/blob/master/src/recursion/MaximumSubArray.java
Two subarray are
[1, 2, 3]
and [4, 9] excluding the negative number
The max sub array here is [ 4, 5]
so the output is 9
Here is the code
public class MaxSubArray{
static void sumM(int a[], int n){
int s1 = Integer.MAX_VALUE;
int k = Integer.MAX_VALUE;
int sum = 0;
int s2 = 0;
for(int i=0;i<n;i++){
if(a[i]<s1){
if(a[i]<0){
k = Math.min(a[i],s1);
}
}
if(a[i]>k){
sum+=a[i];
}
if(a[i]<k){
if(a[i]<0){
continue;
}
s2+=a[i];
}
}
if(sum>s2){
System.out.println(sum);
}
else{
System.out.println(s2);
}
}
public static void main(String[] args){
int a[] = {1,2,3,-7,4,5};
int n = a.length;
sumM(a,n);
}
}
public static int kadane(int[] A) {
int maxSoFar = 0;
int maxEndingHere = 0;
// traverse the given array
for (int i: A) {
// update the maximum sum of subarray "ending" at index `i` (by adding the
// current element to maximum sum ending at previous index `i-1`)
maxEndingHere = maxEndingHere + i;
// if the maximum sum is negative, set it to 0 (which represents
// an empty subarray)
maxEndingHere = Integer.max(maxEndingHere, 0);
// update the result if the current subarray sum is found to be greater
maxSoFar = Integer.max(maxSoFar, maxEndingHere);
}
return maxSoFar;
}
var maxSequence = function(arr){
// ...
if (arr.every((ele) => ele >= 0)) {
return arr.reduce((sum, ele) => sum + ele, 0);
} else if (arr.every((ele) => ele < 0)) {
return 0;
//for me the maximum would be the biggest negative number
//arr.reduce((max, elm) => (max > elm ? max : elm))
} else if (arr === [0]) {
return 0;
} else {
let maxSum = [];
let currentSum = 0;
for (let i = 0; i < arr.length; i++) {
currentSum = Math.max(arr[i], currentSum + arr[i]);
maxSum.push(currentSum);
}
return maxSum.reduce((max, elm) => (max > elm ? max : elm));
}
}
you need to sum all possible sub array. to do that, you can do this code
public static int maxSubsequenceSum(int[] X) {
int max = 0;
boolean max_init = false;
int max_from=0;
int max_to=0; // this is not included
for (int i = 0; i < X.length; i++) {
for (int j = i + 1; j < X.length + 1; j++) {
int total = 0;
for (int k = i; k < j; k++) {
total += X[k];
}
if (total > max || !max_init){
max = total;
max_init = true;
max_from = i;
max_to = j;
}
}
}
for (int i=max_from;i<max_to;i++)
System.out.print(X[i]+",");
System.out.println();
return max;
}

Find Kth min elem by randomized pivot method. Some weird bug

I try to use "randomized pivot" method to find the Kth min elem among given array.
[The code]
public class FindKthMin {
// Find the Kth min elem by randomized pivot.
private static void exchange (int[] givenArray, int firstIndex, int secondIndex) {
int tempElem = givenArray[firstIndex];
givenArray[firstIndex] = givenArray[secondIndex];
givenArray[secondIndex] = tempElem;
}
private static int partition (int[] givenArray, int start, int end, int pivotIndex) {
// Debug:
//System.out.println("debug: start = " + start);
//System.out.println(">> end = " + end);
//System.out.println(">> pivotIndex = " + pivotIndex);
int pivot = givenArray[pivotIndex];
int left = start - 1;
int right = end;
boolean hasDone = false;
while (!hasDone) {
while (!hasDone) {
left ++;
if (left == right) {
hasDone = true;
break;
}
if (givenArray[left] >= pivot) {
// Exchange givenArray[left] and the givenArray[right].
exchange(givenArray, left, right);
break;
}
}
while (!hasDone) {
right --;
if (left == right) {
hasDone = true;
break;
}
if (givenArray[right] < pivot) {
// Exchange the givenArray[right] and the givenArray[left].
exchange(givenArray, right, left);
break;
}
}
}
givenArray[right] = pivot;
// Debug:
//System.out.println(">> split = " + right);
//System.out.println();
return right;
}
private static int findKthMin_RanP_Helper (int[] givenArray, int start, int end, int k) {
if (start > end) return -1;
// Generate a random num in the range[start, end].
int rand = (int)(start + Math.random() * (end - start + 1));
// Using this random num as the pivot index to partition the array in the current scope.
int split = partition(givenArray, start, end, rand);
if (k == split + 1) return givenArray[split];
else if (k < split + 1) return findKthMin_RanP_Helper(givenArray, start, split - 1, k);
else return findKthMin_RanP_Helper(givenArray, split + 1, end, k);
}
public static int findKthMin_RanP (int[] givenArray, int k) {
int size = givenArray.length;
if (k < 1 || k > size) return -1;
return findKthMin_RanP_Helper(givenArray, 0, size - 1, k);
}
// Main method to test.
public static void main (String[] args) {
// Test data: {8, 9, 5, 2, 8, 4}.
int[] givenArray = {8, 9, 5, 2, 8, 4};
// Test finding the Kth min elem by randomized pivot method.
System.out.println("Test finding the Kth min elem by randomized pivot method, rest = " + findKthMin_RanP(givenArray, 1));
}
}
But the result is unstable, sometimes right and sometimes wrong.
Please have a look at the 5th row of findKthMin_RanP_Helper method:
If I change this int split = partition(givenArray, start, end, rand); to int split = partition(givenArray, start, end, end);, the result is always correct. I really can not find what's wrong with this.
EDIT:
The problem comes from the "partition", the new partition should like this:
private static int partition_second_version (int[] givenArray, int start, int end, int pivotIndex) {
int pivot = givenArray[pivotIndex];
int left = start;
int right = end;
while (left <= right) {
while (givenArray[left] < pivot) left ++;
while (givenArray[right] > pivot) right --;
if (left <= right) {
// Exchange givenArray[left] and givenArray[right].
exchange(givenArray, left, right);
left ++;
right --;
}
}
return left;
}
And the findKthMin_RanP_Helper should be changed like this:
private static int findKthMin_RanP_Helper (int[] givenArray, int start, int end, int k) {
if (start > end) return -1;
// Generate a random num in the range[start, end].
int rand = start + (int)(Math.random() * ((end - start) + 1));
// Using this random num as the pivot index to partition the array in the current scope.
int split = partition_second_version (givenArray, start, end, rand);
if (k == split) return givenArray[split - 1];
else if (k < split) return findKthMin_RanP_Helper(givenArray, start, split - 1, k);
else return findKthMin_RanP_Helper(givenArray, split, end, k);
}
Your partition routine could be simplified...
private static int partition(int[] givenArray, int start, int end, int pivotIndex) {
final int pivot = givenArray[pivotIndex];
int left = start;
int right = end;
while (left < right) {
while (left < givenArray.length && givenArray[left] <= pivot) {
left++;
}
while (right > -1 && givenArray[right] > pivot) {
right--;
}
if (left >= right) {
break;
}
exchange(givenArray, right, left);
}
return right;
}
The one bug I see in your code is your partition routine. In the first exchange call, it is not guaranteed that the right index will always point to a value which is < pivot.

Find the Kth smallest element. looping

I try to find k-th minimum element using my code, but can't fix an error in my code.
When it try to make partitioning for [0, 0, 2] with pivot = 0 it's looping.
import java.util.Arrays;
public class OrderStat {
public static void main(String[] args) {
int[] uA = {13, 32, 28, 17, 2, 0, 14, 34, 35, 0};
System.out.println("Initial array: " + Arrays.toString(uA));
int kth = 3; // We will try to find 3rd smallest element(or 2nd if we will count from 0).
int result = getKthSmallestElement(uA, 0, uA.length - 1, kth - 1);
System.out.println(String.format("The %d smallest element is %d", kth, result));
System.out.println("-------------------------------------");
Arrays.sort(uA);
System.out.println("Sorted array for check: " + Arrays.toString(uA));
}
private static int getKthSmallestElement(int[] uA, int start, int end, int kth) {
int l = start;
int r = end;
int pivot = uA[start];
System.out.println("===================");
System.out.println(String.format("start=%d end=%d", start, end));
System.out.println("pivot = " + pivot);
//ERROR HERE: When we will work with [0, 0, 2] part of array with pivot = 0 it will give us infinite loop;
while (l < r) {
while (uA[l] < pivot) {
l++;
}
while (uA[r] > pivot) {
r--;
}
if (l < r) {
int tmp = uA[l];
uA[l] = uA[r];
uA[r] = tmp;
}
}
System.out.println("After partitioning: " + Arrays.toString(uA) + "\n");
if (l < kth)
return getKthSmallestElement(uA, l + 1, end, kth);
else if (l > kth)
return getKthSmallestElement(uA, start, l - 1, kth);
return uA[l];
}
}
Explain me, please, how to fix this problem.
After swapping
if (l < r) {
int tmp = uA[l];
uA[l] = uA[r];
uA[r] = tmp;
}
you need to move l and r (or at least one of them, to make any progress) to the next position (++l; --r;). Otherwise, if both values are equal to the pivot, you loop infinitely.
A correct partitioning that is also usable in a quicksort would be
// make sure to call it only with valid indices, 0 <= start <= end < array.length
private int partition(int[] array, int start, int end) {
// trivial case, single element array - garbage if end < start
if(end <= start) return start;
int pivot = array[start]; // not a good choice of pivot in general, but meh
int left = start+1, right = end;
while(left < right) {
// move left index to first entry larger than pivot or right
while(left < right && array[left] <= pivot) ++left;
// move right index to last entry not larger than pivot or left
while(right > left && array[right] > pivot) --right;
// Now, either
// left == right, or
// left < right and array[right] <= pivot < array[left]
if (left < right) {
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
// move on
++left;
--right;
}
}
// Now left >= right.
// If left == right, we don't know whether array[left] is larger than the pivot or not,
// but array[left-1] certainly is not larger than the pivot.
// If left > right, we just swapped and incremented before exiting the loop,
// so then left == right+1 and array[right] <= pivot < array[left].
if (left > right || array[left] > pivot) {
--left;
}
// Now array[i] <= pivot for start <= i <= left, and array[j] > pivot for left < j <= end
// swap pivot in its proper place in the sorted array
array[start] = array[left];
array[left] = pivot;
// return pivot position
return left;
}
Then you can find the k-th smallest element in an array
int findKthSmallest(int array, int k) {
if (k < 1) throw new IllegalArgumentException("k must be positive");
if (array.length < k) throw new IllegalArgumentException("Array too short");
int left = 0, right = array.length-1, p;
--k; // 0-based indices
while(true) {
p = partition(array, left, right);
if (p == k) return array[p];
if (p < k) {
left = p+1;
k -= left;
} else {
right = p-1;
}
}
// dummy return, never reached
return 0;
}

Categories

Resources