Median of two sorted arrays : Termination condition failing - java

Below code I have written by following the logic from Median of two sorted arrays (method - 2)
You can even see the code at Ideone.com
class MedianOfTwoArrays {
public static void main(String[] args) {
// Note: These are sorted arrays and are of equal length.
int[] array1 = {1, 12, 15, 26, 38};
int[] array2 = {2, 13, 17, 30, 45};
int median = getMedianOfTwoArrays(array1, array2);
System.out.println(median);
}
static int getMedianOfTwoArrays(int[] array1, int[] array2) {
int index1 = array1.length/2;
int index2 = array2.length/2;
int m1 = array1[index1];
int m2 = array2[index2];
if(m1 == m2) {
return m1;
} else {
return findMedian(array1, array2, 0, array1.length - 1, 0, array2.length - 1);
}
}
static int findMedian(int[] array1,
int[] array2,
int low1,
int high1,
int low2,
int high2) {
if((high1 - low1 + 1) == 2 && (high2 - low2 + 1) == 2) {
return (Math.max(array1[low1], array2[low2]) + Math.min(array1[high1], array2[high2]))/2;
}
int mid1 = (low1 + high1)/2;
int mid2 = (low2 + high2)/2;
int m1 = array1[mid1];
int m2 = array2[mid2];
int low1_t = 0;
int high1_t = 0;
int low2_t = 0;
int high2_t = 0;
if(m1 == m2) {
return m1;
} else if(m1 > m2) {
low1_t = low1;
high1_t = mid1;
low2_t = mid2;
high2_t = high2;
return findMedian(array1, array2, low1_t, high1_t, low2_t, high2_t);
} else {
low1_t = mid1;
high1_t = high1;
low2_t = low2;
high2_t = mid2;
return findMedian(array1, array2, low1_t, high1_t, low2_t, high2_t);
}
}
}
It does not work for input arrays like,
int[] array1 = {1, 5, 17, 20}; // median is 10
int[] array2 = {4, 8, 13, 19};
int[] array1 = {1, 3, 5, 7, 9, 11}; // median is 6
int[] array2 = {2, 4, 6, 8, 10, 12};
The problem as per my analysis is, the termination condition. Some how the logic suggessted from geeksforgeeks seems to be having some issue with the termination condition.
(Math.max(array1[low1], array2[low2]) + Math.min(array1[high1], array2[high2]))/2;
But I could not able to solve it and make it work for the above inputs.
Can someone please look into this issue and let me know where am I making mistake?

Your main error is that when you do plain int mid1 = (low1 + high1)/2; your mid1 is always shifted to the left, and then you assign mid1 without taking this shift into account, therefore each nested comparison compares elements of arrays that are shifted left from the intended position, and since median of an array of length 2n is always a[n-1]+a[n]/2, you are comparing wrong elements of arrays after the first performed comparison. You seemingly incorrently implemented this block of Method 2's code:
if (n % 2 == 0)
return getMedian(ar1 + n/2 - 1, ar2, n - n/2 +1);
else
return getMedian(ar1 + n/2, ar2, n - n/2);
In fact, the simple assert (high2-low2==high1-low1) at the entrance to findMedian() would alert you of incorrect logic, since with arrays of size 4 the second entrance yields unequal array sizes. The exit condition is pretty fine, as it's directly copied from Method 2's code. Therefore, you need to change the block of assigning low1_t and others to the following:
assert (high2-low2==high1-low1); // sanity check
int n=high1-low1+1; // "n" from logic
int m1 = median(array1,low1,high1);
int m2 = median(array2,low2,high2);
int low1_t = low1;
int high1_t = high1;
int low2_t = low2;
int high2_t = high2;
if(m1 == m2) {
return m1;
} else if(m1 > m2) {
if (n % 2 == 0) {
high1_t = high1-n/2+1;
low2_t = low2+n/2-1;
} else {
high1_t = high1-n/2;
low2_t = low2+n/2;
}
} else {
if (n % 2 == 0) {
low1_t = low1+n/2-1;
high2_t = high2-n/2+1;
} else {
low1_t = low1+n/2;
high2_t = high2-n/2;
}
}
return findMedian(array1, array2, low1_t, high1_t, low2_t, high2_t);
And add function median like this:
static int median(int[] arr, int low,int hig)
{
if ((low+hig)%2 == 0) return arr[(low+hig)/2];
int mid=(low+hig)/2;
return (arr[mid]+ arr[mid-1])/2;
}
Complete example (alter arrays as necessary): http://ideone.com/zY30Vg

This is a working code and it should solve your problem :-
public static void main(String[] args)
{
int[] ar1 = {1, 3, 5, 7, 9, 11};
int[] ar2 = {2, 4, 6, 8, 10, 12};
System.out.println((int) findMedianSortedArrays(ar1,ar2));
}
public static double findMedianSortedArrays(int A[], int B[]) {
int m = A.length;
int n = B.length;
if ((m + n) % 2 != 0) // odd
return (double) findKth(A, B, (m + n) / 2, 0, m - 1, 0, n - 1);
else { // even
return (findKth(A, B, (m + n) / 2, 0, m - 1, 0, n - 1)
+ findKth(A, B, (m + n) / 2 - 1, 0, m - 1, 0, n - 1)) * 0.5;
}
}
public static int findKth(int A[], int B[], int k,
int aStart, int aEnd, int bStart, int bEnd) {
int aLen = aEnd - aStart + 1;
int bLen = bEnd - bStart + 1;
// Handle special cases
if (aLen == 0)
return B[bStart + k];
if (bLen == 0)
return A[aStart + k];
if (k == 0)
return A[aStart] < B[bStart] ? A[aStart] : B[bStart];
int aMid = aLen * k / (aLen + bLen); // a's middle count
int bMid = k - aMid - 1; // b's middle count
// make aMid and bMid to be array index
aMid = aMid + aStart;
bMid = bMid + bStart;
if (A[aMid] > B[bMid]) {
k = k - (bMid - bStart + 1);
aEnd = aMid;
bStart = bMid + 1;
} else {
k = k - (aMid - aStart + 1);
bEnd = bMid;
aStart = aMid + 1;
}
return findKth(A, B, k, aStart, aEnd, bStart, bEnd);
}

Related

Search in circular sorted martix and run complexity

The method is given NxN matrix always powers of 2 and a number,it will return true if the num is found example for 4x4 size:
this is what i wrote:
public class Search {
public static boolean Search (int [][] matrix, int num)
{
int value = matrix.length / 2;
int first_quarter_pivot = matrix[value-1][0]; // represents highest number in first quarter
int second_quarter_pivot = matrix[value-1][value]; // represents highest number in second quarter
int third_quarter_pivot = matrix[matrix.length-1][value]; // represents highest number in third quarter
int fourth_quarter_pivot = matrix[matrix.length-1][0]; // represents highest number in fourth quarter
boolean isBoolean = false;
int i=0;
int j;
// if the num is not in the range of biggest smallest number it means he can`t be there.
if(!(num >= first_quarter_pivot) && (num <= fourth_quarter_pivot)) {
return false;
}
// if num is one of the pivots return true;
if((num == first_quarter_pivot || (num ==second_quarter_pivot))
|| (num == third_quarter_pivot) || (num == fourth_quarter_pivot ))
return true;
// if num is smaller than first pivot it means num is the first quarter,we limit the search to first quarter.
// if not smaller move to the next quarter pivot
if(num < first_quarter_pivot){{
j =0;
do
if(matrix[i][j] == num) {
isBoolean = true;
break;
}
else if((j == value)) {
j = 0;
i++;
}
else if(matrix[i][j] != num){
j++;
}
while(isBoolean != true) ;
}
return isBoolean;
}
// if num is smaller than second pivot it means num is the second quarter,we limit the search to second quarter.
// if not smaller move to the next quarter pivot
if(num < second_quarter_pivot){{
j = value;// start (0,value) j++ till j=value
do
if(matrix[i][j] == num) {
isBoolean = true;
break;
}
else if((j == matrix.length-1)) {
j = value;
i++;
}
else if(matrix[i][j] != num){
j++;
}
while(isBoolean != true) ;
}
return isBoolean;
}
// if num is smaller than third pivot it means num is the third quarter,we limit the search to third quarter.
// if not smaller move to the next quarter pivot
if(num < third_quarter_pivot){{
i = value;
j = value;// start (0,value) j++ till j=value
do
if(matrix[i][j] == num) {
isBoolean = true;
break;
}
else if((j == matrix.length-1)) {
j = value;
i++;
}
else if(matrix[i][j] != num){
j++;
}
while(isBoolean != true) ;
}
return isBoolean;
}
// if num is smaller than fourth pivot it means num is the fourth quarter,we limit the search to fourth quarter.
// number must be here because we verfied his existence in the start.
if(num < fourth_quarter_pivot){
i = value;
j = 0;// start (0,value) j++ till j=value
do
if(matrix[i][j] == num) {
isBoolean = true;
break;
}
else if((j == value)) {
j = 0;
i++;
}
else if(matrix[i][j] != num){
j++;
}
while(isBoolean != true) ;
}
return isBoolean;
}
}
What i tried to do:
find in which quarter the wanted number is in,after that check
the same quarter by moving j++ until it hits the limit,than i++
until found
with the limits changing for each quarter,i cant understand if run time complexity is O(n^2) or lower? and will it be better do create one dimensional array and and move on the quarter this way: move right until limit,one down,move left until limit and il have a sorted array and just binear search
If you can map an array to a matrix, you can use a normal binary search.
You can define the translation table to achieve that like this:
X = [0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 2, 2, 3, 3, ...]
Y = [0, 1, 1, 0, 2, 3, 3, 2, 2, 3, 3, 2, 0, 1, 1, 0, ...]
The final program looks like this.
static final int MAX_N = 64;
static final int MAX_NN = MAX_N * MAX_N;
static final int[] DX = {0, 0, 1, 1};
static final int[] DY = {0, 1, 1, 0};
static final int[] X = new int[MAX_NN];
static final int[] Y = new int[MAX_NN];
static { // initialize X and Y
for (int i = 0; i < MAX_NN; ++i) {
int x = 0, y = 0;
for (int t = i, f = 0; t > 0; ++f) {
int mod = t & 3;
x += DX[mod] << f; y += DY[mod] << f;
t >>= 2;
}
X[i] = x; Y[i] = y;
}
}
public static boolean Search(int [][] matrix, int num) {
int n = matrix.length, nn = n * n;
int lower = 0;
int upper = nn - 1;
while (lower <= upper) {
int mid = (lower + upper) / 2;
int value = matrix[X[mid]][Y[mid]];
if (value == num)
return true;
else if (value < num)
lower = mid + 1;
else
upper = mid - 1;
}
return false;
}
and
public static void main(String[] args) {
int[][] matrix = {
{1, 3, 7, 9},
{6, 4, 15, 11},
{36, 50, 21, 22},
{60, 55, 30, 26},
};
// case: exists
System.out.println(Search(matrix, 1));
System.out.println(Search(matrix, 60));
System.out.println(Search(matrix, 11));
// case: not exists
System.out.println(Search(matrix, 0));
System.out.println(Search(matrix, 70));
System.out.println(Search(matrix, 20));
}
output:
true
true
true
false
false
false

Java: How to implement 3 sum?

I'm studying the 3 Sum to implement it on my own, and came across the following implementation with the rules:
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)
And implementation (sorts the array, iterates through the list, and uses another two pointers to approach the target):
import java.util.*;
public class ThreeSum {
List<List<Integer>> threeSum(int[] num) {
Arrays.sort(num);
List<List<Integer>> res = new LinkedList<>();
for (int i=0; i<num.length-2; i++) {
if (i==0 || (i>0 && num[i] != num[i-1])) { //HERE
int lo = i+1;
int hi = num.length-1;
int sum = 0 - num[i];
while (lo < hi) {
if (num[lo] + num[hi] == sum) {
res.add(Arrays.asList(num[i], num[lo], num[hi]));
while (lo < hi && num[lo] == num[lo+1]) lo++; //HERE
while (lo < hi && num[hi] == num[hi-1]) hi--; //HERE
lo++; hi--;
} else if (num[lo] + num[hi] < sum) lo++;
else hi--;
}
}
}
return res;
}
//Driver
public static void main(String args[]) {
ThreeSum ts = new ThreeSum();
int[] sum = {-1, 0, 1, 2, -1, -4};
System.out.println(ts.threeSum(sum));
}
}
And my question is (located where commented: //HERE), what's the reason for checking num[i] != num[i-1], num[lo] == num[lo+1], and num[hi] == num[hi-1]? Supposedly they are supposed to skip the same result, but what does that mean? Examples would really help.
Thank you in advance and will accept answer/up vote.
Imagine you have {-1,-1,0,1,2,4} and considering triplet num[0], num[2], num[3] (-1,0,1).
lo=0 here. To exclude triplet num[1], num[2], num[3] with the same values, we should increment lo and pass over duplicate
This will prevent the list to have duplicate triplet.
For example, with you test :
int[] sum = {-1, 0, 1, 2, -1, -4};
will be sorted like :
sum = {-4, -1, -1, 0, 1, 2};
You see that you have -1 twice. Without these test, you would test twice if -1 = 0 + 1. This is not usefull so the algo simply search the next different value.
You could remove duplicate in the sorted List to prevent these test.
Thanks to MBo, we can't remove duplicate since we can have triplet with same value (but with different index)
All the three sentences is used to avoid the duplicate output.
Consider a sorted list {-2, -2 , 1, 1}
If there is no checking for num[i] != num[i-1], the output of the program would be(-2, 1, 1)and(-2, 1, 1), which are two duplicate triplets.
The checking for num[lo] != num[lo + 1]and num[hi] != num[hi - 1] are for the same reason.
Consider a sorted list
{-2,-1,-1,0,3}
If there is no checking for num[lo], you will get (-2,-1,3) and (-2,-1,3) as the output.
Still, I want to recommend a better solution for this problem. You can numerate the sum of two numbers in the list and find the 3rd number by hash or binary search. It will helps you to gain a O(n^2logn) time complexity rather than O(n^3). (I was wrong, the time complexity of this algorithm is O(n^2), sorry for that.)
Following program finds pairs of three integer with O(N*2)
Sort the input Array
and iterate each element in for loop and check for sum in program which is developed for Two sum.
Two sum in linear time after sorting ->
https://stackoverflow.com/a/49650614/4723446
public class ThreeSum {
private static int countThreeSum(int[] numbers) {
int count = 0;
for (int i = 0; i < numbers.length; i++) {
int front = 0, rear = numbers.length - 1;
while (front < rear) {
if (numbers[front] + numbers[rear] + numbers[i] == 0) {
System.out.printf(String.format("Front : {%d} Rear : {%d} I : {%d} \n", numbers[front],
numbers[rear], numbers[i]));
front++;
rear--;
count++;
} else {
if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
front++;
} else {
rear--;
}
}
}
}
return count;
}
public static void main(String[] args) {
int[] numbers = { 1, 3, 5, 7, 12, 16, 19, 15, 11, 8, -1, -3, -7, -8, -11, -17, -15 };
Arrays.sort(numbers);
System.out.println(countThreeSum(numbers));
}
}
It's worked with any NSum (3Sum, 4Sum, 5Sum, ...) and quite fast.
public class ThreeSum {
private static final int RANDOM_RANGE = 20;
private Integer array[];
private Integer arrayIndex[];
private int result[];
private int bagLength;
private int resultIndex = 0;
private void generateData(int size) {
array = new Integer[size];
Random random = new Random();
for (int i = 0; i < size; i++) {
array[i] = random.nextInt(RANDOM_RANGE) - (RANDOM_RANGE/2);
}
}
private void markArrayIndex(int size) {
arrayIndex = new Integer[size];
for (int i = 0; i < size; i++) {
arrayIndex[i] = i;
}
}
private void prepareBeforeCalculate(int size, int sumExpected, int bagLength) {
this.bagLength = bagLength;
result = new int[bagLength];
generateData(size);
markArrayIndex(size);
}
void calculate(int size, int sumExpected, int bagLength) {
prepareBeforeCalculate(size, sumExpected, bagLength);
Arrays.sort(arrayIndex, (l, r) -> array[l].compareTo(array[r]));
System.out.println(Arrays.toString(array));
long startAt = System.currentTimeMillis();
if (sumExpected > 0) findLeft(sumExpected, 0, 0, array.length);
else findRight(sumExpected, 0, 0 - 1, array.length - 1);
System.out.println("Calculating in " + ((System.currentTimeMillis() - startAt) / 1000));
}
private void findLeft(int total, int indexBag, int left, int right) {
while (left < array.length && array[arrayIndex[left]] < 0 && indexBag < bagLength) {
navigating(total, arrayIndex[left], indexBag, left, right);
left++;
}
}
private void findRight(int total, int indexBag, int left, int right) {
while (right >= 0 && array[arrayIndex[right]] >= 0 && indexBag < bagLength) {
navigating(total, arrayIndex[right], indexBag, left, right);
right--;
}
}
private void navigating(int total, int index, int indexBag, int left, int right) {
result[indexBag] = index;
total += array[index];
if (total == 0 && indexBag == bagLength - 1) {
System.out.println(String.format("R[%d] %s", resultIndex++, toResultString()));
return;
}
if (total > 0) findLeft(total, indexBag + 1, left + 1, right);
else findRight(total, indexBag + 1, left, right - 1);
}
private String toResultString() {
int [] copyResult = Arrays.copyOf(result, result.length);
Arrays.sort(copyResult);
int iMax = copyResult.length - 1;
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(array[copyResult[i]]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
}
public class ThreeSumTest {
#Test
public void test() {
ThreeSum test = new ThreeSum();
test.calculate(100, 0, 3);
Assert.assertTrue(true);
}
}

Optimal algorithm for finding max value

I need to design an algorithm to find the maximum value I can get from (stepping) along an int[] at predefined (step lengths).
Input is the number of times we can "use" each step length; and is given by n2, n5 and n10. n2 means that we move 2 spots in the array, n5 means 5 spots and n10 means 10 spots. We can only move forward (from left to right).
The int[] contains the values 1..5, the size of the array is (n2*2 + n5*5 + n10*10). The starting point is int[0].
Example: we start at int[0]. From here we can move to int[0+2] == 3, int[0+5] == 4 or int[0+10] == 1. Let's move to int[5] since it has the highest value. From int[5] we can move to int[5+2], int[5+5] or int[5+10] etc.
We should move along the array in step lengths of 2, 5 or 10 (and we can only use each step length n2-, n5- and n10-times) in such a manner that we step in the array to collect as high sum as possible.
The output is the maximum value possible.
public class Main {
private static int n2 = 5;
private static int n5 = 3;
private static int n10 = 2;
private static final int[] pokestops = new int[n2 * 2 + n5 * 5 + n10 * 10];
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
//TODO: return the maximum value possible
}
}
This is an answer in pseudocode (I didn't run it, but it should work).
fill dp with -1.
dp(int id, int 2stepcount, int 5stepcount, int 10stepcount) {
if(id > array_length - 1) return 0;
if(dp[id][2stepcount][5stepcount][10stepcount] != -1) return dp[id][2stepcount][5stepcount][10stepcount];
else dp[id][2stepcount][5stepcount][10stepcount] = 0;
int 2step = 2stepcount < max2stepcount? dp(id + 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5step = 5stepcount < max5stepcount? dp(id + 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10step = 10stepcount < max10stepcount? dp(id + 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
dp[id][2stepcount][5stepcount][10stepcount] += array[id] + max(2step, 5step, 10step);
return dp[id][2stepcount][5stepcount][10stepcount];
}
Call dp(0,0,0,0) and the answer is in dp[0][0][0][0].
If you wanna go backwards, then you do this:
fill dp with -1.
dp(int id, int 2stepcount, int 5stepcount, int 10stepcount) {
if(id > array_length - 1 || id < 0) return 0;
if(dp[id][2stepcount][5stepcount][10stepcount] != -1) return dp[id][2stepcount][5stepcount][10stepcount];
else dp[id][2stepcount][5stepcount][10stepcount] = 0;
int 2stepForward = 2stepcount < max2stepcount? dp(id + 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5stepForward = 5stepcount < max5stepcount? dp(id + 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10stepForward = 10stepcount < max10stepcount? dp(id + 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
int 2stepBackward = 2stepcount < max2stepcount? dp(id - 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5stepBackward = 5stepcount < max5stepcount? dp(id - 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10stepBackward = 10stepcount < max10stepcount? dp(id - 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
dp[id][2stepcount][5stepcount][10stepcount] += array[id] + max(2stepForward, 5stepForward, 10stepForward, 2stepBackward, 5backForward, 10backForward);
return dp[id][2stepcount][5stepcount][10stepcount];
}
But your paths don't get fulled explored, because we stop if the index is negative or greater than the array size - 1, you can add the wrap around functionality, I guess.
this is a solution but i am not sure how optimal it is !
i did some optimization on it but i think much more can be done
I posted it with the example written in question
import java.util.Arrays;
import java.util.Random;
public class FindMax {
private static int n2 = 5;
private static int n5 = 3;
private static int n10 = 2;
private static final int[] pokestops = new int[n2 * 2 + n5 * 5 + n10 * 10];
public static int findMaxValue(int n2, int n5, int n10, int pos, int[] pokestops) {
System.out.print("|");
if (n2 <= 0 || n5 <= 0 || n10 <= 0) {
return 0;
}
int first;
int second;
int third;
if (pokestops[pos] == 5 || ((first = findMaxValue(n2 - 1, n5, n10, pos + 2, pokestops)) == 5) || ((second = findMaxValue(n2, n5 - 1, n10, pos + 5, pokestops)) == 5) || ((third = findMaxValue(n2, n5, n10 - 1, pos + 10, pokestops)) == 5)) {
return 5;
}
return Math.max(Math.max(Math.max(first, second), third), pokestops[pos]);
}
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
//TODO: return the maximum value possible
int max = findMaxValue(n2, n5, n10, 0, pokestops);
System.out.println("");
System.out.println("Max is :" + max);
}
}
You need to calculate following dynamic programming dp[c2][c5][c10][id] - where c2 is number of times you've stepped by 2, c5 - by 5, c10 - by 10 and id - where is your current position. I will write example for c2 and c5 only, it can be easily extended.
int[][][][] dp = new int[n2 + 1][n5 + 1][pokestops.length + 1];
for (int[][][] dp2 : dp) for (int[][] dp3 : dp2) Arrays.fill(dp3, Integer.MAX_VALUE);
dp[0][0][0] = pokestops[0];
for (int c2 = 0; c2 <= n2; c2++) {
for (int c5 = 0; c5 <= n5; c5++) {
for (int i = 0; i < pokestops.length; i++) {
if (c2 < n2 && dp[c2 + 1][c5][i + 2] < dp[c2][c5][i] + pokestops[i + 2]) {
dp[c2 + 1][c5][i + 2] = dp[c2][c5][i] + pokestops[i + 2];
}
if (c5 < n5 && dp[c2][c5 + 1][i + 5] < dp[c2][c5][i] + pokestops[i + 5]) {
dp[c2][c5 + 1][i + 5] = dp[c2][c5][i] + pokestops[i + 5];
}
}
}
}
I know the target language is java, but I like pyhton and conversion will not be complicated.
You can define a 4-dimensional array dp where dp[i][a][b][c] is the maximum value that you can
get starting in position i when you already has a steps of length 2, b of length 5 and c of length
10. I use memoization to get a cleaner code.
import random
values = []
memo = {}
def dp(pos, n2, n5, n10):
state = (pos, n2, n5, n10)
if state in memo:
return memo[state]
res = values[pos]
if pos + 2 < len(values) and n2 > 0:
res = max(res, values[pos] + dp(pos + 2, n2 - 1, n5, n10))
if pos + 5 < len(values) and n5 > 0:
res = max(res, values[pos] + dp(pos + 5, n2, n5 - 1, n10))
if pos + 10 < len(values) and n10 > 0:
res = max(res, values[pos] + dp(pos + 10, n2, n5, n10 - 1))
memo[state] = res
return res
n2, n5, n10 = 5, 3, 2
values = [random.randint(1, 5) for _ in range(n2*2 + n5*5 + n10*10)]
print dp(0, n2, n5, n10)
Suspiciously like homework. Not tested:
import java.util.Arrays;
import java.util.Random;
public class Main {
private static Step[] steps = new Step[]{
new Step(2, 5),
new Step(5, 3),
new Step(10, 2)
};
private static final int[] pokestops = new int[calcLength(steps)];
private static int calcLength(Step[] steps) {
int total = 0;
for (Step step : steps) {
total += step.maxCount * step.size;
}
return total;
}
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
int[] initialCounts = new int[steps.length];
for (int i = 0; i < steps.length; i++) {
initialCounts[i] = steps[i].maxCount;
}
Counts counts = new Counts(initialCounts);
Tree base = new Tree(0, null, counts);
System.out.println(Tree.max.currentTotal);
}
static class Tree {
final int pos;
final Tree parent;
private final int currentTotal;
static Tree max = null;
Tree[] children = new Tree[steps.length*2];
public Tree(int pos, Tree parent, Counts counts) {
this.pos = pos;
this.parent = parent;
if (pos < 0 || pos >= pokestops.length || counts.exceeded()) {
currentTotal = -1;
} else {
int tmp = parent == null ? 0 : parent.currentTotal;
this.currentTotal = tmp + pokestops[pos];
if (max == null || max.currentTotal < currentTotal) max = this;
for (int i = 0; i < steps.length; i++) {
children[i] = new Tree(pos + steps[i].size, this, counts.decrement(i));
// uncomment to allow forward-back traversal:
//children[2*i] = new Tree(pos - steps[i].size, this, counts.decrement(i));
}
}
}
}
static class Counts {
int[] counts;
public Counts(int[] counts) {
int[] tmp = new int[counts.length];
System.arraycopy(counts, 0, tmp, 0, counts.length);
this.counts = tmp;
}
public Counts decrement(int i) {
int[] tmp = new int[counts.length];
System.arraycopy(counts, 0, tmp, 0, counts.length);
tmp[i] -= 1;
return new Counts(tmp);
}
public boolean exceeded() {
for (int count : counts) {
if (count < 0) return true;
}
return false;
}
}
static class Step {
int size;
int maxCount;
public Step(int size, int maxCount) {
this.size = size;
this.maxCount = maxCount;
}
}
}
There's a line you can uncomment to allow forward and back movement (I'm sure someone said in the comments that was allowed, but now I see in your post it says forward only...)

Finding the median of two sorted arrays in logarithmic time

I am trying to understand this solution to the problem of finding the medians of two sorted arrays:
public static double findMedianSortedArrays(int A[], int B[]) {
int m = A.length;
int n = B.length;
if ((m + n) % 2 != 0) // odd
return (double) findKth(A, B, (m + n) / 2, 0, m - 1, 0, n - 1);
else { // even
return (findKth(A, B, (m + n) / 2, 0, m - 1, 0, n - 1)
+ findKth(A, B, (m + n) / 2 - 1, 0, m - 1, 0, n - 1)) * 0.5;
}
}
public static int findKth(int A[], int B[], int k,
int aStart, int aEnd, int bStart, int bEnd) {
int aLen = aEnd - aStart + 1;
int bLen = bEnd - bStart + 1;
// Handle special cases
if (aLen == 0)
return B[bStart + k];
if (bLen == 0)
return A[aStart + k];
if (k == 0)
return A[aStart] < B[bStart] ? A[aStart] : B[bStart];
int aMid = aLen * k / (aLen + bLen); // a's middle count
int bMid = k - aMid - 1; // b's middle count
// make aMid and bMid to be array index
aMid = aMid + aStart;
bMid = bMid + bStart;
if (A[aMid] > B[bMid]) {
k = k - (bMid - bStart + 1);
aEnd = aMid;
bStart = bMid + 1;
} else {
k = k - (aMid - aStart + 1);
bEnd = bMid;
aStart = aMid + 1;
}
return findKth(A, B, k, aStart, aEnd, bStart, bEnd);
}
The first part that I cannot understand is how aMid and bMid defined in the findKth method represent A and B's middle counts. I went through a couple of examples by hand and I could see that, indeed, after comparing A[aMid] to B[bMid], only half of the total number of elements remained. But what is the idea behind the definition of these two indexes? Why are there only half the elements remaining after the comparison of A[aMid] and B[bMid]? Can anyone please explain me this solution?
Say A[] = {1,5,6,7,8,9} and B[] = {2,3,4}, so the median of A[] and B[] should be 5, let's walk through the code.
findMedianSortedArrays(A, B), m=6, n=3
findKth(A, B, 4, 0, 5, 0, 2)
aMid=6*4/(6+3)=2, bMid=4-2-1=1, as a[2]=6>b[1]=3, so k=4-(1-0+1)=2, aEnd=2, bStart=2
findKth(A, B, 2, 0, 2, 2, 2)
aMid=3*2/(3+1)=1, bMid=2-1-1=0, as A[1]=5>B[bMid+bStart]=B[2]=4, so k=2-(2-2+1)=1, aEnd=1, bStart=3
findKth(A, B, 1, 0, 1, 3, 2)
bLen=0, return A[aStart+k]=A[0+1]=A[1]=5
So the overall idea is:
According to the weight of the array length, get the possible median of the array. (refer to aMid and bMid)
Compare A[aMid] and B[bMid], if (A[aMid] > B[bMid]), that means:
For all the elements in B[bStart..bMid], it should be in the left of the median, that's the easy part
And for all the elements in A[aMid+1..aEnd], it should be in the right of the median. That's because (aMid-aStart + bMid-bStart) already equal to (aLen+bLen)/2, and there may be extra elements in B[aMid+1..aEnd] that in left of array median.
So we reduce both of the array to half size, that's the reason run time complexity should be O(log (m+n)).
So recursive to find the midian in A[aStart..aMid] and B[bMid+1..bEnd].
Basic way for java;
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
double res;
int arr[] = new int[nums1.length+nums2.length];
for(int i= 0; i<nums1.length; i++){
arr[i] = nums1[i];
}
for(int i = nums1.length, j = 0; i<nums1.length+nums2.length; i++, j++){
arr[i] = nums2[j];
}
Arrays.sort(arr);
if(arr.length == 1)res = arr[0];
else {
if(arr.length % 2 == 0)res = ((arr[arr.length/2]+arr[(arr.length/2)-1])/2.0);
else{
res = arr[(arr.length/2)];
}
}
return res;
}

Java's equivalent to bisect in python

Is there an equivalent in Java for Python's bisect module? With Python's bisect you can do array bisection with directions. For instance bisect.bisect_left does:
Locate the proper insertion point for item in list to maintain sorted order. The parameters lo and hi may be used to specify a subset of the list which should be considered; by default the entire list is used.
I know I can do this manually with a binary search too, but I was wondering if there is already a library or collection doing this.
You have two options:
java.util.Arrays.binarySearch on arrays
(with various overloads for different array types)
java.util.Collections.binarySearch on List
(with Comparable and Comparator overloads).
Combine with List.subList(int fromIndex, int toIndex) to search portion of a list
To this date (Java 8), this is still missing, so you must still make your own. Here's mine:
public static int bisect_right(int[] A, int x) {
return bisect_right(A, x, 0, A.length);
}
public static int bisect_right(int[] A, int x, int lo, int hi) {
int N = A.length;
if (N == 0) {
return 0;
}
if (x < A[lo]) {
return lo;
}
if (x > A[hi - 1]) {
return hi;
}
for (;;) {
if (lo + 1 == hi) {
return lo + 1;
}
int mi = (hi + lo) / 2;
if (x < A[mi]) {
hi = mi;
} else {
lo = mi;
}
}
}
public static int bisect_left(int[] A, int x) {
return bisect_left(A, x, 0, A.length);
}
public static int bisect_left(int[] A, int x, int lo, int hi) {
int N = A.length;
if (N == 0) {
return 0;
}
if (x < A[lo]) {
return lo;
}
if (x > A[hi - 1]) {
return hi;
}
for (;;) {
if (lo + 1 == hi) {
return x == A[lo] ? lo : (lo + 1);
}
int mi = (hi + lo) / 2;
if (x <= A[mi]) {
hi = mi;
} else {
lo = mi;
}
}
}
Tested with (X being the class where I store static methods that I intend to reuse):
#Test
public void bisect_right() {
System.out.println("bisect_rienter code hereght");
int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
assertEquals(0, X.bisect_right(A, -1));
assertEquals(1, X.bisect_right(A, 0));
assertEquals(6, X.bisect_right(A, 2));
assertEquals(8, X.bisect_right(A, 3));
assertEquals(8, X.bisect_right(A, 4));
assertEquals(9, X.bisect_right(A, 5));
assertEquals(10, X.bisect_right(A, 6));
assertEquals(10, X.bisect_right(A, 7));
}
#Test
public void bisect_left() {
System.out.println("bisect_left");
int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
assertEquals(0, X.bisect_left(A, -1));
assertEquals(0, X.bisect_left(A, 0));
assertEquals(2, X.bisect_left(A, 2));
assertEquals(6, X.bisect_left(A, 3));
assertEquals(8, X.bisect_left(A, 4));
assertEquals(8, X.bisect_left(A, 5));
assertEquals(9, X.bisect_left(A, 6));
assertEquals(10, X.bisect_left(A, 7));
}
Just for completeness, here's a little java function that turns the output from Arrays.binarySearch into something close to the output from bisect_left. I'm obviously missing things, but this does the job for the simple case.
public static int bisectLeft(double[] a, double key) {
int idx = Math.min(a.length, Math.abs(Arrays.binarySearch(a, key)));
while (idx > 0 && a[idx - 1] >= key) idx--;
return idx;
}
Why not do a quick port of the tried and tested Python code itself? For example, here's a Java port for bisect_right:
public static int bisect_right(double[] A, double x) {
return bisect_right(A, x, 0, A.length);
}
private static int bisect_right(double[] A, double x, int lo, int hi) {
while (lo < hi) {
int mid = (lo+hi)/2;
if (x < A[mid]) hi = mid;
else lo = mid+1;
}
return lo;
}
Based on the java.util.Arrays.binarySearch documentation
Here I use the example for a long[] array,
but one can adapt the code to utilize any of the supported types.
int bisectRight(long[] arr, long key) {
int index = Arrays.binarySearch(arr, key);
return Math.abs(index + 1);
}
Note: Limitation on the java API, by the following sentence from javadoc:
If the array contains multiple elements with the specified value,
there is no guarantee which one will be found
Indeed, I've tested that with sorted array of distinct elements.
My use-case was for range grouping, where arr an array of distinct timestamps that indicate the start time of an interval.
You need to define on your own, here's mine:
bisect.bisect_left
public static int bisectLeft(int[] nums, int target) {
int i = 0;
int j = nums.length - 1;
while (i <= j) {
int m = i + (j-i) / 2;
if (nums[m] >= target) {
j = m - 1;
} else {
i = m + 1;
}
}
return i;
}
bisect.bisect_right
public static int bisectRight(int[] nums, int target) {
int i = 0;
int j = nums.length - 1;
while (i <= j) {
int m = i + (j-i) / 2;
if (nums[m] <= target) {
i = m + 1;
} else {
j = m - 1;
}
}
return j+1;
}
Derived from #Profiterole's answer, here is a generalized variant that works with an int->boolean function instead of an array. It finds the first index where the predicate changes.
public class Bisect {
/**
* Look for the last index i in [min, max] such that f(i) is false.
*
* #param function monotonous function going from false to true in the [min, max] interval
*/
public static int bisectLeft(Function<Integer, Boolean> function, int min, int max) {
if (max == min) {
return max;
}
if (function.apply(min)) {
return min;
}
if (!function.apply(max)) {
return max;
}
while (true) {
if (min + 1 == max) {
return min;
}
int middle = (max + min) / 2;
if (function.apply(middle)) {
max = middle;
} else {
min = middle;
}
}
}
/**
* Look for the first index i in [min, max] such that f(i) is true.
*
* #param function monotonous function going from false to true in the [min, max] interval
*/
public static int bisectRight(Function<Integer, Boolean> function, int min, int max) {
if (max == min) {
return max;
}
if (function.apply(min)) {
return min;
}
if (!function.apply(max)) {
return max;
}
while (true) {
if (min + 1 == max) {
return max;
}
int middle = (max + min) / 2;
if (function.apply(middle)) {
max = middle;
} else {
min = middle;
}
}
}
}
For example, to find the insertion point in an array, the function compares the value inserted with the values of the array:
#Test
public void bisect_right() {
int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
assertEquals(0, bisectRight(f(A, -1), 0, A.length));
assertEquals(1, bisectRight(f(A, 0), 0, A.length));
assertEquals(6, bisectRight(f(A, 2), 0, A.length));
assertEquals(8, bisectRight(f(A, 3), 0, A.length));
assertEquals(8, bisectRight(f(A, 4), 0, A.length));
assertEquals(9, bisectRight(f(A, 5), 0, A.length));
assertEquals(10, bisectRight(f(A, 6), 0, A.length));
assertEquals(10, bisectRight(f(A, 7), 0, A.length));
}
public Function<Integer, Boolean> f(int[] A, int x) {
return n -> (n >= A.length || A[n] > x);
}

Categories