Debugging number of possible sums - JavaScript - javascript

I'm writing an algorithm that finds the number of possible sums from an array that contains unique values, and a second array that contains the quantity of each corresponding value in the first array. For example, the pair [10, 20, 50] and [1, 2, 1] indicates that the total number of elements that I am combining is actually [10, 20, 20, 50], because there are two instances of the number 20.
My algorithm is currently passing every test except one, and I cannot for the life of me figure out why, since it is passing other, more complicated pairings. Here is my algorithm so far:
function possibleSums(values, quantity) {
const sums = new Set([]);
//my recursive function that finds all possible combinations moving
//down from a specific starting index in the valueArrray:
const combinations = (valueArray, countArray, position, currentSum) => {
if (currentSum > 0) sums.add(currentSum);
for(let i = position; i < valueArray.length; i++){
if (countArray[i] === 0){
continue;
}
currentSum += valueArray[i];
//reduce the count of that value that is still available
countArray[i]--;
//send off a recursive call to find the sum with the next
//available value
combinations(valueArray, countArray, i, currentSum);
//return the original count since `i` is increasing past
//the current value's location in the valueArray
countArray[i]++;
}
}
for (let i = 0; i < values.length; i++){
//start the recursive function calls at each index in the value array
combinations(values, quantity, i, 0)
}
return sums.size
}
This algorithm passes array pairs like :
[3, 1, 1] and [111, 84, 104] with the expected output of 521
[1, 1, 1, 1, 1] and [9, 19, 18, 12, 19] with the expected output of 77
[1, 2, 3] and [2, 3, 10000] with the expected output of 30008
but is failing
[10, 50, 100, 500] and [5, 3, 2, 2] , outputting 96 when the expected output is 122
Can anyone spot what I am missing in my logic?

122 expected output is not too big a test case for logging :)
Let's log the parameters:
...
const combinations = (valueArray, countArray, position, currentSum) => {
console.log(countArray, position, currentSum)
if (currentSum...
We see this, which makes sense:
[ 0, 0, 0, 0 ] 3 1400
But then we also see:
[ 0, 0, 1, 0 ] 3 1400
[ 0, 1, 0, 0 ] 3 1400
...
[ 1, 1, 1, 0 ] 3 1400
which don't.
Changing the current argument during the iteration seems to be affecting the variable during other calls.
Changing
currentSum += valueArray[i];
...
combinations(valueArray, countArray, i, currentSum);
to
//currentSum += valueArray[i];
...
combinations(valueArray, countArray, i, currentSum + valueArray[i]);
seems to do the trick.

Related

Does anyone know why my for loop is stopping at 10 and not completing the rest of index in my array test?

Im trying to take an array of numbers and finding the two adjacent numbers with the highest product. So created a function that multiplies the first two indexes od the array and pushes that product to a new array. My code works for the first index positions but stops and doesn't complete the remaining indexes. What am I doing wrong. This is a code signal practice exercise.
Test: inputArray: [3, 6, -2, -5, 7, 3]
Output: [18, -12, 10]
function solution(inputArray) {
var newArray = []
for (var i = 0; i < inputArray.length; i++) {
const indexOneAndTwoProduct = inputArray[0] * inputArray[1]
newArray.push(indexOneAndTwoProduct)
inputArray.shift()
}
return newArray
}
console.log(solution([3, 6, -2, -5, 7, 3]));
It's a good practice do not modify the same array that you are iterating.
I think that something like this could work:
function solution(inputArray) {
const newArray = []
for (let i = 0; i < inputArray.length - 1; i++) {
const indexOneAndTwoProduct = inputArray[i] * inputArray[i + 1]
newArray.push(indexOneAndTwoProduct)
}
return newArray
}
console.log(solution([3, 6, -2, -5, 7, 3]))

Please Help me solve this problem i have trying to solve Range extraction

The problem i try to solve is this using js :
A format for expressing an ordered list of integers is to use a comma separated list of either:
-individual integers
-or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. The range includes all integers in the interval including both endpoints. It is not considered a range unless it spans at least 3 numbers. For example "12,13,15-17"
Complete the solution so that it takes a list of integers in increasing order and returns a correctly formatted string in the range format.
Example:
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]);
// returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
so my idea was to use 3 functions :
1- newRangeStart: creates a new Array in results to store the range numbers and puts in it the first element of the range (RangeStarter).
2-olSupp : deletes elements from the list that were used by the 3rd function RangeArr so that we get a new Arr with a new RangeStarter using 1st function.
3-RangeArr : uses the 1st function than adds elements from the list to the array created by it which are consecutive starting from the Range starter, and then uses the 2nd function to delete the elements used from the ol so the next time we use the RangeArr function it creates another range.
By repeating the RangeArr function with a while loop that runs until ol becomes empty we will have a resuts array with arrays inside of it that contains ranges.
now the poblem is when i run RangeArr function it doesn't delete the used elements from the ol as i want i tried to fix the olSupp function several times but it just doesn't work i think there is a problem in my entire code pls someone help me to fix it here is my code:
function solution(list){
// TODO: complete solution
let ol = [...list];
let results = [];
/*This adds a new array for a range by adding the first number of the range to
an array (2D array) and stores it in the resuts array */
function newRangeStart(orderedlist,result){
result.push([orderedlist[0]]);
return result;
}
/*This functions takes the ol and deletes elements that are found in the results
so that the next time we run the newRangeStart function it creates an other array
for another range with a different start number*/
function olSupp(orderedlist,result){
let toRemove = result.flat();
let newList = [];
for (let i = 0; i < orderedlist.length; i++) {
if(!toRemove.includes(orderedlist[i])){
newList.push(orderedlist[i]);
}
}
orderedlist = [...newList];
return orderedlist;
}
/*Finally RangeArr function creates a range from the ol (ordered list)
starting by the first element of the results array and then uses olSupp to delete
the used numbers from the ol */
function RangeArr (orderedlist,result){
newRangeStart(orderedlist,result);
let i = 0;
while(orderedlist[i+1]- orderedlist[i] == 1 && orderedlist[i+2]- orderedlist[i+1]== 1) {
result[i].push(orderedlist[i+1],orderedlist[i+2]);
i = i+1;
}
olSupp(orderedlist,result);
return result;
}
/*we execute the RangeArr function until ol becomes emepty
and this will give us multiple arrays in the result array containing
the elements of each range found in the ol */
//PS: i didnt put the code beacuse it causes an infinte loop using while
RangeArr(ol,results);
console.log(ol,results);
}
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]);
const solution = arr => {
let s = null, d = 0;
const result = arr.sort((a, b) => a - b).reduce((p, c, i, arr) => {
d++;
if (!s) s = c;
if (arr[i + 1] - s > d) {
s === c ? p.push(s) : d < 3 ? p.push(s, c) : p.push(`${s}-${c}`);
s = null;
d = 0;
}
if (arr[i + 1] === undefined) s === c ? p.push(s) : d < 3 ? p.push(s, c) : p.push(`${s}-${c}`);
return p;
}, []);
return result;
}
console.log(solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]));
Variables:
s (start) for saving the number that starts a range.
d (distance) for counting the distance between the start and end of a range.
for a and b read the doc for javascript sort method to learn more.
same with p (pervious), c (current), i (index) and arr (original array) read the doc for javascript reduce method
Logic:
we can determine a range (for example: [1, 2, 3, 4, 5] is equal to "1-5") by calculate the distance between the starting number 1 and the ending number 5. so the distance between 1 and 5 is 4 because 1 needs to increment by 1 four times to reach 5 and the distance inside the array between the starting number 1 (index 0) and the ending number 5 (index 4) is also 4. so if we take the end minus the start 5 - 1 = 4 if it matches to the correct distance inside the array which is 4 then it is a range "1-5". let's have another example [1, 2, 3, 6, 7, 8], this should be "1-3" and "6-8". when we calculate the distance with 1 and 6 (6 - 1 = 5) we get 5 which is incorrect, because it doesn't match the correct distance inside the array (1 is at index 0 and 6 is at index 3, 3 - 0 = 3. the distance between 1 and 6 is only 3 index apart and not 5, that isn't a range). but if we do the calculate with 1 and 3 it matches our criteria and it's a range "1-3".
Code:
we have to do the calculation inside a loop (i'm using reduce method because it's convenient). first thing i do in the loop is d++ to track the distance of index(s) that the loop had travel inside the array. if (!s) s = c; is for check we've saved a starting number or not. if (arr[i + 1] - s > d) { ... } this is where we do the calculation to see if the current element inside the array minus s is greater than the distance index we've travel or not. if it's true then it means s and the last element must be a range and we push that range in the result array. and then we reset s and d to let them work on the next range.
Update
const solution = arr => {
let s = null;
return arr.sort((a, b) => a - b).reduce((p, c, i, arr) => {
if (!s) s = c;
if (c + 1 !== arr[i + 1]) {
s === c ? p.push(s) : c - 1 === s ? p.push(s, c) : p.push(`${s}-${c}`);
s = null;
}
return p
}, [])
}
console.log(solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]));

Sequence in an array

I have an algo exercise to do and I have no idea how to do it, as the requirement is to use only basic programming concepts. So - the point is to find the longest sequence of numbers in an array that are growing or not changing value.
So for the array [1,1,2,4,0,1,7,4], it would be [1,1,2,4].
It should have as small time and memory complexity as possible. Any solutions, tips? Much love and thanks in advance for any advice or feedback.
That's what I've managed to do in the last 10 minutes, but I feel like I'm doing it in the most complex way possible...
function idk(array) {
var current = 0;
var winner = 0;
var currentArray = [];
var winnerArray = [];
for (let i = 0; i <= array.length; i++) {
if (array[i + 1] >= array[i]) {
currentArray.push(array[i]);
current = currentArray.length;
} else {
currentArray.push(array[i]);
if (currentArray.length > best.length) {
// copy array and append it to the new array?
}
}
}
return winnerArray;
}
Try this javascript algorithm:
var array = [1, 8, 1, 1, 5, 7, 2, 2]
var output = []
array.forEach(function(value, index) {
if (array[index - 1] <= value && index != 0) {
output[output.length - 1].push(value)
} else {
output.push([value])
}
})
var longestArray = []
output.forEach(function(arrayCompare, index) {
if (arrayCompare.length > longestArray.length || index == 0) {
longestArray = arrayCompare
}
})
console.log(longestArray)
The first forEach loops through the elements of array. If the element is larger than or equal to the previous element, it adds it to the last array in output. If it is not, then it creates a new array, and pushes the array into output. This creates arrays with "growing" sequences.
After that, it loops through each sequence, and checks if the length of the sequence is greater than the current longest sequence, which is stored in longestArray. If it is, it changes longestArray to that. If it isn't, it does nothing.
Note that both of these loops have exceptions if the index is 0, since there is no element with index -1 (therefore such an exception had to be made).
Also, here's the same implementation in python:
array = [1, 8, 1, 1, 5, 7, 2, 2]
output = []
index = 0
while index < len(array):
value = array[index]
if (array[index-1] <= value and index !=0):
output[-1].append(value)
else:
output.append([value])
index += 1
longestArray = []
index = 0
while index < len(output):
arrayCompare = output[index]
if index==0 or len(arrayCompare) > len(longestArray):
longestArray = arrayCompare
index += 1
print(longestArray)
Just why loop over the end of the array?
Because you could take this advantage to gather the last longest sequence without having a check after the loop for having found a longest sequence.
But in parts:
Why not a temporary array? Because there is no need to use it, if you collect the values. the startignn index of a sequence is important and the actual indec to decide if the sequence is longer then the previously found one.
The loop feature two conditions, one for continuing the loop, if in sequence and another to check if the actual ended sequence is longer. And for storing the actual index.
The last loop with a check for an undefined value is false, it does not continue the loop and the nect check reveals either a new longest sequence or not.
Some other annotation:
winnerArray has to be an empty array, because of the check later for length
The sequence check take the previous element, because the loop starts with the first index and the previous element is given.
The Big O is O(n).
function idk(array) {
let winnerArray = [],
index = 0;
for (let i = 1; i < array.length + 1; i++) {
if (array[i - 1] <= array[i]) continue;
if (i - index > winnerArray.length) winnerArray = array.slice(index, i);
index = i;
}
return winnerArray;
}
console.log(...idk([1, 1, 2, 4, 0, 1, 7, 4])); // [1, 1, 2, 4]
console.log(...idk([1, 8, 1, 1, 5, 7, 2, 2])); // [1, 1, 5, 7]
console.log(...idk([1, 8, 1, 1, 5, 7, 2, 2, 2, 2, 2]));
Here is the dynamic programming algorithm in Python:
def longest_sequence(numbers):
length = len(numbers)
L = [0] * length #L stores max possible lengths at each index
L[-1] = 1 # base case
for i in range(length-2, -1, -1):
max_length = L[i]
for j in range(i, length):
if (L[j] > max_length) and (numbers[j] > numbers[i]):
max_length = L[j]
L[i] = max_length + 1
#trace back
max_length = max(L)
result = []
for k in range(max_length, 0, -1):
result.append(numbers[L.index(k)])
numbers = numbers[L.index(k):]
L = L[L.index(k):]
return result
my_numbers = [36,13,78,85,16,52,58,61,63,83,46,19,85,1,58,71,26,26,21,31]
print(longest_sequence(my_numbers))
#>>[13, 16, 52, 58, 61, 63, 83, 85]
To be optimal, you should not use intermediate lists during processing. You only need to hold indexes, sizes and the previous value:
def longSeq(A):
longStart,longSize = 0,0 # best range so far (index and size only)
start,size,prev = 0,0,None # current range (index and size)
for i,a in enumerate(A):
if i == 0 or prev <= a: # increase current range
size += 1
prev = a
if size > longSize: # track longest so far
longStart,longSize = start,size
else:
start,size,prev = i,1,a # sequence break, restart current
return A[longStart:longStart+longSize]
output:
print(longSeq([1,1,2,4,0,1,7,4])) # [1, 1, 2, 4]
print(longSeq( [1,8,1,1,5,7,2,2])) # [1, 1, 5, 7]
This will perform in O(n) time and use O(1) space.

Issue trying to find length of sequence in an array

I'm trying to find the length of the sequence in an array, between the first and the second occurance of a specified number.
For Example: lengthOfSequence([0, -3, 7, 4, 0, 3, 7, 9], 7) would return 5, because there are 5 indices between the first and second occurrence of the number 7.
I feel like the code that I have written should work, but after console logging it looks as if my arr.push() method is only pushing the first index to my indexes array variable, and its pushing it twice. Why would this be happening?
Here is my code for context:
var lengthOfSequence = function (arr, n) {
var indexes = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] === n) {
indexes.push(arr.indexOf(arr[i]));
}
}
return arr.indexOf(indexes[1]) - arr.indexOf(indexes[0]);
}
So, for example, if I use my array that I used earlier lengthOfSequence([0, -3, 7, 4, 0, 3, 7, 9], 7), my for loop would find the first occurrence of 7 (index 2) and push it to my index array variable, but it would just do it twice. So my indexes array would just be [2,2]. Why would it not be [2,6]?
indexOf does not do what you think it does. It returns the index of the first item that it finds with the provided value. For both values in the array, it returns that first index.
Since you want the index only and you are already iterating over it with your loop, you can simply use i itself:
indexes.push(i);
You may do it as follows but don't know why it is 5 that you want. I guess it should be 4. OK lets make it 5.
function lengthOfSequence(a,f){
var fi = a.indexOf(f);
return a.slice(fi)
.indexOf(f)+(2*fi+1);
}
var a = [0, -3, 7, 4, 0, 3, 7, 9],
f = 7;
console.log(lengthOfSequence(a,f));
You could use just the index and return the difference between the last element of indices and the first one plus one.
var lengthOfSequence = function(arr, n) {
var indexes = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] === n) {
indexes.push(i);
}
}
return indexes[indexes.length - 1] - indexes[0] + 1;
}
console.log(lengthOfSequence([0, -3, 7, 4, 0, 3, 7, 9], 7)); // 5
console.log(lengthOfSequence([0, -3, 7, 4, 0, 3, 7, 9], -3)); // 1

Javascript Array: Algorithm sorting and picking

In this case I have an array of 4 integers between 0 and 256 that need to be sorted ascending. eg:
[0, 12, 211, 4] when I sort the I get (of course): [0, 4, 12, 211]
I simply get the integer value by requesting Array[0] (first indexed)
now, my problem is; many times, there are equal values in the array. like:
[0, 0, 0, 12] // already sorted
In these cases I need to pick a random index from the topmost equal values (0,0,0), other possiblities are (after sorting):
[211, 211, 211, 255] // results in 0 OR 1 OR 2
[13, 13, 125, 256] // results in 0 OR 1
[4, 211, 211, 255] // results in 0
[0, 1, 1, 4] // results in 0;
so I need to pick a random index from the topmost values in a ascending sorted array.
Is that to be done while sorting , or in a simpler way than a lot of if-elses?
Sorting
If speed is important (which you seem to suggest it is) then have you looked at sorting networks? I have found these to be incredibly fast when sorting small sets of numbers.
To sort with a sorting network:
Network for N=4, using Bose-Nelson
Algorithm.
CreationDate: Tue Feb 15 04:44:06 2011
Creator: perl module
Algorithm::Networksort version 1.05.
Network for N=4, using Bose-Nelson
Algorithm. Input line. Comparator size
1. Comparator size 2. There are 5 comparators in this network, grouped
into 3 parallel operations.
[[0,1],[2,3]] [[0,2],[1,3]] [[1,2]]
This is graphed in 4 columns.
Pseudo:
if [0] > [1] { swap(0, 1) }
if [2] > [3] { swap(2, 3) }
if [0] > [2] { swap(0, 2) }
if [1] > [3] { swap(1, 3) }
if [1] > [2] { swap(1, 2) }
Finding Set of Indexes
Anyway this problem can be solved with a sort of divide and conquer (pseudo):
// First index is unique
if [0] != [1]
return 0
// First 2 are equal
else if [1] != [2]
return 0 or 1
// First 3 are equal
else if [2] != [3]
return 0 or 1 or 2
// All are equal
else
return 0 or 1 or 2 or 3
end
Or you can do this with a loop:
for i = 0 to 2
if [i] != [i+1]
return random(0 to i)
break loop
end if
loop
You should go for the algorithm which makes most semantic sense and is easiest to maintain probably over anything else, unless speed is crucial.
This will return a random index of equal values:
var myNums = new Array(211, 211, 211,211,214, 255);
myNums = myNums.sort();
if(myNums.length == 0)
alert("Array is zero sized");
else
{
var smallest = myNums[0];
var last=0;
var start = 0;
while(smallest == myNums[last])
last++;
last = last-1;
var randIndex = Math.floor(Math.random() *(last - start + 1)+ start);
alert(randIndex);
}
See it work here:
http://jsfiddle.net/rAbh3/
Making a for loop from right to left to select the elements will do the trick and if is done after the sorting process it will only add N to the complexity
Changing from nlogn to nlogn + n is not that much cpu expensive.
Edit:
The top most equal values in your example, shouldn't it be:
[211, 211, 211, 255] // results in 0 OR 1 OR 2
[13, 13, 125, 256] // results in 0 OR 1
[4, 211, 211, 255] // results in 1 or 2
[0, 1, 1, 4] // results in 1 or 2;
??

Categories

Resources