Maximum Subarray, i can't understand how the code works - javascript

There is a function that finds the largest sum in a subarray
var maxSubArray = function(nums) {
if(nums.length == 0) return 0;
let result = Number.MIN_SAFE_INTEGER;
let sum = 0;
for(let i = 0; i < nums.length; i++) {
sum += nums[i];
result = Math.max(sum, result);
sum = sum < 0 ? 0 : sum;
}
return result;
};
console.log(maxSubArray([-2,1,-3,4,-1,2,1,-5,4]));
But I cannot understand what is changing and why it stops working if you remove the line: 'result = Math.max(sum, result);'
And change return result to sum

If you comment the result line basically the function won't have memory of previous "candidates to best sum". It will just run over the array adding values to the sum variable, even when it lowers the value of the sum (which you can't avoid and it's useful only if you keep track of the previous best sum candidates).
To further explain, imagine you comment that line and return sum.
Every time the sum turns out to be negative it will be immediately reset to 0. So in your proposed example the final max subarray will be effectively starting to sum up on 4 an will keep summing up if the total sum is greater than zero EVEN if after adding the new value the sum is less than its previous value.
So basically it will go like this:
[-2] // sum = -2. Less than 0, so reset to sum = 0
[-2, 1] // sum = 1
[-2, 1, -3] // sum = -2. Less than 0, so reset to sum = 0
[-2, 1, -3, 4] // sum = 4
[-2, 1, -3, 4, -1] // sum = 3
[-2, 1, -3, 4, -1, 2] // sum = 5
[-2, 1, -3, 4, -1, 2, 1] // sum = 6 BEST SUM
[-2, 1, -3, 4, -1, 2, 1, -5] // sum = 1 :(
[-2, 1, -3, 4, -1, 2, 1, -5, 4] // sum = 5
That's why the variable result is necessary. You have to keep track of the best sum so far and compare it with the "local best", so to speak.
In the original code the state throughout the iterations would look something like this:
[-2]
// sum = -2
// result = -2 because -2 is greater than Number.MIN_SAFE_INTEGER.
// sum = 0. It gets reset because it's negative
[-2, 1]
// sum = 1
// result = 1 because 1 > -2
[-2, 1, -3]
// sum = -2
// result = 1 and isn't updated because the overall max sum so far is greater than the current sum
// sum = 0
[-2, 1, -3, 4]
// sum = 4
// result = 4
[-2, 1, -3, 4, -1]
// sum = 3
// result = 4 and isn't updated because the overall max sum is greater than the current sum
[-2, 1, -3, 4, -1, 2]
// sum = 5
// result = 5
[-2, 1, -3, 4, -1, 2, 1]
// sum = 6 BEST SUM
// result = 6
[-2, 1, -3, 4, -1, 2, 1, -5]
// sum = 1
// result = 6
[-2, 1, -3, 4, -1, 2, 1, -5, 4]
// sum = 5
// result = 6
So as you can see, the final value is now 6 because the overall max sum is persisted in the result variable while sum keeps track of the local best.

This is the implementation of Kadane's Algorithm for Largest Sum Subarray.
The gist of the algo is:
Don't add a subarray which is already summing up to a negative number. This will only decrease the sum further.
Result has to be updated everytime with a subarray's sum having value more than it's current value
For example: This is the output of adding console.log at relevant positions.
Note: Here is the link where you can read more: https://www.geeksforgeeks.org/largest-sum-contiguous-subarray/

It is adding all adjacent elements to get subarray with maximum sum.
In above example the subarray is [4,-1,2,1]
all other subarrays having sum smaller than 6.

Related

The elegant way to resolve negative zero in Javascript

I have to multiply the sign of all elements in an array.
For example 1:
input: [1, 2, 3]
output: 1
Explain: 1 * 1 * 1 = 1
Ex2:
input: [1, -2, 3]
output: -1
Explain: 1 * (-1) * 1 = -1
Ex3:
input: [1, -2, 3, 0]
output: 0
Explain: 1 * (-1) * 1 * 0 = 0
And here is my solution
function cal(A)
{
return A.reduce((total, currentValue) => total * Math.sign(currentValue), 1);
}
However, the output of ex3 cal([1, -2, 3, 0]) is -0.
I've already thought about adding one more condition like this
function cal(A)
{
var total = A.reduce((total, currentValue) => total * Math.sign(currentValue), 1);
if(total === 0)
return 0;
else
return total;
}
And obviously, It looks ugly. Is there a more elegant way to resolve that?
In order to avoid conditional checks and keep the function purely computational you can use the curious rules of -0 to simply add 0 to the result of your reduce() which will have no effect on non-zero results but will have the effect of converting -0 to 0.
function cal(arr) {
return arr.reduce((a, c) => a * Math.sign(c), 1) + 0;
}
console.log(cal([1, 2, 3])); // 1
console.log(cal([1, -2, 3])); // -1
console.log(cal([1, -2, 3, 0])); // 0
See signed zero for more general discussion.
In your example, there is no need to multiply all the values. If there is at least one zero, the result is zero, so there is no need to iterate through the entire array. Example below:
function compute(arr)
{
let result = true;
for (let item of arr) {
if (item === 0) return 0;
if (item < 0) result = !result;
}
return result ? 1 : -1;
}
console.log(compute([1, 2, 3]));
console.log(compute([1, -2, 3]));
console.log(compute([1, -2, 3, 0]));

How to iterate over an array multiple times without repeating summed elements

I am trying to solve this problem but I don't know why I can't pass all test cases. I need some help and explanation, how can I count some array (in this example: variable s) multiple times and not repeat the same elements that I already summed.
Problem description:
Lily has a chocolate bar that she wants to share it with Ron for his
birthday. Each of the squares has an integer on it. She decides to
share a contiguous segment of the bar selected such that the length of
the segment matches Ron's birth month and the sum of the integers on
the squares is equal to his birth day. You must determine how many
ways she can divide the chocolate.
Consider the chocolate bar as an array of squares, s=[2,2,1,3,2].
She wants to find segments summing to Ron's birth day, d=4 with a
length equalling his birth month, m=2. In this case, there are two
segments meeting her criteria: [2,2] and [1,3].
Function Description
Complete the birthday function in the editor below. It should return
an integer denoting the number of ways Lily can divide the chocolate
bar.
birthday has the following parameter(s):
s: an array of integers, the numbers on each of the squares of
chocolate,
d: an integer, Ron's birth day, m: an integer, Ron's birth month
My code:
function birthday(s, d, m) {
let bars = 0;
if (m !== 1) {
s.reduce((acc, val) => (acc+val) === d ? ++bars : bars)
} else {
bars = 1;
}
return bars;
}
Some cases:
s = [2, 5, 1, 3, 4, 4, 3, 5, 1, 1, 2, 1, 4, 1, 3, 3, 4, 2, 1]
d = 18
m = 7
s = [4, 5, 4, 5, 1, 2, 1, 4, 3, 2, 4, 4, 3, 5, 2, 2, 5, 4, 3, 2, 3,
5, 2, 1, 5, 2, 3, 1, 2, 3, 3, 1, 2, 5]
d = 18
m = 6
s = [4, 5, 4, 2, 4, 5, 2, 3, 2, 1, 1, 5, 4]
d = 15
m = 4
My code works with this:
s = [1, 2, 1, 3, 2]
d = 3
m = 2
This can be found on HackerRank > Practice > Algorithms > Implementation
You just have to slice the array with the sliced length of m, and then compare that to d
As slice doc:
The slice() method returns a shallow copy of a portion of an array into a new array object selected from start to end (end not included) where start and end represent the index of items in that array. The original array will not be modified.
For example:
s = [1, 2, 1, 3, 2]
m = 2
d = 3
// We loop through s with index stop at s.length - m + 1 for slice to be in correct range
// Slices:
i=0: [1, 2] -> sum=3 -> res=0+1=1
i=1: [2, 1] -> sum=3 -> res=1+1=2
i=2: [1, 3] -> sum=4 -> do nothing
i=4: [3, 2] -> sum=5 -> do nothing
Below is a worked solution
function birthday(s, d, m) {
let res = 0
const sum = (arr) => arr.reduce((acc, el) => acc + el, 0)
for (let i = 0; i < s.length - m + 1; i++) {
if (sum(s.slice(i, i + m)) === d) {
res++
}
}
return res
}
Whenever you are looping over an array to get the summation or do a mathematical equation on it and you have to remove that specific element that you already calculated, You can use one of these built in function to remove an element from an array using a specific index.
Array.prototype.slice()
&& Array.prototype.splice()
Here's an easy to understand way with nested loops:
function birthday(s, d, m) {
var matches = 0; // Total matches found
// Look at chunks starting a position 0. Last chunk can't be m spots past end of array, so last chunk starts at 1 + s.length - m:
for ( let i=0; i < 1 + s.length - m; i++ ) {
var sum = 0; // What this chunk sums to
// Sum up the values of this chunk:
for ( let j=0; j < m; j++ ) {
sum += s[i+j];
}
if ( sum === d ) { // Does this chunk sum to d?
matches++; // Yes!
}
}
return matches;
}

Mini-Max Sum - Wrong output

Given five positive integers, find the minimum and maximum values that can be calculated by summing exactly four of the five integers. Then print the respective minimum and maximum values as a single line of two space-separated long integers.
For example, if the array is [1, 3, 5, 7, 9]. Our minimum sum is 1 + 3 + 5 + 7 = 16 and our maximum sum is 3 + 5 + 7 + 9 = 24.
function miniMaxSum(arr) {
let max = arr.reduce((a, b) => a + b, 1);
let min = arr.reduce((a, b) => a + b, 0, arr.length - 1);
console.log(min, max);
}
Right now, the output should be 10, 14 if the array is just [1, 2, 3, 4, 5].
The output I am getting is 15, 16.
The max variable should just add everything starting from index 1 no?
And the min variable I'm not sure if you're able to do this, but my thinking was initialize the starting at index 0 and go up to the end of the array but minus 1 index.
How can I correct this?
You need to identify which 4 of the 5 elements are the largest, and which 4 of the 5 elements are the smallest - or, equivalently, identify which one element is the smallest, and which one element is the largest, and subtract those from the sum of all 5 elements:
function miniMaxSum(arr) {
// fullSum: sum of all items in the array
const fullSum = arr.reduce((a, b) => a + b, 0);
// Find smallest value in array
const min = Math.min(...arr);
// Find largest value in array
const max = Math.max(...arr);
console.log(fullSum - max, fullSum - min);
}
miniMaxSum([1, 3, 5, 7, 9]);
miniMaxSum([1, 2, 3, 4, 5]);
Another approach is to use slice and sum the small and and the large end of the array.
function miniMax(arr) {
const sum = a => a.reduce((a, b) => a + b, 0);
// we can skip the sort if we know the input is sorted, but just in case
const sorted = arr.sort((a,b) => a-b)
const min = sum(sorted.slice(0, sorted.length-1)) // sum the small end of the array
const max = sum(sorted.slice(1)) // sum the large end of the array
return { min, max }
}
console.log(miniMax([1, 3, 5, 7, 9]));
console.log(miniMax([1, 2, 3, 4, 5]));
This only works if the numbers are listed in ascending order in the array
function miniMaxSum(arr)
{
let max = arr.reduce((a,c,i)=>i?a+c:0, 0);
let min = arr.reduce((a,c,i,t)=>i?a+t[i-1]:0, 0);
document.write(`${JSON.stringify(arr)} -> min: ${min}, max: ${max} <br>`);
}
miniMaxSum([1, 3, 5, 7, 9]);
miniMaxSum([1, 2, 3, 4, 5]);
It was fun to do ;)

Max Average For All Durations

I want to find all possible maximum contiguous subarray averages from an array of values. Each array value represents the value at a duration, the number of seconds passed.
Ex. Input = [6, 4, 3, 10, 5]
Ex. Output = [5.6, 5.75, 6, 7.5, 10]
Output[0] = 6+4+3+10+5 / 5 = 5.6
Output[1] = 6+4+3+10 / 4 = 5.75
Output[2] = 3+10+5 / 3 = 6
Output[3] = 10+5 / 2 = 7.5
Output[4] = 10 / 1 = 10
The issue is that the real data has length of up to 40,000 values.
The result should have the same length as the input. I‘ve done a reduce on a subarray of specific lengths (only getting 5s, 60s, 3600s, etc. length), but that’s not a viable solution for each possible duration. Is there a way I can partition or otherwise create a specialized data structure to get these values? If not, how can I exclude durations as I go?
You can just take the reverse of the input array, then calculate sum and average incrementally. Then again taking the of output array.
const input = [6, 4, 3, 10, 5].reverse();
let output = [];
let total_sum = 0;
for (var i = 0; i < input.length; i++) {
total_sum += input[i];
let avg = total_sum / (i + 1);
output.push(avg);
}
console.log(output.reverse());
(You can even eliminate the reverse by looping the for loop in reverse order.)
Why not .map()? Mixed with reduce you could do something like this:
const output = [
[1, 2, 3, 4],
[5, 6, 7, 8]
];
const averages = output.map(
subarray =>
subarray.reduce(
(previousValue, currentValue) => previousValue + currentValue,
0
) / subarray.length
);
Where subarray is the collection of values, they're then added together and divided by the length of the subarray.
I hope this is what you mean

How do I find the largest negative integer in an array?

let test12 = [-1, -2, -3];
I'm stuck on this one. I used the below on positive integers but I'm not sure what to change for negatives.
let test = [1 , 2, 3];
var largest= 0;
for (i=0; i<=test.length;i++){
if (test[i]>largest) {
largest=test[i];
}
}
console.log(largest);
largest negative integer in an array
Your question can have 3 interpretations:
The negative integer that is farthest from 0
The negative integer that is closest to 0
The largest number in general (you might be struggling if an all negative integer array comes along, because you initialize min to 0)
Just to clarify, smallest is 'farthest from zero'. But here's all three ways :) :
const ints = [-3, -2, -1, 0, 1, 2]
const negativeInts = ints.filter(i => i < 0)
const smallestNegative = Math.min(...negativeInts)
const largestNegative = Math.max(...negativeInts)
const largestOverall = Math.max(...ints)
console.log({smallestNegative, largestNegative, largestOverall}) // -3, -1, 2
Hope this helps. Cheers.
Just initialize largest to -Infinity rather than 0. You also need to iterate over the input array's length, not 0 to largest:
let test12 = [-1, -2, -3];
var largest = -Infinity;
for (i = 0; i < test12.length; i++) {
if (test12[i] > largest) {
var largest = test12[i];
}
}
console.log(largest);
Another method would be to spread into Math.max:
let test12 = [-1, -2, -3];
console.log(
Math.max(...test12)
);
Largest negative would be -244, so you can sort and get the first index.
let arr = [-1, -2, -244, -7],
[largets] = arr.slice().sort((a, b) => a - b);
console.log(largets);
Try this..
var array = [-155,3, 6, 2, 56, 32, 5, -89, -32,115,-150];
array.sort(function(a, b) {
return a - b;
});
console.log(array[0]);
In case you want a programming solution ,like an edit that your program needs to perform even if an all negative array is given, then try this :
let test = [-10, -1, -2, -3];
// just Assign largest to the first element in test.
// the largest int might not be greater than zero,
// but will definitely be larger that any other element in the array.
var largest= test[0];
for (i=0; i<=test.length;i++){
if (test[i]>largest) {
largest=test[i];
}
}
console.log(largest);

Categories