I'm looking over an assignment that I finished a few days ago and realized I'm not supposed to use constants. The assignment is the well-known "find the largest sum of a sub-array of integers both positive and negative recursively using a divide and conquer approach" problem. My algorithm works, but a part of it uses a constant in order to figure out the largest sum of sub-arrays that include the middle of the array.
Here's the relevant code:
lfSum = Integer.MIN_VALUE;
sum = 0;
// Sum from left to mid
for (int i = mid; i >= LF; i--) {
sum += array[i];
if (sum > lfSum) {
lfSum = sum;
if (lfSum > lfMax) {
lfMax = lfSum;
}
}
}
rtSum = Integer.MIN_VALUE;
sum = 0;
// Sum from mid to right
for (int j = mid+1; j <= RT; j++) {
sum += array[j];
if (sum > rtSum) {
rtSum = sum;
if (rtSum > rtMax) {
rtMax = rtSum;
}
}
}
// Largest sum spanning whole array
midMax = lfSum + rtSum; // midMax = leftMid + midRight;
What this does is it loops through each half of the entire array and checks to see if the sum is larger than the smallest integer possible in case the entire array is negative. If it is, it sets that side's max sum to sum's value. If that value is larger than what one of the recursive calls returned (lfMax or rtMax), set the respective side's recursive value to it.
Like I said earlier, this works perfectly well, but I'm not supposed to be using "Integer.MIN_VALUE". Is there another way around this? Obviously I could initialize lfSum/rtSum to the numerical value of Integer.MIN_VALUE, but I'd like to know if there are any other options.
I've tried removing rtSum/lfSum and just comparing sum to the recursive values, and initializing lfSum/rtSum to 0, but both did not work correctly. Thanks for taking the time to read this!
You can initialize lfSum as null:
Integer lfSum = null;
And modify the if condition like this:
if (lfSum == null || (lfSum != null && sum > lfSum.intValue())) {
lfSum = sum;
if (lfSum > lfMax) {
lfMax = lfSum;
}
}
Similar strategy applies to rtSum.
Related
int even = 0;
int odd = 0;
for(i=0;i<5;i++){
for(j=0;j<5;j++){
if(j%2>=i) // I think the problem is this
even += twoD[i][j];
else
if(i%2!>=j) // I think the problem is this
odd += twoD[j][i];
}
}
System.out.println("The sum of the even elements above the diagonal is: "+even);
System.out.println("The sum of the odd elements below the diagonal is: "+odd);
This is the code I am working with. The problem is it doesn't display the real sum of the elements above and below as I'd want in a diagonal. The matrix is basically randomized so I had to check it everytime to verify the sum.
For the elements above the diagonal j > i (index of column is always greater than that of the row); for the elements below j < i.
Then the loops may be optimized slightly to provide j > i and swapping indexes for elements below the matrix diagonal:
int even = 0;
int odd = 0;
for(int i=0; i<twoD.length; i++) {
for(int j=i + 1; j<twoD[i].length; j++) {
if(twoD[i][j] %2 == 0) {
even += twoD[i][j];
}
if(twoD[j][i] %2 != 0) { // swap indexes below diagonal
odd += twoD[j][i];
}
}
}
Exactly as you've thought, the conditions are incorrect. Actually I'm not sure what was the logic behind them.
What you need to do is to find whether given number is above or below diagonal. You can do that by comparing indicies themselves - j>i and j<i.
Only then you need to check if a given number (not index) is odd or even - twoD[i][j] % 2 == 0.
Something like so should get the job done:
for(i=0;i<5;i++){
for(j=0;j<5;j++){
if(j>i && twoD[i][j]%2==0){
even += twoD[i][j];
}
if(j<i && twoD[i][j]%2!=0){
odd += twoD[i][j];
}
}
}
Referencing https://www.geeksforgeeks.org/find-subarray-with-given-sum-in-array-of-integers/ for some reason I'm feeling a little thick about this and not totally grasping the reason that once you find the highest index for (current_sum - target_sum) in the map, that you know if you start at the index immediately following that in the array and include the values up to the current index where you encounter this in the array, that you have your subarray solution.
I pretty much get it, that it's because if we've reached a point in our iterating of the array that we've seen the difference between our current sum and the target number, then if we remove that difference from the sum we have found the subarray for the solution, but I can't quite grasp why exactly that is. For example, what if the difference is "2" but the index we have stored in our map where we last saw the sum was "2" is not immediately before the subarray leading up to where we are now and provides the solution. Again, I kind of get it but would appreciate a clear and precise explanation so I have that "aha" moment and more solidly grasp it.
Also wondering the logic that might lead me to this solution after solving this in a different way for positive integers only, namely the efficient solution covered here https://www.geeksforgeeks.org/find-subarray-with-given-sum/.
Thanks.
public static void subArraySum(int[] arr, int n, int sum) {
//cur_sum to keep track of cummulative sum till that point
int cur_sum = 0;
int start = 0;
int end = -1;
HashMap<Integer, Integer> hashMap = new HashMap<>();
for (int i = 0; i < n; i++) {
cur_sum = cur_sum + arr[i];
//check whether cur_sum - sum = 0, if 0 it means
//the sub array is starting from index 0- so stop
if (cur_sum - sum == 0) {
start = 0;
end = i;
break;
}
//if hashMap already has the value, means we already
// have subarray with the sum - so stop
if (hashMap.containsKey(cur_sum - sum)) {
start = hashMap.get(cur_sum - sum) + 1;
end = i;
break;
}
//if value is not present then add to hashmap
hashMap.put(cur_sum, i);
}
// if end is -1 : means we have reached end without the sum
if (end == -1) {
System.out.println("No subarray with given sum exists");
} else {
System.out.println("Sum found between indexes "
+ start + " to " + end);
}
}
I'm reading Cormen's "Introduction to Algorithms".
For the linear algorithm for Max Sum Subarray problem I came up with my own solution. Didn't check existing one (Kadena's) before implementing.
Now I'm testing it with different test scenarios and always have better results than Kadena's. I don't believe in such a luck, but can't find what have I missed. Could you take a look whether it is a working solution?
public void findMaxSubarray(Number[] numbers) {
int maxSum = Integer.MIN_VALUE;
int left = 0;
int right = numbers.length - 1;
int i = 0;
int j = i + 1;
int sum = numbers[i].intValue();
while (i < numbers.length) {
if (maxSum < sum) {
maxSum = sum;
left = i;
right = j - 1;
}
if (j >= numbers.length)
return;
sum = sum + numbers[j].intValue();
if (sum <= 0) {
// ignoring "first" negative numbers. shift i to first non-negative
while (numbers[j].intValue() <= 0) {
if (maxSum < numbers[j].intValue()) {
maxSum = numbers[j].intValue();
left = j;
right = j;
}
if (++j >= numbers.length)
return;
}
i = ++j;
sum = 0;
}
j++;
}
System.out.println(String.format("Max subarray is %d, [%d; %d]", maxSum, left, right));
}
Update
The idea of code is to keep in track only one subarray, and adding to its' tail numbers, when numbers are that low that sum becomes negative - set beginning of array after the tail.
Additionally negative items in the beginning are being ignored. head of subarray is just shifted forward.
Everytime sum appears to be maximum - maxSum and limits are updated.
shift i() --to first non negative number
from j = i+1 up to N.length
sum + N[j]
if sum <= 0
i = j+1
if N[i] < 0
shift i()
sum = 0
I think your algorithm is basically sound, but it has two bugs that I can see:
On the input 1 -2 10 3, it will skip over the 10 and output 3. I think you can fix this by changing i = ++j; to i = j;.
In 2 different places you return if j goes past the end, which will cause no output to be produced at all! (This will happen if, e.g., a long list of negative numbers appears at the end of the list.)
Also I don't expect it to be faster (or slower, for that matter) than Kadane's. Summing two numbers is a fast operation, as fast as copying one variable to another, which is what you are doing when you shift the start of the subarray.
Heyo,
I´m actually try to implement a function that takes an integer as input.
I´ve also have an array of ascendent integer numbers.
Now i´ve try to find the closest lower and closest higher number to my single integer.
I´ve like to return it as an array but I´ve only found a solution to find THE one closest number to a given input.
public int getClosestTimeValue(int time) {
int nearest = -1;
int bestDistanceFoundYet = Integer.getInteger(null);
int[] array = null;
// We iterate on the array...
for (int i = 0; i < array.length; i++) {
// if we found the desired number, we return it.
if (array[i] == time) {
return array[i];
} else {
int d = Math.abs(time - array[i]);
if (d < bestDistanceFoundYet) {
nearest = array[i];
}
}
}
return nearest;
}
Has anyone an idea how I can solve this problem in java?
Thank you, Lucas
If you are not required to use an array directly, then you can use a NavigableSet and the ceiling()/floor() methods to get the nearest greater/lesser elements in the set. Example:
NavigableSet<Integer> values = new TreeSet<Integer>();
for (int x : array) { values.add(x); }
int lower = values.floor(time);
int higher = values.ceiling(time);
If you are required to use an array (homework?) then find a good reference on binary search.
At the moment you are searching for one time only. To find both the closest lower and closest higher time, you should have two variables. Then you can check whether the iterated time is lower or higher than the input and store the values in corresponding variables. Also at the moment you are returning only one value, but in order to return multiple values, you should do it through an array.
I'm not sure whether it answers your question, but here's how I would solve the problem:
array = new int[]; // Array of times you have declared elsewhere.
// Method which returns the array of found times.
public int[] getClosestTime(int time) {
int closestLowerTime = 0;
int closestHigherTime = 100; // Value bigger than the largest value in the array.
times = new int[2]; // Array for keeping the two closest values.
// Iterating the array.
for (int i = 0; i < array.length; i++) {
// Finding the two closest values.
int difference = time - array[i];
if (difference > 0 && array[i] > closestLowerTime) {
closestLowerTime = array[i];
} else if (difference < 0 && array[i] < closestHigherTime) {
closestHigherTime = array[i];
}
}
times[0] = closestLowerTime;
times[1] = closestHigherTime;
return times;
}
This finds both the closest lower and higher value and returns them as an array. At the moment I solved it as the times were between 0 and 100, but in case you don't know the largest time value, you can find it through another loop which iterates through the array and stores the largest value in closestHigherTime. I didn't find a proper way to return the exact value through an array, but is it required?
As the array is sorted....
1) Check the middle two elements ..if both are less than the number check the left half (.i.e repeat step1)
else if both are greater than the number repeat step1 for right half...else the selected two numbers are your required answer
I want to find the majority in array (number that appears most of the time).
I have a sorted array and use these cycles:
for(int k = 1;k < length;k++)
{
if(arr[k-1] == arr[k])
{
count++;
if(count > max)
{
max = count;
maxnum = arr[k-1];
}
} else {
count = 0;
}
}
or
for(int h=0;h<length;h++)
{
for(int l=1;l<length;l++)
{
if(arr[h] == arr[l])
{
count++;
if(count > max)
{
max = count;
maxnum = arr[h];
}
} else count = 0;
}
}
they are similiar. When i try them on small arrays everything seems to be ok. But on a long run array with N elements 0<=N<=500000, each element K 0<=K<=10^9 they give wrong answers.
Here is solution with mistake http://ideone.com/y2gvnX. I know there are better algos to find majority but i just need to know where is my mistake.
I really can't find it :( Will really appreciate help!
First of all, you should use the first algorithm, as your array is sorted. 2nd algorithm runs through the array twice unnecessarily.
Now your first algorithm is almost correct, but it has two problems: -
The first problem is you are setting count = 0, in else part,
rather it should be set to 1. Because every element comes at least
once.
Secondly, you don't need to set max every time in your if. Just
increment count, till the if-condition is satisfied, and as soon
as condition fails, check for the current count with current
max, and reset the current max accordingly.
This way, your max will not be checked on every iteration, but only when a mismatch is found.
So, you can try out this code: -
// initialize `count = 1`, and `maxnum = Integer.MIN_VALUE`.
int count = 1;
int max = 0;
int maxnum = Integer.MIN_VALUE;
for(int k = 1;k < length;k++)
{
if(arr[k-1] == arr[k]) {
count++; // Keep on increasing count till elements are equal
} else {
// if Condition fails, check for the current count v/s current max
if (max < count) { // Move this from `if` to `else`
max = count;
maxnum = arr[k - 1];
}
count = 1; // Reset count to 1. As every value comes at least once.
}
}
Note : -
The problem with this approach is, if two numbers say - 1 and 3, comes equal number of times - which is max, then the max count will be counted for 3 (assuming that 3 comes after 1, and maxnum will contain 3 and ignore 1. But they both should be considered.
So, basically, you cannot use a for loop and maintain a count to take care of this problem.
A better way is to create a Map<Integer, Integer>, and store the count of each value in there. And then later on sort that Map on value.
Your first algorithm looks correct to me. The second one (which is what your linked code uses) needs some initialization each time through the loop. Also, the inner loop does not need to start at 1 each time; it can start at h + 1:
for(int h=0; h<length; h++)
{
count = 1; // for the element at arr[h]
for(int l=h + 1; l<length; l++)
{
if(arr[h] == arr[l])
{
count++;
}
}
if(count > max)
{
max = count;
maxnum = arr[h];
}
}
The first algorithm is much better for sorted arrays. Even for unsorted arrays, it would be cheaper to sort the array (or a copy of it) and then use the first algorithm rather than use the second.
Note that if there are ties (such as for the array [1, 1, 2, 2, 3] as per #Rohit's comment), this will find the first value (in the sort order) that has the maximum count.
The error I can readily see is that if all elements are distinct, then the max at end is 0.
However it has to be 1.
So when you update count in "else" case, update it to 1 instead of 0, as a new element has been discovered, and its count is 1.
Your first algorithm only makes sense if the array is sorted.
Your second algorithm just sets count to zero in the wrong place. You want to set count to zero before you enter the inner for loop.
for(int h=0;h<length;h++)
{
count = 0;
for(int l=0;l<length;l++)
{
if(arr[h] == arr[l])
{
count++;
if(count > max)
{
max = count;
maxnum = arr[h];
}
}
}
}
Also, you don't need to check count each time in the inner loop.
max = 0;
for(int h=0;h<length;h++)
{
count = 0;
for(int l=0;l<length;l++)
{
if(arr[h] == arr[l])
count++;
}
if(count > max)
{
max = count;
maxnum = arr[h];
}
}