How do I get arrayMaxConsecutiveSum from CodeSignal more efficient? - javascript

I am working through some problems on CodeSignal. I came across this one, arrayMaxConsecutiveSum. I got it to pass almost all tests, but it is timing out on the last one. If I move the test into custom tests, it passes there, so I'm not sure what to do. How do I code it better so that it doesn't time out?
Problem:
Given array of integers, find the maximal possible sum of some of its k consecutive elements.
Example: For inputArray = [2, 3, 5, 1, 6] and k = 2, the output should be
arrayMaxConsecutiveSum(inputArray, k) = 8. All possible sums of 2 consecutive elements are:
2 + 3 = 5;
3 + 5 = 8;
5 + 1 = 6;
1 + 6 = 7.
Thus, the answer is 8.
function arrayMaxConsecutiveSum(inputArray, k) {
let max = 0;
for(let i = 0; i < inputArray.length; i++){
let sum = 0;
let newArr = inputArray.slice(i, i + k);
sum = newArr.reduce((accumulator, currentVal) => accumulator + currentVal);
if(sum > max){
max = sum;
}
}
return max;
}
Error: Tests passed: 19/20. Execution time limit exceeded on test 20: Program exceeded the execution time limit. Make sure that it completes execution in a few seconds for any possible input.

Your current algorithm is O(n ^ 2) because it requires a nested loop.
You can make it O(n) by using a rolling sum instead. Start with the sum of elements 0 to k, then on each iteration, subtract the earliest element that makes up the sum and add the next element not included in the sum yet.
For example, with a k of 2:
start out with the sum of elements [0] and [1]
subtract [0], add [2], compare the new sum
subtract [1], add [3], compare the new sum
and so on.
function arrayMaxConsecutiveSum(inputArray, k) {
let rollingSum = inputArray.slice(0, k).reduce((a, b) => a + b);
let max = rollingSum;
for(let i = 0; i < inputArray.length - k; i++){
rollingSum += inputArray[i + k] - inputArray[i];
max = Math.max(max, rollingSum);
}
return max;
}
console.log(arrayMaxConsecutiveSum([2, 3, 5, 1, 6], 2));

function arrayMaxConsecutiveSum(inputArray, k) {
var max = inputArray.slice(0,k).reduce((a,b)=>a+b);
var cur = max;
for(var i = k; i < inputArray.length; i++) {
cur = cur + inputArray[i] - inputArray[i-k];
if(cur>max)
max = cur
}
return max
}
console.log(arrayMaxConsecutiveSum([2, 3, 5, 1, 6], 2));

Related

Calculate average of any two numbers in a given array in JavaScript?

I was asked this question in an interview. I was unable to solve it.
Suppose we an have array let arr = [4,5,10,9,8].
Write a code in JavaScript to print numbers that are greater than the average of any two elements of the given array.
Suppose I decide to calculate the average of 5 & 9. The average would be 7. So the answer would be numbers greater than 7 i.e 8 9 & 10 should print on the console.
NOTE- We have to find the average of any two elements and then check, not the average of all the numbers.
Can someone please help with the logic?
I don' know if this will help you:
let arr = [4,5,10,9,8]
arr.sort((a,b) => a-b) // sorted array
function greaterThanAverage(x,y){
let average = (x+y)/2
let index = arr.findIndex(e => e > average )
return arr.slice(index == -1 ? arr.length : index)
}
console.log(greaterThanAverage(5,9))
console.log(greaterThanAverage(10,9))
console.log(greaterThanAverage(5,4))
console.log(greaterThanAverage(30,4))
So according to the problem we have to display all numbers which are greater than any average of two elements.
So it's better to choose the two smallest elements in the array and find all the numbers which are greater than their average.
function solve(arr) {
arr.sort(function (a, b) { return a - b });
let firstMinimum = arr[0];
let secondMinimun = arr[1];
let avg = (firstMinimum + secondMinimun) / 2;
for (let i = 0; i < arr.length; i++) {
if (arr[i] > avg) console.log(arr[i]);
}
}
solve([4, 5, 10, 9, 8]);
For me, it looks like you could get only two results:
No result if two max values are the same, so no value is greater than the average.
Only the greatest value if a next smaller value exists.
Another solution could be to select a pair and filter the array basex on the average of the pair.
const
values = [4, 5, 10, 9, 8];
for (let i = 0, l = values.length - 1; i < l; i++) {
for (j = i + 1; j < values.length; j++) {
const average = (values[i] + values[j]) / 2;
console.log(values[i], values[j], '|', average, '|', ...values.filter(v => v > average));
}
}
here is O(n) solution.
find min and secondMin, take average and it will give you min average.
const values = [4, 5, 10, 9, 8];
const minValue = Math.min(...values);
values.splice(values.indexOf(minValue),1);
const secondMinValue = Math.min(...values);
const minAverage = (minValue + secondMinValue)/2;
const result = values.filter(val => val > minAverage);

My nested for loop times out when searching for 2 numbers in an array that add up to a sum value

I'm trying to find a better/faster algorithm that doesn't timeout when I try to find any 2 numbers in an array of numbers that add up to a sum(ex. s). I need to return the pair of numbers that adds up to sum(s) which also have the indices that appear the earliest(there maybe many other pairs). Here is my nested for loop approach.
function sum(ints, s){
let pusher = [];
let count = 1000;
for(let i = 0; i < ints.length; i++){
for(let j = i+1; j < ints.length; j++){
if(ints[i] + ints[j] === s){
if(j < count){
pusher = [ints[i],ints[j]];
count = j;
}
}
}
}
return pusher.length > 0 ? pusher : undefined ;
}
To reduce the computational complexity from O(n ^ 2) to O(n), create a Setinstead. When iterating over a number, check if the set has the value sum - num - if it does, return that pair as the result. Otherwise, put the number into the Set:
Or you could use a Set
function sum(ints, sum) {
const set = new Set();
for (const num of ints) {
const otherNum = sum - num;
if (set.has(otherNum)) {
return [num, otherNum];
} else {
set.add(num);
}
}
}
console.log(sum([3, 4, 99, -2, 55, 66, -3], 1));
console.log(sum([1, 9, 15, 2, 4, 7, 5], 6));

Improve the speed of a JavaScript function

I have a task I found on CodeWars and I managed to solve it, however, after submitting is says:
Execution timed out: (12000 ms)
When I try to test the function is passed, but I guess it is too slow.
Before you condemn me for not finding the answer on my own. I don't really care about submitting that as a response, but I have no idea how to make it faster and that is why I am here.
Here is the function:
const ls = [0, 1, 3, 6, 10]
const partsSums = (ls) => {
const sum = []
for(let i = 0, len = ls.length; i < len + 1; i++) {
let result = ls.slice(i).reduce( (accumulator, currentValue) => accumulator + currentValue, 0)
sum.push(result)
}
return sum
}
Here are the instructions:
Let us consider this example (array written in general format):
ls = [0, 1, 3, 6, 10]
Its following parts:
ls = [0, 1, 3, 6, 10]
ls = [1, 3, 6, 10]
ls = [3, 6, 10]
ls = [6, 10]
ls = [10]
ls = []
The corresponding sums are (put together in a list): [20, 20, 19, 16,
10, 0]
The function parts_sums (or its variants in other languages) will take
as parameter a list ls and return a list of the sums of its parts as
defined above.
For this kind of array maipulations, you better not use build in methods, like slice or reduce, because they are slow in comparison to a for loop, or any other looping approaches.
This approach takes a sinlge loop and uses the index for getting a value of the given array and takes the last sum of the new array.
Some speed tests on Codewars: Sums of Parts:
5621 ms with sparse array sum = []; sum[i] = 0; (the first version of this answer),
3452 ms with Array(i + 1).fill(0) and without sum[i] = 0;,
1261 ms with Array(i + 1) and sum[i] = 0; (find below),
3733 ms with Icepickle's first attempt.
const
partsSums = (ls) => {
let i = ls.length;
const sum = Array(i + 1);
sum[i] = 0;
while (i--) sum[i] = sum[i + 1] + ls[i];
return sum;
},
ls = [0, 1, 3, 6, 10];
console.log(...partsSums(ls));
You can still take a more functional approach but optimise the way you're doing the calculations.
Here is the idea - since you're trying to sum all items, then sum all but the first, then sum all but the second, etc., mathematically equivalent to getting the sum then subtracting from it each number in order and keeping the total.
[sum([41, 42, 43]), sum([42, 43]), sum([43]), sum([])]
is the same as:
total = sum([41, 42, 43])
[total - 0, total - 0 - 41, total - 0 - 41 - 42, total - 0 - 41 - 42- 43]
is the same as:
total = sum([41, 42, 43])
[total -= 0, total -= 41, total -= 42, total -= 43]
Generalised, this looks like:
total = sum([a1, a2, ..., aN])
[total -= 0, total -= a1, total -= a2, ..., total -= aN]
Using the trusty Array#reduce we can derive the sum once. Then we can derive the new array using Array.map using ls.map(num => total -= num).
The only problem here is that we get one less item - we don't calculate the initial total -= 0 which has to exist for all items. One way to do it is to append it to the start [0].concat(ls) will create the correct array to map over. However, since we already know what the value there would be, we can skip this step and directly substitute with total (after all the result of total -= 0 is total and leaves total unchanged). So, we can directly use [total].concat(ls.map(num => total -= num)) to start with total and add the rest of the items. to the end.
const ls = [0, 1, 3, 6, 10]
const partsSums = (ls) => {
let total = ls.reduce((a, b) => a + b, 0);
return [total]
.concat(
ls.map(num => total -= num)
);
}
console.log(partsSums(ls));
Personally, I would just use the previous sum to calculate the next, I don't see any need to re-iterate all the previous sums, so, I would probably go for a basic loop and then reverse the results, like so
function partsSums(ls) {
const result = [0];
if (ls.length === 0) {
return result;
}
for (let i = ls.length, q = 0; i--; q++) {
result.push(result[q] + ls[i]);
}
return result.reverse();
}
or, without reversing, look more like Nina's solution (except for predefining the length of the array)
function partsSums(ls) {
const len = ls.length;
const result = new Array(len+1);
result[len] = 0;
for (let i = len; i--;) {
result[i] = result[i+1] + ls[i];
}
return result;
}
Both also seem to run faster than Nina's on codewars nodejs engine, in the first part probably because of push, in the second one, probably because the array's length is defined from the start, for more information see this question
A solution using normal for loop along the time of execution .
var arr = [0, 1, 3, 6, 10];
function giveList(array){
var sum=0;
for(let i=0;i<array.length;i++){
sum=sum+array[i];
}
var result = [];
result.push(sum);
var temp;
for(let i=0;i<array.length;i++){
temp=sum-array[i];
result.push(temp);
sum=sum-array[i];
}
return result;
}
console.time();
console.log(giveList(arr));
console.timeEnd();
const partsSums = (ls, sum = 0) =>
[...ls, 0].reverse().map(x => sum = x + sum).reverse();
Takes around 1100 ms when I run it on CodeWars, which is slightly faster than other answers.
The repeated operation is too more. e.g: when you compute sum of [3, 6, 10], the up step [1, 3, 6, 10] already compute。 So you can think in another direction, back to end compute the sum of array
const ls = [0, 1, 3, 6, 10];
function partSums(ls) {
const len = ls.length;
const dp = [];
if(len === 0) { return [0] }
dp[len] = 0;
dp[len - 1] = ls[len - 1];
for (let i = len - 2; i >= 0; i--) {
dp[i] = dp[i + 1] + ls[i];
}
return dp;
}

Performance of Array.reduce

I was trying out the Codility MaxCounter question:
You are given N counters, initially set to 0, and you have two possible operations on them:
increase(X) − counter X is increased by 1,
max_counter − all counters are set to the maximum value of any counter.
A non-empty zero-indexed array A of M integers is given. This array represents consecutive operations:
if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
if A[K] = N + 1 then operation K is max_counter.
For example, given integer N = 5 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the values of the counters after each consecutive operation will be:
(0, 0, 1, 0, 0)
(0, 0, 1, 1, 0)
(0, 0, 1, 2, 0)
(2, 2, 2, 2, 2)
(3, 2, 2, 2, 2)
(3, 2, 2, 3, 2)
(3, 2, 2, 4, 2)
The goal is to calculate the value of every counter after all operations.
For example, given:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the function should return [3, 2, 2, 4, 2].
Assume that:
N and M are integers within the range [1..100,000];
each element of array A is an integer within the range [1..N + 1].
Complexity:
expected worst-case time complexity is O(N+M);
expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
Here is my solution, for which i used the reduce method. It scores 40% on performance.
Can anyone see where the performance issue is here?
I'm assuming perhaps it is the reduce speed itself thats the problem and that in order to increase this score i would need to convert this to for loops, but this just feels like a really ugly way to use modern javascript in this context.
Hopefully one of you will point out something non JS related about this solution that will not indicate that reduce is the issue and instead indicate that im an idiot (I will deal with the ramifications of this over a cold beer)
function maxCounter(N, A) {
let maxCounter = 0
const NArray = new Array(N).fill(0)
const results = A.reduce((acc, current) => {
if (current === N + 1) return new Array(N).fill(maxCounter)
const out = acc.map((element, index) => {
if (index + 1 === current){
const newValue = element + 1
if (newValue > maxCounter) maxCounter = newValue
return newValue
} else {
return element
}
})
return out
}
, NArray)
return results
}
const results = maxCounter(5, [1,4,2,5,2,6,2])
console.log({results})
You could introduce a min value, which is set if all values have to be set to the max value, but this does happen only if the value is incremented, then the min value is used for update or at the end to give all items at least the min value.
function maxCounter(n, a) {
var min = 0,
max = 0,
result = [],
i;
for (i of a) {
if (--i === n) {
min = max;
continue;
}
if (!result[i] || result[i] < min) result[i] = min;
if (++result[i] > max) max = result[i];
}
for (i = 0; i < n; i++) {
if (!result[i] || result[i] < min) result[i] = min;
}
return result;
}
console.log(...maxCounter(5, [3, 4, 4, 6, 1, 4, 4]));
console.log(...maxCounter(5, [1, 4, 2, 5, 2, 6, 2]));
The time complexity of your solution is O(NM), which exceeds the required complexity of O(N + M). The reason is that your solution builds a new array of length N for each of the M queries.
This isn't because you used reduce, but rather because you used reduce on an array of length M with a reduction operation which takes O(N) time. If your reduction operation took O(1) time instead, then you would be fine.
It is possible to achieve O(1) time per query by updating the counters array in-place; the hard part is answering the "set-all" query in O(1) time, i.e. without updating all N elements of the counter array. One solution is to record a kind of "timestamp" for the last time a counter was modified, and another "timestamp" for the last time "set-all" was done; this allows you to test if a counter's value is older than the most-recent "set-all" query, and get the correct counter value either way. Here's a class which answers both queries in O(1) using this technique:
class Counters {
constructor(n) {
this.setallVal = 0;
this.setallTimestamp = 0;
this.maxCounter = 0;
this.counts = new Array(n).fill(0);
this.timestamps = new Array(n).fill(0);
}
getCounter(i) {
if (this.timestamps[i] >= this.setallTimestamp) {
return this.counts[i];
} else {
return this.setallVal;
}
}
incCounter(i) {
let c = this.getCounter(i) + 1;
this.counts[i] = c;
this.timestamps[i] = this.setallTimestamp;
if (c > this.maxCounter) {
this.maxCounter = c;
}
}
setAllToMax() {
this.setallVal = this.maxCounter;
this.setallTimestamp++;
}
}
#kaya3 Thanks!
I tried this too which gets 60% but i assume falls over with the same issue of setting the max across all elements
function solution(N, A) {
let maxCounter = 0
const NArray = new Array(N).fill(0)
for (i=0; i<A.length; i++){
if (A[i]>N){
NArray.fill(maxCounter)
} else {
const currentVal = NArray[A[i] -1] + 1
if (currentVal > maxCounter) maxCounter = currentVal
NArray[A[i] -1] = currentVal
}
}
return NArray
}

Possible to use Math.min to get second smallest number from array?

I am using Math.min to get the smallest number out of an array of numbers. However, I also need to get the second smallest number as well. Wondering if there is a way to do this using Math.min as well, otherwise looking for the best way to get this number.
Here's what I have:
var arr = [15, 37, 9, 21, 55];
var min = Math.min.apply(null, arr.filter(Boolean));
var secondMin; // Get second smallest number from array here
console.log('Smallest number: ' + min);
console.log('Second smallest number: ' + secondMin);
just to make that thread completed: the fastest way is to iterate all the elements just like you can do to find minimum. But up to your needs there will be two variables used: first minimum (candidate) and second one.
This logic is O(N) while sorting approach is O(N lg(N)).
But maybe you shouldn't care if this is just for practice.
In case repeatition should be processed as independant value(just like it would be for .sort(...)[1]) then it should <= be used instead of <.
var arr = [15, 37, 9, 21, 55];
var min = Infinity, secondMin = Infinity;
for (var i= 0; i< arr.length; i++) {
if (arr[i]< min) {
secondMin = min;
min = arr[i];
} else if (arr[i]< secondMin) {
secondMin = arr[i];
}
}
console.log('Smallest number: ' + min);
console.log('Second smallest number: ' + secondMin);
I see you are using Array.filter (but why?) on your first min. So, if we are using ES6 features you can find the second lowest value by first removing min from the array.
var secondMin = Math.min.apply(null, arr.filter(n => n != min));
edit: for clarity, unless you are doing something very clever with Array.filter(Boolean) while calculating the first min, you should just pass it the array without filtering:
var min = Math.min.apply(null, arr);
If you are not worried about performance you could simply sort the array.
arr.sort(function(a, b) {
return a - b;
});
#skyboyer provides what is probably the fastest algorithm for finding the two smallest elements. Another type algorithm that runs in O(n) time is selection (in the general comp-sci definition, not how it is normally used in JavaScript), which will find the kth smallest element of an array and separate the elements into those larger and those smaller than the kth.
Even though most partitioning selection algorithms (quickselect, Floyd-Rivest, introselect) run in O(n) time, #skyboyer's answer will be faster because of the specific nature of the partition you are looking for, and because all those algorithms come with a heavy constant factor.
There is a javascript library implementing Floyd-Rivest, but named quickselect that can do the partitioning for you, in place:
quickselect(arr, 1)
arr will be rearranged so that arr[0] is the minimum, arr[1] is the second smallest element, and the remaining elements are in some arbitrary order.
ES6 solution with reduce (very performant) 🚀
const A = [8, 24, 3, 20, 1, 17]
const min = Math.min(...A)
const secondMin = A.reduce((pre, cur) => (cur < pre && cur !== min) ? cur : pre
, Infinity)
console.log(min, secondMin)
This the #skyboyer version that will handle repeats correctly:
function secondSmallest(x) {
if (x.length < 2) return 0;
let first = Number.MAX_VALUE;
let second = Number.MAX_VALUE;
for (let i = 0; i < x.length; i++) {
let current = x[i];
if (current < first) {
second = first;
first = current;
} else if (current < second && current !== first) {
second = current;
}
}
return second;
}
console.log(secondSmallest([1, 1, 1, 1, 2]));
console.log(secondSmallest([1, 2, 1, 1, 1]));
console.log(secondSmallest([6, 3, 4, 8, 4, 5]));
var arr=[1,1,1,1,1,-1,-2];
var firstmin=arr[0];
var secondmin=arr[1];
for(i=0;i<=arr.length;i++){
if(arr[i]<firstmin){
secondmin=firstmin;
firstmin=arr[i];
}else if(i>=1 && arr[i]<secondmin) {
secondmin=arr[i];
}
}
console.log(firstmin);
console.log(secondmin);`
var arr=[1,0,-1,-2,-8,5];
var firstmin=arr[0];
var secondmin=arr[1];
for(i=0;i<=arr.length;i++) {
if(arr[i]<firstmin){
secondmin=firstmin;
firstmin=arr[i];
}
else if(i>=1 && arr[i]<secondmin) {
secondmin=arr[i];
}
}
console.log(firstmin);
console.log(secondmin);
var arr = [15, 37, 9, 21, 55];
var min = Infinity, secondMin = Infinity;
for (var i= 0; i< arr.length; i++) {
if (arr[i]< min) {
secondMin = min;
min = arr[i];
} else if (arr[i]< secondMin) {
secondMin = arr[i];
}
}
console.log('Smallest number: ' + min);
console.log('Second smallest number: ' + secondMin);
A recursive approach with ternary operators would look like this. If the array has duplicates it would give you a non duplicated second min.
For example if you have have [1, 1, 2] the second min would be 2 not 1.
function findMinimums(arr, min, secondMin, i) {
if (arr.length === i) {
return {
min,
secondMin
}
}
return findMinimums(
arr,
min = arr[i] < min ? arr[i] : min,
arr[i] < secondMin && min !== arr[i] ? arr[i] : secondMin,
++i
)
}
const arr = [5, 34, 5, 1, 6, 7, 9, 2, 1];
console.log(findMinimums(arr, arr[0], arr[1], 0))
var arr = [15, 37, 9, 21, 55];
const [secondMin, min] = arr.sort((a,b) => b - a).slice(-2)
console.log('Smallest number: ' + min);
console.log('Second smallest number: ' + secondMin);
function secondSmallest(arr){
let firstSmallest = Infinity, secondSmallest = Infinity;
// if the length is 1; return the element
if(arr.length == 1){
return arr[0]
}
for (let i= 0; i< arr.length; i++) {
if (arr[i]< firstSmallest) {
secondSmallest = firstSmallest;
firstSmallest = arr[i];
} else if (arr[i]< secondSmallest) {
secondSmallest = arr[i];
}
}
console.log(firstSmallest, secondSmallest)
return secondSmallest
}
function findTwoSmallestNumbersFromArray(num) {
let min = Math.min(...num)
let secondMin = Math.min(...num.filter(numbers => !min.toString().includes(numbers)))
console.log(min, secondMin)
}
Yes, it's possible. But rather than directly using Math.min() we have to use Math.min.apply(), as we know Math.min() wouldn't take an array. Here's the solution which will work even if there's duplicates of smallest number in the array.
const findSecondSmallest = function (arr) {
const smallest = Math.min.apply(null, arr);
/*
To remove duplicates too I compared the smallest number through loop.
Otherwise You can direcly set the value of smallest element to 'Infinity'
like this-
arr[arr.indexOf(smallest)] = Infinity;
*/
for (let i = 0; i < arr.length; i++) {
if (smallest === arr[i]) {
arr[i] = Infinity;
}
}
const secondSmallest = Math.min.apply(null, arr);
return secondSmallest;
};
console.log(findSecondSmallest([3, 3, 3, 5, 5, 7, 9, 11, 13])); //5
The Best way to get the second minimum value from an array.
let array = [5, 8, 3, 8, 2, 6, 7, 10, 2];
let minValue = Math.min(...array);
let secondMinVAlue = Math.min(...array.filter((v) => v !== minValue));
console.log(secondMinVAlue);
let num = [4,12,99,1,3,123,5];
let sort = num.sort((a,b) => {return a-b})
console.log(sort[1])

Categories

Resources