Combination Sum in Javascript - javascript

I'm trying to create the combination sum algorithm in Javascript.
Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
The same repeated number may be chosen from candidates unlimited number of times.
My solution is using recursive method.
var combinationSum = function(candidates, target) {
let ans = []
if(candidates === null || candidates.length === 0)
return ans;
candidates.sort();
let current = []
findNumbers(candidates, target, 0, current, ans);
return ans;
};
const findNumbers = function(candidates, target, i, current, ans){
if(target === 0){
const temp = current.slice();
ans.push(temp);
return;
}
for(let j=i; j<candidates.length; j++){
if(target < candidates[j])
return;
current.push(candidates[j]);
findNumbers(candidates, target - candidates[j], j, current, ans);
current.pop();
}
}
It works with basic tests. However, with the input below it fails.
candidates = [3,12,9,11,6,7,8,5,4], target = 15
My output is:
[[3,3,3,3,3],[3,3,3,6],[3,3,4,5],[3,3,9],[3,4,4,4],[3,4,8],[3,5,7],[3,6,6],[4,4,7],[4,5,6],[5,5,5],[6,9],[7,8]]
The correct output should be:
[[3,3,3,3,3],[3,3,3,6],[3,3,4,5],[3,3,9],[3,4,4,4],[3,4,8],[3,5,7],[3,6,6],[3,12],[4,4,7],[4,5,6],[4,11],[5,5,5],[6,9],[7,8]]
I have no clue why it is not inserting in the ans array the solution [3,12] and [4,11]. Any idea why?
Thanks

You are not getting the correct result because you are not sorting the array correctly
By default, sort function sorts the array items by converting them to strings and then comparing their UTF-16 code units, which means your candidates array
[3, 12, 9, 11, 6, 7, 8, 5, 4]
is sorted as
[11, 12, 3, 4, 5, 6, 7, 8, 9]
You need to provide a custom function to sort the numbers correctly
candidates.sort((a, b) => a - b);

Related

How to get the number nearest to the average of array of numbers? JavaScript

If I have an array of numbers and I want get the number nearest to average, how can I get that number?
I've tried to sort the array in an ascending order, followed by retrieving the middle element which is the average.
Note: I used Math.floor() in case if the length of the array is odd not even number
const arr = [1, 6, 10, 3, 15, 9, 4, 7];
const arrSorted = arr.sort((a,b)=>a-b);
const nearToAVG = arrSorted[Math.floor(arr.length/2)];
console.log(nearToAVG); // 7
Are there any better ways of getting the number near to the average in array?
Let's dissect this problem. You want the index with the nearest value to the average of the the array indices. To get that you may first calculate the sum of the array. Knowing the sum, you can do a second sweep of the array arr to take the difference of each element from the average and store it in a variable. Lastly, you would update that variable if the difference between the average and the current index is closer to zero than the stored value. By the end of the array, you will have the answer you are looking for.
Here is a simple example of the above:
const arr = [1, 6, 10, 3, 15, 9, 4, 7];
// get average
let sum = 0;
for(let i = 0; i < arr.length; sum += arr[i], i++);
let ave = sum / arr.length; // 6.875
function nearestIdxToAverage(array, average) {
if(!array.length) return 0;
// stores initial difference and gets replaced by index
let nearest = Math.abs(array[0] - average);
for(let i = 0; i < array.length; i++) {
const diff = Math.abs(array[i] - average);
// stores the offset of the diff closest to 0 (relative to the average)
nearest = (diff < nearest)? i: nearest;
}
return nearest;
}
console.log(`average: ${ave}\nnearest idx: ${nearestIdxToAverage(arr, ave)}`);
// average: 6.875
// nearest idx: 7
As I noted in a comment to another answer, this is one of those cases, when you really need to be more precise than "average". The median and the mean are the most common averages, and depending on which you want, you may get different answers. For instance, in [3, 20, 1, 4, 2], the median is 3, but, since the mean is 6, the original value closest to the mean is 4.
This version assumes you want to get the value in the array closest to the arithmetic mean of the numbers:
const closestToMean = (ns, m = ns .reduce ((a, b) => a + b, 0) / ns .length) =>
ns .reduce ((c, n) => Math .abs (n - m) < Math .abs (c - m) ? n : c, Infinity)
console .log (closestToMean ([3, 20, 1, 4, 2]))
console .log (closestToMean ([1, 6, 10, 3, 15, 9, 4, 7]))
We sum the numbers and divide by their length to find the mean, then we fold the list of numbers, keeping at every stage the one closer to that mean.
I would actually prefer to break this up and extract the common helper functions sum, which totals an array of numbers, mean, which uses sum to calculate the arithmetic mean, and minimumBy, which finds the smallest element of an array, according to the result of applying the function supplied to each element. I think this version is much more readable:
const sum = (ns) =>
ns .reduce ((a, b) => a + b, 0)
const mean = (ns) =>
sum (ns) / ns .length
const minimumBy = (fn) => (ns) =>
ns .reduce ((c, n) => fn (n) < fn (c) ? n : c, Infinity)
const closestToMean = (ns, m = mean (ns)) =>
minimumBy (n => Math .abs (n - m)) (ns)
console .log (closestToMean ([3, 20, 1, 4, 2]))
console .log (closestToMean ([1, 6, 10, 3, 15, 9, 4, 7]))
... and we've also happened upon some useful functions to use elsewhere in this program or others.
Another approach to achieve that By using:
reduce to accumulate the sum and divide it by its length to get the average.
map iterate over that array and return each number with its nearByAVG (distance between that number and average).
sort by that property in order ascending and get the first one that has the minimum distance to average.
const arr = [1, 6, 10, 3, 15, 9, 4, 7];
const sum = arr.reduce((acc, curr)=>acc+curr);
const avg = sum/arr.length;
const arrMaped = arr.map(num=>({num,nearByAVG: Math.abs(num-avg)}));
const arrSorted = arrMaped.sort((a,b)=>a.nearByAVG-b.nearByAVG);
console.log(arrSorted[0].num)
You should add all the numbers and divide by the length of the array.
Apart from that, the number the sorted array's center, is not the average of the numbers, just the median.
let numArray=[12,32,134,23,54,345,32,1]; //all elements numbers
let num=0;
for(let number of numArray) {num+=number};
num/=numArray.length
console.log(num)

Split array into arrays of numbers where the sum is equal to a specific target

I need to create a function that take as parameter an array and a target. It should return an array of arrays where the sum of these numbers equals to the target
sumPairs(array, target) {
}
For example:
sumPairs([1, 2, 3, 4, 5], 7) // output : [[2, 5], [3, 4]]
I know I have to use map(), and probably reduce(), set(), or filter() maybe (I read their documentation in MDN but still cant find out). I tried some ways but I can't get it.
If you guys could help me to find out how to dynamically create arrays and push them into a new array..
I read there some solutions (Split array into arrays of matching values) but I hate to just use created functions without knowing what they really do or how they work.
Some very basic code for achieving it, Just run all over combinations and conditionally add the items you want.
function sumPairs(array, target) {
var res = [];
for(var i = 0; i < array.length; i++){
for(var j = 0; j < array.length; j++){
if(i!=j && array[i]+array[j]==target &&
res.filter((x)=> x[0] == array[j] && x[1] == array[i]).length == 0 )
res.push([array[i], array[j]]);
}
}
return res;
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
Option 2 - see this answer for more options (like using reduce)
function sumPairs(array, target) {
return array.flatMap(
(v, i) => array.slice(i+1).filter(w => (v!=w && v+w==target)).map(w=> [w,v])
);
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
"The exercise says that it sould be arrays of pairs that sum the
target value so I think only 2 items"
If you need a pair that matches a sum and you pick any number from the list, you are left with
the following equation to solve num + x = sum where we want to find x. E.g. if you picked 7 and the target sum is 10 then you know you are looking for a 3.
Therefore, we can first construct a counting map of the numbers available in our list linear (O(n)) time and then search for matches in linear time as well rather than brute forcing with a quadratic algorithm.
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
return nums.reduce((pairs, num) => {
countByNum[num]--;
const target = sum - num;
if (countByNum[target] > 0) {
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[num]++;
}
return pairs;
}, []);
}
function countGroupByNum(nums) {
return nums.reduce((acc, n) => (acc[n] = (acc[n] || 0) + 1, acc), {});
}
Here's another implementation with more standard paradigms (e.g. no reduce):
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
const pairs = [];
for (const num of nums) {
const target = sum - num; //Calculate the target to make the sum
countByNum[num]--; //Make sure we dont pick the same num instance
if (countByNum[target] > 0) { //If we found the target
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[target]++; //Didin't find a match, return the deducted num
}
}
return pairs;
}
function countGroupByNum(nums) {
const countByNum = {};
for (const num of nums) {
countByNum[num] = (countByNum[num] || 0) + 1;
}
return countByNum;
}
You can also sort your array and find all the pairs with given sum by using two pointer method. Place the first pointer to the start of the array and the second pointer to the end.
if the sum of the values at the two places is :
More than target: Decrement your second pointer by 1
Less than target: Increment your first pointer by 1
Equal to target: This is one possible answer, push them to your answer array and increment your first pointer by 1 and decrement your second pointer by 1.
This is more performant solution with complexity O(n*log(n))

Refactor Nested For Loop

Instructions
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
How can I refactor this to eliminate the nested for-loop? I'd like to get the time complexity down.
Code
const twoSum = function(nums, target) {
for(let i in nums){
for(let j in nums) {
if(nums[i] + nums[j] === target && nums[i] != nums[j]) {
return [i, j];
}
}
}
};
console.log(twoSum([2, 7, 11, 15], 9));
You can save the difference of each element with the target inside an object with the result as keys and the index as values. This will make checking for the existence of an element inside an object without looping though the whole content. In a different loop check if the array elements exist in the object, if they do then you have got the pair. The additional condition is to prevent comparing an element with itself.
const twoSum = function(nums, target) {
const temp = {};
for(let i=0; i<nums.length; i++) {
temp[target - nums[i]] = i;
}
for(let i=0; i<nums.length-1; i++) {
if(temp[nums[i]] && temp[nums[i]] !== i) {
return [i, temp[nums[i]]]
}
}
};
console.log(twoSum([2, 11, 7, 17], 9));
console.log(twoSum([1, 3, 4, 2], 6));
Since this appears to be homework, I'll make a couple suggestions without giving away a complete solution:
Your current code is repeating index checks. For example, you're looping over indices [0,1] and [1,0], which will always have the same sum since a+b = b+a. Instead, I would suggest your loop for i go from 0 to len-1, and your loop for j go from i+1 to len-1. That way you will never duplicate checks.
Part of your current check includes the condition that nums[i] != nums[j], but your problem doesn't state that two values in the array can't be the same. Is it possible to call this function with values like toSum([1, 4, 4], 8) such that 4+4=8? If so, then you can remove the nums[i] != nums[j] check to save time.
It's not clear if the array provided is sorted. If it's not, then you could create a tracking variable to account for values you've already checked, and prevent checking them on future iterations. For example, if you already compared the value 4 against all other values in the array and found no solution, then if you encounter 4 later in the array, there is no reason to check it.
You can solve this problem with O(n) time. The condition is to solve by this approach is that the array must be sorted.
let twosum = (arr, x) => {
let s = 0,
e = arr.length - 1;
let loc = [];
while (s < e) {
if (arr[s] + arr[e] === x) {
loc.push([s,e]);
s++;
e--;
} else if (arr[s] + arr[e] < x) {
s++;
} else {
e--;
}
}
return loc;
};
console.log(twosum([1, 2, 3, 4, 5, 7, 8], 9));
console.log(twosum([2, 7, 11, 15], 9));
The algorithm behind this if anyone interested:
1. Set s value as 0
2. Set e value as last index say (arr.length - 1)
3. While s is less than e i.e not pass one another
4. Check if arr[s] + arr[e] === x then we find it.
4.1. increment s value by 1 as there is no possibility to find any combination before the current s value
4.2. decrement e value by 1 as there is no possibility to find any combination after the current e value
4.3. collect the indexes where the match found.
5. If arr[s] + arr[e] < x
5.1 increment s as there is no possibility to find any combination before the current s value. But there still has the possibility for the e value to get a match.
6. If arr[s] + arr[e] > x
6.1 decrement e as there is no possibility to find any combination after the current e value. But there still has the possibility for the s value to get a match.

Can you sort positive integers in O(N) (linear) time using an object in JavaScript?

I need to sort an array of positive integers.
It's easy enough to do via JavaScript's sort method in O(N * log(N)):
let a = [4, 1, 3, 9, 7, 19, 11];
a.sort((a,b) => a - b);
return a;
// returns [1, 3, 4, 7, 9, 11, 19]
But, it seems like it can be done in O(N) using a JavaScript object?
Looping through the array to add an integer into an object is O(N), then grabbing the values from that object is also O(N). (Alternatively, grab the keys and convert back to numbers).
let o = {};
let a = [4, 1, 3, 9, 7, 19, 11];
a.forEach(integer => { o[integer] = integer });
return Object.values(o);
// returns [1, 3, 4, 7, 9, 11, 19]
Drop the constant and we're looking at sorting positive integers in O(N) (sacrificing additional O(N) space).
From everything I've read, this shouldn't be possible. What am I missing here?
The internal code used for setting and retrieving keys is implementation-dependent. The output (and order) is guaranteed (for all property enumeration methods, as of ES2020), but the mechanism is up to the implementer. You'd have to look at the engine source code for that.
I don't know the code that the different Javascript engines are running under the hood, but if you know of an upper bound on the number in the array, this is possible in O(n) (or, more precisely, O(n + k) where k is a constant - the upper bound) by using counting sort: create a map of the keys (similar to you're doing, but including the number of times each item appears), then iterate from 0 to the upper bound, checking to see if the number being iterated over is included in the keys. If so, push to the array:
let o = {};
let a = [4, 1, 3, 9, 7, 19, 11];
// O(n)
for (const num of a) {
if (!o[num]) {
o[num] = [];
}
o[num].push(num);
}
// O(n). This part isn't strictly necessary, but the alternative makes the code uglier
const max = Math.max(...a);
const result = [];
// O(k)
for (let i = 0; i <= max; i++) {
if (o[i]) {
// total of O(n) items pushed over the whole loop
result.push(...o[i]);
}
}
console.log(result);
If, like in your example, there are no repeated numbers, the code is significantly easier:
let o = {};
let a = [4, 1, 3, 9, 7, 19, 11];
for (const num of a) {
o[num] = true;
}
// O(n)
const max = Math.max(...a);
const result = [];
// O(k)
for (let i = 0; i <= max; i++) {
if (o[i]) {
// total of O(n) items pushed over the whole loop
result.push(i);
}
}
console.log(result);

Generate an array of unique N random numbers which if all numbers summed up equal to N

Assume that N = 3, I want to make a function to generate 3 unique random numbers which if all numbers summed up will equal to 3. For example:
numbers = [1, 0, 2]
numbers = [2, -4, 5]
I already have my own solution in JavaScript below:
let i = 0;
let arr = []
function getRandomInt(number) {
return Math.floor(Math.random()*(number*2)) - number;
}
function generateArray(i, arr, number) {
let lastIndex = number-1;
while (i < lastIndex) {
let randomNumber = getRandomInt(number);
if (arr.indexOf(randomNumber) > -1) {
continue;
} else {
arr[i] = randomNumber;
}
i++;
}
let summed = arr.reduce((a, b) => a+b);
let lastNumber = number - summed;
if (arr.indexOf(lastNumber) > -1) {
return generateArray(lastIndex-1, arr, number);
} else {
arr[lastIndex] = lastNumber;
return arr;
}
}
But I still have a problem with the last index that tends to deviate quite a lot. For example with N = 10, I could have a result like this one below:
numbers = [2, -1, 3, 4, -4, 0, -5, -8, -6, 15]
I wonder if you guys have a much better solution with also a better performance. Thank you!
Here's a snippet that first fills an array with N unique numbers in a range between -N and N.
Then replaces the last value in the array so that the total = N.
When that recalculated final value is already part of in the array, then the function recurses.
To avoid that the last value isn't unique.
And it also recurses when that final value deviates to much.
function getArrayRandomNumbersInRange(min, max, N) {
let arr = [];
while(arr.length < N){
let num = Math.floor(Math.random() * (max - min + 1)) + min;
if(arr.indexOf(num) > -1) continue;
arr[arr.length] = num;
}
let total = arr.reduce(function(accum, val) {return accum + val});
let lastElem = arr[arr.length-1] - total + N;
if(lastElem < min || lastElem > max || (total !== N && arr.indexOf(lastElem) > -1)) {
//console.log(lastElem + ' -> recurse');
arr = [];
return getArrayRandomNumbersInRange(min, max, N);
}
arr[arr.length-1] = lastElem;
return arr;
}
function getArrayRandomNumbers(N){
return getArrayRandomNumbersInRange(-N, N, N);
}
function sumArray(arr){
return arr.reduce(function(accum, val) {return accum + val})
}
let randomUniqueArray = getArrayRandomNumbers(5);
console.log("Total:\t" + sumArray(randomUniqueArray));
console.log(randomUniqueArray);
EDITED: (I thought about this some more, and think I found an even better way, described below)
Here's what I came up with. This makes the list appear more random, and while there may be a few elements that deviate more than others, but the elements that do could be from any index. It only requires one small change.
You have:
let randomNumber = getRandomInt(number);
Replace this with:
let randomNumber = getRandomInt(number) + i - currentSum;
where currentSum is just a rolling sum of the array, and i is an incrementing variable that starts at zero and increments one every pass through, both of which you would update in the else block of the while loop. (In other words, this would replace the summed variable you have as this would keep track of the sum as the array is generating random numbers). What this change aims to do is to normalize the random number to not have the sum go to far from the what the rolling some is supposed to be around. To sum n numbers to add to n, the 'trivial' solution would be to have every number be 1 (i.e. the rolling sum is just the index of the array). What the above code change does is create a random number that generates random numbers around the expected rolling sum I just described. Thus, if I were to run the code a million times, the average value of every value in the array would be 1, which is perfect with regards to wanting a list as you described. I tested this method in Java real quick and it seems to do what you want, so I hope this 'quick fix' helps.
Another idea (I did not test this one though) to further reduce deviation would be to, in addition to the above change, make the generateRandomInt() function generate numbers in a smaller bound, as right now this function generates numbers with a range of 2 * number, which could produce bigger numbers than you want.
Here are a few test arrays I got when I ran my the changed code (with number = 10):
[-3, 10, 0, -4, -1, 6, -5, 5, -7, 9]
[-6, -2, 4, 6, -8, 8, 3, -4, 7, 2]
[-2, 4, -10, 1, 6, 13, -3, -6, 12, -5]
I hope you get the idea of this; hope this helps!
P.S. I believe the code you posted should have the i++ command inside the else block, as otherwise you might not fill up the entire array.

Categories

Resources