i'm working on a bigger problem but am a little stuck on a certain issue. Hopefully, I can explain it clearly! I am looking to generate an array of arrays where each individual array has elements then when added together equal a certain number. An example would be:
target = 4
solution : [[1,1,1,1], [1,1,2], [2,2], [1,3], [4]]
edit: to make the question more clear, the solution should contain every possible combination of positive integers that will equal the target
You could take a recursive approach and loop from the last found item or one and call the function until no more values are to disperse.
function x(count) {
function iter(left, right) {
if (!left) return result.push(right);
for (var i = right[right.length - 1] || 1; i <= left; i++)
iter(left - i, [...right, i]);
}
var result = []
iter(count, []);
return result;
}
x(4).map(a => console.log(...a));
I'm not sure what language you were working in. Also, it's general StackOverflow etiquette to show what you have already tried an what exact step you got stuck on. That said, here is some Python code that does what you want.
This problem is easy to solve as a recursive function. If you have some number n, the first number in a list of sums of n could be any number between 1 and n. Call that number i. Once it's picked, the rest of the list should sum to n - i. So just make a recursive function that adds up the results for all i's that are less than n and all the results for each of the solutions to n-i.
def get_sum_components(n):
# ignore negatives
if n <= 0:
raise ValueError("n must be a positive int")
# The only way to sum to 1 is [1]. This is the base case
if n == 1:
return [[1]]
results = []
# imagine that the first number in the list of sum components was i
for i in range(1, n):
remainder = n - i
# get_sum_components(remainder) will return a list of solutions for n-i
for result in get_sum_components(remainder):
# don't forget to add i back to the beginning so they sum to n
result = [i] + result
results.append(result)
# lastly, just the number itself is always an answer
results.append([n])
return results
print(get_sum_components(4))
# gives [[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4]]
If you don't care about order, this will create some duplicates (like [1, 3], [3, 1]). It should be easy to filter those out, though. (Just sort all the lists and then use a dict/set to remove duplicates)
Related
I'm trying to solve a coding challenge
it gives me an array and waits for the answer ,then gives me the next array and so on.
https://www.codewars.com/kata/5648b12ce68d9daa6b000099/train/javascript
I am trying to take this input: var busStops = [[10,0],[3,5],[5,8]] and return 5.
the code is supposed to add the first in each pair to a total while subtracting the second in each pair from the total eg: 10 - 0 + 3 - 5 + 5 - 8 = 5
First my code loops through the inner arrays and outer array ,pushing it into myarr as a regular array eg: [10,0,3,5,5,8].
It then adds the value if it is index is 0 or even and subtracts it if the index is odd.
This actually works!
Until it is given a second array eg: [[3,0],[9,1],[4,10],[12,2],[6,1],[7,10]]
It is still calculating the total correctly but is still remembering the total from the first array meaning it is returning 22 instead of 17
Why?
There is a var answer = 0 that is being executed ahead of the second loop
It should forget the value of the previous answer.
Right?
Edit: I figured out my problem. I just needed to empty myarr after the total was calculated!
let myarr = [];
var number = function (busStops) {
for (var i = 0; i < busStops.length; i++) {
for (var j = 0; j < busStops[i].length; j++) {
/*console.log(busStops[i][j]);*/
myarr.push(busStops[i][j]);
}
}
console.log("test");
var answer = 0;
console.log("test again");
for (let t = 0; t < myarr.length; t++) {
if (t == 0 || t % 2 == 0) {
answer = answer + myarr[t];
} else {
answer = answer - myarr[t];
}
}
console.log(answer);
return answer;
};
The task at your hand tries to somehow find a value (an integer) from an array of arrays (multidimensional array). That task seems to be reducing that multidimensional array into a single integer.
Luckily, JavaScript has some powerful array methods and one of them is the reduce method:
The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value. Source: MDN
The reduce method tries to reduce an array's elements into a single value and in your case we want to reduce that multidimensional array into a single value that is the number persons who are still in the bus.
Before typing some code, let's dig a bit deeper into the reduce method:
It accepts 2 parameters, a callback function that acts as the reducer and the initial value to be used in the first iteration of the reduce method.
The reducer callback function, on its own, accepts 4 parameters that are supplied by the reduce method. You may learn more about those parameters here as am only going to focus on the first 2 parameters the reducer accepts:
previousValue: hold the value from the previous reducer call. On first call, it contains the value you set to the initial value parameter of the reduce method or, if you didn't supply an initial value, previousValue shall hold the value of your array's first element (arr[0]).
currentValue: hold the current reduce iteration's item.
Now, let's get back to the task we have, we need to calculate the number of persons who are still in the bus based on a supplied multidimensional array. Each item in that multidimensional array is an array of two values where the result we need at the end is: the sum of the differences between each array, in the multidimensional array, first and second values (sum = multiDim[i][0] - multiDim[i][1] + multiDim[i + 1][0] + multiDim[i + 1][1] etc...).
To solve this task, we'll reduce that multidimensional array into a single number (let's call it result) by using a simple reducer function that will start by an initial value of 0 (as we're calculating a sum in our case) and will add, to the result, the difference between the first and the second values of the array supplied by the reduce at each iteration.
To illustrate, here's a live demo:
/**
* a function that calculates and returns the number of person who are still in the bus or precisely, the sum of the differences between "c[0]" and "c[1]"
* busArray: the supplied multidimensional array to reduce
* the reducer accepts two parameters:
* r: the result from the last call to the reducer function (initially set to 0, the initial value (see second parameter passed to the "reduce" method))
c: hold the current iteration's array.
*/
const calculateWhoAreInTheBus = busArray => busArray.reduce((r, c) => r += c[0] - c[1], 0);
/** test the created "calculateWhoAreInTheBus" function */
console.log(calculateWhoAreInTheBus([
[10, 0],
[3, 5],
[5, 8]
])); // should print: 5
console.log(calculateWhoAreInTheBus([
[3, 0],
[9, 1],
[4, 10],
[12, 2],
[6, 1],
[7, 10]
])); // should print: 17
console.log(calculateWhoAreInTheBus([
[3, 0],
[9, 1],
[4, 8],
[12, 2],
[6, 1],
[7, 8]
])); // should print: 21
console.log(calculateWhoAreInTheBus([
[0, 0],
[0, 0]
])); // should print: 0
I would advice you to use Array.prototype.reduce instead. For example like this:
const reducer = (previous, current) => previous + current[0] - current[1];
const answer = busStops.reduce(reducer, 0);
It is very brief (although this is not a goal in and of itself) and the reducer function does almost trivial work, so it does not complicate unneccesarily. Best of all it encapsulates the functionality with a minimal need of extra variables.
Othwerwise you could simplify your function a bit but use the let keyword to keep variables locked to scope like:
function number(busStops) {
let answer = 0;
for (let bs of busStops) {
answer += bs[0] - bs[1];
}
return answer;
}
I am writing a function that takes an array and an integer number and returns an array of subarrays. The number of subarrays is exact the integer number passed to the function. And the subarrays have to be continuous, meaning the original order of items in the array has to be preserved. Also no subarray can be empty. They have to have at least one item in it. For example:
const array = [2,3,5,4]
const numOfSubarray = 3
const subarrays = getSubarrays(arraym numOfSubarray)
In this case subarrays is this:
[
[[2, 3], [5], [4]],
[[2], [3, 5], [4]],
[[2], [3], [5, 4]],
]
Here is my attempt:
function getSubarrays(array, numOfSubarray) {
const results = []
const recurse = (index, subArrays) => {
if (index === array.length && subArrays.length === numOfSubarray) {
results.push([...subArrays])
return
}
if (index === array.length) return
// 1. push current item to the current subarray
// when the remaining items are more than the remaining sub arrays needed
if (array.length - index - 1 >= numOfSubarray - subArrays.length) {
recurse(
index + 1,
subArrays.slice(0, -1).concat([subArrays.at(-1).concat(array[index])])
)
}
// 2. start a new subarray when the current subarray is not empty
if (subArrays.at(-1).length !== 0)
recurse(index + 1, subArrays.concat([[array[index]]]))
}
recurse(0, [[]], 0)
return results
}
Right now it seems to be working. But I wanted to know what is the time/space complexity of this algorithm. I think it is definitely slower than O(2^n). Is there any way to improve it? Or any other solutions we can use to improve the algorithm here?
You can't get an answer down to anything like 2n, I'm afraid. This grows much faster than that, because the answer has to do with the binomial coefficients, whose definitions have fundamental factorial parts, and whose approximations involve terms like nn.
Your solution seems likely to be worse than necessary, noted because of the exponential number of calls required to solve the simplest case, when numOfSubarrays is 1, and you should just be able to return [array]. But as to full analysis, I'm not certain.
As the first comment shows, the above analysis is dead wrong.
However, if your're interested in another approach, here's how I might do it, based on the same insight others have mentioned, that the way to do this is to find all sets of numOfSubarrays indices of the positions between your values, and then convert them to your final format:
const choose = (n, k) =>
k == 0
? [[]]
: n == 0
? []
: [... choose (n - 1, k), ... choose (n - 1, k - 1). map (xs => [...xs, n])]
const breakAt = (xs) => (ns) =>
[...ns, xs .length] .map ((n, i) => xs .slice (i == 0 ? 0 : ns [i - 1], n))
const subarrays = (xs, n) =>
choose (xs .length - 1, n - 1) .map (breakAt (xs))
console .log (subarrays ([2, 3, 5, 4], 3) // combine for easier demo
.map (xs => xs .map (ys => ys .join ('')) .join('-')) //=> ["23-5-4", "2-35-4", "2-3-54"]
)
console .log (subarrays ([2, 3, 5, 4], 3)) // pure result
.as-console-wrapper {max-height: 100% !important; top: 0}
Here, choose (n, k) finds all the possible ways to choose k elements from the numbers 1, 2, 3, ..., n. So, for instance, choose (4, 2) would yield [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]].
breakAt breaks an array into sub-arrays at a set of indices. So
breakAt ([8, 6, 7, 5, 3, 0, 9]) ([3, 5])
// 3 5 ///=> [[8, 6, 7], [5, 3], [0, 9]]
And subarrays simply combines these, subtracting one from the array length, subtracting one from numOfSubarrays, calling choose with those two values, and then for each result, calling breakAt with the original array and this set of indices.
Even here I haven't tried to analyze the complexity, but since the output is factorial, the process will take a factorial amount of time.
If you want to completely split a list of n elements into k disjunct, continuous sub-lists this is like placing k-1 split points into the n-1 gaps between the elements:
2 | 3 | 5 4
2 | 3 5 | 4
2 3 | 5 | 4
In combinatorics this is taking k-1 from n-1. So I think the result size of the ouput will be n-1 take k-1 = (n-1)! / ((k-1)! * (n-k)!). Thus the complexity is something polynomial like O(n^(k-1)) for constant k. If you don't fix k but raise it with n like k = n/2 the complexity will get exponential.
I don't think that you can improve this, because the output's size is increasing by this complexity.
tl;dr
The number of solutions is bound to (as #gimix mentioned) binomial coefficient, so if I understand correctly it's pessimistically exponential
https://en.wikipedia.org/wiki/Binomial_coefficient#Bounds_and_asymptotic_formulas.
If I'm not mistaken that makes your algorithm this exponential * n (for each element of each solution) * n (because on nearly every step you copy array which length might be dependent on n).
fix second if - only call recurse if subArrays.length < numOfSubarrays
you are copying arrays a lot - slice, concat, spread operator - all of those create new arrays. If for every solution (which length might be depending on n) on every step you copy this solution (which I think is happening here) you multiply the complexity by n.
the space complexity is also exponential * n - you store the exponential number of solutions, possibly of length dependent on n. Using a generator and returning one solution at the time could greatly improve that. As #gimix mentioned the combinations might be the simplest way to do it. Combinations generator in python: https://docs.python.org/3/library/itertools.html#itertools.combinations
Dwelling on complexity:
I think you are right about the slower than exponential complexity, but - bare with me - how much do you know about Fibonacci's sequence? ;)
Let's consider input:
array = [1, 2, ..., n]
numOfSubarrays = 1
We can consider the recursive calls a binary tree with if 1. guarding the left child (first recurse call) and if 2. guarding the right child (second recurse call).
For each recurse call if 1. will be fulfilled - there are more items than sub arrays needed.
Second if will be true only if current sub array has some elements. It's a tricky condition - it fails if, and only if, it succeeded one frame higher - an empty array has been added at the very beginning (except for the root call - it has no parent). Speaking in terms of a tree, it means we are in the right child - the parent must have just added an empty sub array as a current. On the other hand, for the left child parent has just pushed (yet another?) element to the current sub array and we are sure the if 2. will succeed.
Okay, but what does it say about the complexity?
Well, we can count the number of nodes in the tree, multiply by the number of operations they perform - most of them a constant number - and we get the complexity. So how many are there?
I'll keep track of left and right nodes separately on every level. It's gonna be useful soon. For convenience I'll ignore root call (I could treat it as a right node - it has empty sub array - but it messes up the final effect) and start from level 1 - the left child of the root call.
r1 = 0
l1 = 1
As a left node (sub array isn't empty) it has two children:
r2 = 1
l2 = 1
Now, the left node always has two children (1. is always fulfilled; 2. is true because parent pushed element to current sub array) and the right node has only the left child:
r3 = r2 + l2 = 1 + 1 = 2
l3 = r2 = 1
we could continue. The results are:
l
r
1
0
1
1
2
1
3
2
5
3
well... it's oddly familiar, isn't it?
Okay, so apparently, the complexity is O(Σ(Fi + Fi-1) where 1 <= i <= n).
Alright, but what does it really mean?
There is a very cool prove that S(n) - sum of the Fibonacci numbers from 0 to n is equal F(n+2) - 1. It simplifies the complexity to:
O(S(n) + S(n-1)) = O(F(n+2) - 1 + F(n+1) - 1) = O(F(n+3) - 2) = O(F(n+3))
We can forget about the +3 since F(n+3) < 2 * F(n+2) < 4 * F(n+1) < 8 * F(n).
The final question, is Fibonacci sequence exponential? Yes and no apparently.
The is no number that would fulfil the xn = F(n) - the value oscillates between 2 and √2, because for F(n+1) < 2 * F(n) < F(n+2).
It's proven though, that lim(n->∞) F(n+1) / F(n) = φ - the golden ratio. It means the O(F(n)) = O(φn). (Actually, you copy arrays a lot, so it's more like O(φn*n))
How to fix it? You could check if there isn't too many arrays before recursing in if 2.
Other than that, just as #Markus mentioned, depending on the input, the number of solutions might be exponential, so the algorithm to get them also has to be exponential. But that's not true for every input, so let's keep those cases to minimum :D
The problem can be solved by a different approach:
compute all the combinations of numOfSubarray numbers ranging from 1 to the length of array
each combination is a valid slicing of array. For instance, you want to slice the array in your example at positions (1, 2), (1, 3), (2, 3), yielding subarrays [[2],[3],[5,4]], [[2],[3,5],[4]], and [[2,3],[5],[4]]
Time complexity is, I believe, O(r(nCr)) where n is (length of array - 1) and r is (number of subarrays - 1).
To visualize how and why it works have a look at the stars and bars technique
I'm trying to write a function decreasingOrder which takes a positive integer as input and return an array of its digits in decreasing order.
e.g., decreasingOrder(1234) Should give [4,3,2,1].
function decreasingOrder(n) {
let unarr = [...`${n}`].map(i => parseInt(i)); //Unordered Array of Digits
let oarr = []; //Ordered Array of Digits
for(let j=0; j<unarr.length; j++){
let max = Math.max.apply(Math, unarr);
oarr.push(max);
unarr.splice(unarr.indexOf(max), 1); //delete element from array
}
return oarr;
}
console.log(decreasingOrder(1234));
//Expected [4,3,2,1], Instead got [4,3]
I think, deleting element using splice method also reduces the number
of iteration.
I also tried delete operator but get [4, NaN, NaN, NaN] (because Math.max([undefined])).
When I tried with specific number instead of unarr.length in condition expression for for loop, it works fine!
So when I use splice method to delete elements it reduces the unarr.length and when I tried to keep unarr.length constant using delete operator it gives NaN, what should I do? Is there any other way to write to the same function? I'm beginner in JavaScript.
The issue in your code is unarr.splice(unarr.indexOf(max), 1) inside loop.
By taking your example of console.log(decreasingOrder(1234)). In the first cycle the highest number from the array is found and is removed from the array and pushed to new array.
At the end of the first cycle the outputs will be unarr = [1, 2, 3], oarr = [4] and j=1
Likewise after second loop unarr = [1, 2], oarr = [4, 3] and j=2. Now the loop condition j < unarr.length is not satisfied hence the loop breaks. So the output will be [4, 3].
Instead you can use the below utility for your requirement.
function decreasingOrder(n) {
let unarr = [...`${n}`].map(i => parseInt(i)) //Unordered Array of Digits
return unarr.sort((a,b) => b-a)
}
console.log(decreasingOrder(1234))
Hope this helps.
It seems lodash's sortedIndex expects a forward sorted array for its binary search to work. (e.g. [0,1,2,4])
Is there a way to used sortedIndexBy when the array is reverse sorted? (e.g. [4,2,1,0])?
> _.sortedIndex( [0,1,2,4], 3 )
> 3
> _.sortedIndex( [4,2,1,0], 3 )
> 4
To get this to work now, I have to reverse the array, find the sortedIndex, insert the new element, and then un-reverse the array.
Note -- need something which works for sorting strings as well as numbers.
['A','B','D'] into ['D','B','A'] and insert 'C'.
How about _.sortedIndexBy?
Edited: For string comparison, String.prototype.charCodeAt() can help you convert it to Number, then the same logic can be applied.
const arr1 = [0, 1, 2, 4];
const arr2 = [4, 2 ,1, 0];
console.log(_.sortedIndex(arr1, 3 ));
// Similar, but with ranking function.
console.log(_.sortedIndexBy(arr2, 3, function(x) {return -x;}));
const charArr = ['D','B','A'];
// Take the first char and convert to Number
let index = _.sortedIndexBy(charArr, 'C', function(x) {
// Type checks. (If you want it to be general to many types..
if (typeof x === 'string') {
return -x.charCodeAt(0);
} else if (typeof x === 'number') {
return -x;
} // else ... for other types.....
});
console.log('To insert char C, put it to index: ', index);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
or by _.sortedIndex, it also has iteratee to rank before 4.0.0
const arr1 = [0, 1, 2, 4];
const arr2 = [4, 2 ,1, 0];
console.log(_.sortedIndex(arr1, 3));
console.log("Reversed order without ranking func: ",_.sortedIndex(arr2, 3));
// Ranking function to inverse the order.
console.log("Reversed order with ranking func: ",_.sortedIndex(arr2, 3, function(x) {return -x;}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.3.1/lodash.min.js"></script>
Thanks to pilau:
The sortedIndex expects the array is forward sorted, so we can't just put the reverse sorted array and get arr.length - index, and in order to handle vary scenario, I think we need to do either
Reverse array -> get sorted index and put -> reverse it again. or
Get reversed copy by slice and reverse -> get sorted index and calculate by arr.length - index -> inserted to origin array.
To achieve the expected result.
Having learned some Javascript from CodeAcademy, I wanted to try some exercises to test my knowledge.
The exercise was this
Write a JavaScript function which will take an array of numbers stored and find the second lowest and second greatest numbers, respectively. - See more at: http://www.w3resource.com/javascript-exercises/javascript-functions-exercises.php#EDITOR
My function was this
function checker(array) {
narray = array.sort();
console.log(narray);
console.log(narray[1] + "," + narray[array.length - 2]);
}
checker([1, 2, 3, 4, 5]);
Their function was this
function Second_Greatest_Lowest(arr_num)
{
arr_num.sort(function(x,y)
{
return x-y;
});
var uniqa = [arr_num[0]];
var result = [];
for(var j=1; j<arr_num.length; j++)
{
if(arr_num[j-1] !== arr_num[j])
{
uniqa.push(arr_num[j]);
}
}
result.push(uniqa[1],uniqa[uniqa.length-2]);
return result.join(',');
}
alert(Second_Greatest_Lowest([1,2,3,4,5]));
Do you see any reason why the second option would be better?
As noted, their answer wants the 2nd unique number and it is rather inefficient as well.
There are two differences between your solution and theirs:
sorting function
uniqueness
The implicit sorting function is a natural sort. This means that the objects are converted to strings and compared as strings:
[1, 2, 3, 11].sort() // [1, 11, 2, 3]
[1, 2, 3, 11].sort(function(a,b){ return a-b }); // [1, 2, 3, 11]
The second difference, the uniqueness of the numbers. Your solution gives the number on the second position in the array, while theirs gives the number with the second lowest value:
[1,1,2] // Your solution: 1,1
[1,1,2] // Their solution: 2,1
While you can argue that it is not required, that is a matter of definition.
Either way, an improvement could be made in their solution as well to make it more efficient, though not as readable:
function getNumbers(arr) {
if (!arr.length) throw new Error('arr has no items');
arr = arr.slice().sort(function(a,b){ return a-b }); // copy and sort the array
for (var i=0; arr[i] === arr[0]; i++);
if (i === arr.length) { // all numbers are identical
return [arr[0], arr[0]];
}
var a = arr[i]; // second lowest number
i = arr.length-1;
var b = arr[i];
while (arr[i] === b) i--;
return [a, arr[i]];
}
// usage
getNumbers([2,3,1,1,1,6,4,5,6,1,11]) //[2, 6]
As you can see, once you found the number you're interested in, you no longer iterate through the sorted array. Compared to the solution that computes an array of unique numbers, this one is far more efficient, especially for large arrays.
No. Your function is just better. Really.
Reason is that on w3resource.com wants to show you how it should work - it's tutorial. You are just practical programmer.
Both solutions will work also when you put string inside, or negative numbers ..
The text of an exercise is misunderstood. They SHOULD have ask for a second "unique" number from the beginning and from the end. Then:
1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6
Real answers are: 2, 5. Answers from Your script are 1 and 6.
So Your answer is OF COURSE great and OK, but their question is a bit inaccurate.
But a fact that You have note their mistake, and make a better algorithm, sugest that You can skip to another lesson :).