How Do I Solve This Complex Javascript Algorithm Without Overcomplicating it? - javascript

How many ways can you make the sum of a number?
From wikipedia: https://en.wikipedia.org/wiki/Partition_(number_theory)#
In number theory and combinatorics, a partition of a positive integer n, also called an integer partition, is a way of writing n as a sum of positive integers. Two sums that differ only in the order of their summands are considered the same partition. If order matters, the sum becomes a composition. For example, 4 can be partitioned in five distinct ways:
4
3 + 1
2 + 2
2 + 1 + 1
1 + 1 + 1 + 1
Examples
Basic
sum(1) // 1
sum(2) // 2 -> 1+1 , 2
sum(3) // 3 -> 1+1+1, 1+2, 3
sum(4) // 5 -> 1+1+1+1, 1+1+2, 1+3, 2+2, 4
sum(5) // 7 -> 1+1+1+1+1, 1+1+1+2, 1+1+3, 1+2+2, 1+4, 5, 2+3
sum(10) // 42
Explosive
sum(50) // 204226
sum(80) // 15796476
sum(100) // 190569292
My Attempt
I tried to loop through two arrays simultaneously and test them against eachother. This doesn't work (at least in the way I did it) for a few reasons.
My Code:
function sum(num, arr = []) {
if(num == 0){
return testNumbers(arr, num);
}
arr.push(num);
return sum(num - 1, arr);
function testNumbers(arrr, n){
let arr2 = [...arrr];
let count = 0;
let calculations = arrr.filter((item)=>{
return item + arr2.map((a)=>{
return a;
}) == n;
})
console.log(calculations);
}
}
console.log(sum(10));
You don't need to fix my code, as I don't think its salvageable, but how do you solve the problem?

This is in fact a fairly simple recursion, if we think of the problem as counting the partitions with a given lower bound. We have two simple bases cases of 0 and 1 if the lower bound is greater than our target number or equal to it. Otherwise we recur in two ways, one for when we use that lower bound, and one for when we don't. Here's one version, where lb is the lower bound of the numbers to use:
const count = (n, lb = 1) =>
lb > n
? 0
: lb == n
? 1
: count (n - lb, lb) + count (n, lb + 1)
This counts the number of partitions with the given lower bound. So count(10, 3) would yield 5, one for each array in [[3, 3, 4], [3, 7], [4, 6], [5, 5], [10]]. Although the default value for the lower bound means that we can call it with just our target number, there are potential issues with this if we tried, say, to map over it. So it might be best to wrap this in a separate function, const countPartitions = (n) => count (n, 1)
const count = (n, lb) =>
lb > n
? 0
: lb == n
? 1
: count (n - lb, lb) + count (n, lb + 1)
const countPartitions = (n) => count (n, 1)
console .log ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .map (countPartitions))
But this will be quite slow for larger input. My test for 100 took 56.3 seconds.) If we memoize the intermediate results, we should speed things up a great deal. We can do this manually or, as I'd prefer, with a memoization helper:
const memoize = (makeKey, fn) => {
const memo = {}
return (...args) => {
const key = makeKey (...args)
return memo [key] || (memo [key] = fn (...args))
}
}
const count = memoize (
(n, lb) => `${n}~${lb}`,
(n, lb) =>
lb > n
? 0
: lb == n
? 1
: count (n - lb, lb) + count (n, lb + 1)
)
const countPartitions = (n) => count (n, 1)
console .log (countPartitions (100))
And this now takes 20 milliseconds in my test.
Update: answering comment
A comment asked
Hey Scott, sorry to bring up an old post, but I've been going over your solution here and I'm afraid I'm having trouble understanding exactly how it works. If you don't mind, could you go a little more in-depth on why counting instances of n===lb leads to the answer? Maybe my math is just weak, but I'm not following the partitions logic.
Let's imagine we're trying to partition 10, and we've already counted those whose lowest value is 1 and those whose lowest value is 2, and now we're trying to count the partitions where the lowest value is at least 3.
We call count (10, 3). Since 3 > 10 is false, we don't return 0. And since 3 == 10 is false, we don't return 1. Instead we make two recursive calls and add their results together. The first one is where we choose to use 3 in our output, choose (7, 3), since we will have seven remaining when we've selected the first 3. The second one is where we choose not to use 3 in our output, choose (10, 4), since the lowest bound will be 4 if we are to skip 3, but we still have ten to partition.
The call structure would look like this:
(10, 3)
___________________________________/\____________________
/ \
(7, 3) + (10, 4)
___________/\___________ __________/\_________
/ \ / \
(4, 3) + (7, 4) (6, 4) + (10, 5)
____/\___ ________/\_______ ____/\____ _____/\_______
/ \ / \ / \ / \
(1, 3) + (4, 4) (3, 4) + (7, 5) (2, 4) + (6, 5) (5, 5) + (10, 6)
| | | ___/\___ | ___/\___ | _____/\_____
| | | / \ | / \ | / \
| | | (2, 5) + (7, 6) | (1, 5) + (6, 6) | (4, 6) + (10, 7)
| | | | __/\__ | | | | | ____/\____
| | | | / \ | | | | | / \
| | | | (1, 6) + (7, 7) | | | | | (3, 7) + (10, 8)
| | | | | | | | | | | | ___/\____
| | | | | | | | | | | | / \
| | | | | | | | | | | | (2, 8) + (10, 9)
| | | | | | | | | | | | | ___/\___
| | | | | | | | | | | | | / \
| | | | | | | | | | | | | (1, 9) + (10, 10)
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
(3>1) (4=4) (4>3) (5>2) (6>1) (7=7) (4>2) (5>1) (6=6)(5=5) (6>4) (7>3) (8>2) (9>1) (10=10)
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 1
| | | | |
| | | | |
[[3, 3, 4], [3, 7], [4, 6],[5, 5], [10]]

We can get a speed up over Scott's solution by using the recurrence relation for the partition function that uses pentagonal numbers:
function _p(k, memo){
if (k == 0)
return 1;
if (memo[k])
return memo[k];
let result = 0;
let i = 1;
const ms = [1, 1, -1, -1];
while (true){
const n = i & 1 ? (i + 1) / 2 : -i / 2;
const pentagonal = (3*n*n - n) / 2;
if (pentagonal > k)
break;
result = result + ms[(i-1) % 4] * _p(k - pentagonal, memo);
i = i + 1;
}
return memo[k] = result;
}
function p(k){
return _p(k, {});
}
var ks = [1, 2, 3, 4, 5, 6, 10, 50, 80, 100];
for (let k of ks){
const start = new Date;
console.log(`${ k }: ${ p(k) }`);
console.log(`${ new Date - start } ms`);
console.log('');
}

Related

How does recursion work in a Countdown function

I'm learning a bit of JavaScript, but I'm having hard time understanding the lesson on FreeCodeCamp about the recursion countdown (link).
In the lesson, there this initial example. But I'm confused on how it operates:
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
return countArray;
}
}
console.log(countup(5));
I see the steps that this function performs (in order), as something like this:
n (5 in the beginning) is greater than 1, so go to the else statement.
in the else statement I'll have a constant (countArray) assigned to do countup(n - 1), which
is 4.
the next operation js encounters is countArray.push(n), so it will push 4 into an array.
then it encounters return countArray, which is 4.
the function continues with 3,2,1 till the base condition reaches n==0, and in that case the
function ends returning me an empty array.
I know this is not how it works, I'm simply "describing" how my noob mind works looking at this recursive function.
I looked at the solution of the exercise and other people's explanations, but I cannot understand why the function does not work like I described, and also why after reaching n==0, it starts to fill up an array counting up from 1 to 5.
I would like to understand this.
Realise that every execution of countup will have its own n and countArray variables.
It may help to visualise it. Each execution context is visualised as a "box". The variables in the outer boxes will still be there, when a function call returns.
The outermost box is the execution context that is created by the initial call: countup(5):
// n is 5 and does not change
const countArray = countup(n - 1);
+-------------------------------------------------------------------------------+
| // n is 4 and does not change |
| const countArray = countup(n - 1); |
| +-------------------------------------------------------------------------+ |
| | // n is 3 and does not change | |
| | const countArray = countup(n - 1); | |
| | +-------------------------------------------------------------------+ | |
| | | // n is 2 and does not change | | |
| | | const countArray = countup(n - 1); | | |
| | | +-------------------------------------------------------------+ | | |
| | | | // n is 1 and does not change | | | |
| | | | const countArray = countup(n - 1); | | | |
| | | | +-------------------------------------------------------+ | | | |
| | | | | // n is 0 and does not change | | | | |
| | | | | return []; // the if-block is executed because n < 1 | | | | |
| | | | +-------------------------------------------------------+ | | | |
| | | | // countArray is [] | | | |
| | | | countArray.push(n); // n is still 1 | | | |
| | | | return countArray; // returns [1] | | | |
| | | +-------------------------------------------------------------+ | | |
| | | // countArray is [1] | | |
| | | countArray.push(n); // n is still 2 | | |
| | | return countArray; // returns [1, 2] | | |
| | +-------------------------------------------------------------------+ | |
| | // countArray is [1, 2] | |
| | countArray.push(n); // n is still 3 | |
| | return countArray; // returns [1, 2, 3] | |
| +-------------------------------------------------------------------------+ |
| // countArray is [1, 2, 3] |
| countArray.push(n); // n is still 4 |
| return countArray; // returns [1, 2, 3, 4] |
+-------------------------------------------------------------------------------+
// countArray is [1, 2, 3, 4]
countArray.push(n); // n is still 5
return countArray; // returns [1, 2, 3, 4, 5]
You can just add some logging to visualize what is happening:
function countup(n) {
if (n < 1) {
console.log('n = %d, returning empty array', n);
return [];
} else {
console.log('n = %d, calling countup(%d - 1)', n, n);
const countArray = countup(n - 1);
console.log('n = %d, countArray is %s', n, JSON.stringify(countArray))
console.log('n = %d, pushing n onto array', n);
countArray.push(n);
console.log('n = %d, returning %s', n, JSON.stringify(countArray));
return countArray;
}
}
console.log(countup(5));
Here what the array looks like inside of each function call if this helps:
Array: 1 N: 1
Array: 1,2 N: 2
Array: 1,2,3 N: 3
Array: 1,2,3,4 N: 4
Array: 1,2,3,4,5 N: 5
If you put a console log in here like this, you can visualize it better.
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
console.log(`countup(${n}) returns ${countArray}`);
return countArray;
}
}
console.log(countup(5));
What happens is that as soon as you hit that first invocation of countup, execution moves there. So, invoke it with 5 and you see the first output statement is run with a 1, because several more times it basically called itself.
countup (5) calls countup (4) calls countup(3) calls countup (2) calls countup (1) --
countup (1) returns 1, back to the caller of 2 which returns 2, 1, back to the caller of 3 returns 3, 2, 1...and so on back up to 5.

how to concatenate bits in javascript?

I want to concatenate the bits of two number values like this:
+---------+---------+---------+-----------------+
| | val1 | val2 | concatenate val |
+---------+---------+---------+-----------------+
| decimal | 18 | 16 | 592 |
| hexa | 0x12 | 0x10 | 0x250 |
| binary | 0b10010 | 0b10000 | 0b1001010000 |
+---------+---------+---------+-----------------+
I have try to just concatenate with + like that :
const test = 0b10010, test1 = 0b10000
console.log(test+test1)//return 34
It does not concatenate the values but adds them together.
You could shift the first value by the bitwise length of the second value before adding.
const
add = (a, b) => (a << Math.ceil(Math.log2(b)) + 1) + b;
test = 0b10010,
test1 = 0b10000,
console.log(add(test, test1)); //

Permutation tree not making sense with the code

I watched a youtube video where this basic permutation tree was shown. If you look at this bit of code:
function recursion(input, set = [], result = []) {
if (!input.length) {
result.push([...set].join(''));
}
for (let i = 0; i < input.length; i++) {
const newArr = input.filter((n, index) => index !== i);
set.push(input[i]);
recursion(newArr, set, result);
set.pop();
}
return result.join(', ');
}
you can see that the base case (if statement) is at the top before the parameter nums is filtered. So, my whole question is how the tree and the code makes sense because for me the code would remove one too many digits from the set array. Becuase it pops an item of when returning and doesn't it return more than two times?
Does this log add clarity?
/ entering recursion with input = [1,2,3], set = [], result = []
| looping, i = 0
| adding 1 to set
| / entering recursion with input = [2,3], set = [1], result = []
| | looping, i = 0
| | adding 2 to set
| | / entering recursion with input = [3], set = [1,2], result = []
| | | looping, i = 0
| | | adding 3 to set
| | | / entering recursion with input = [], set = [1,2,3], result = []
| | | | adding 123 to result
| | | \ returning [123]
| | | removing 3 from set
| | \ returning [123]
| | removing 2 from set
| | looping, i = 1
| | adding 3 to set
| | / entering recursion with input = [2], set = [1,3], result = [123]
| | | looping, i = 0
| | | adding 2 to set
| | | / entering recursion with input = [], set = [1,3,2], result = [123]
| | | | adding 132 to result
| | | \ returning [123,132]
| | | removing 2 from set
| | \ returning [123,132]
| | removing 3 from set
| \ returning [123,132]
| removing 1 from set
| looping, i = 1
| adding 2 to set
| / entering recursion with input = [1,3], set = [2], result = [123,132]
| | looping, i = 0
| | adding 1 to set
| | / entering recursion with input = [3], set = [2,1], result = [123,132]
| | | looping, i = 0
| | | adding 3 to set
| | | / entering recursion with input = [], set = [2,1,3], result = [123,132]
| | | | adding 213 to result
| | | \ returning [123,132,213]
| | | removing 3 from set
| | \ returning [123,132,213]
| | removing 1 from set
| | looping, i = 1
| | adding 3 to set
| | / entering recursion with input = [1], set = [2,3], result = [123,132,213]
| | | looping, i = 0
| | | adding 1 to set
| | | / entering recursion with input = [], set = [2,3,1], result = [123,132,213]
| | | | adding 231 to result
| | | \ returning [123,132,213,231]
| | | removing 1 from set
| | \ returning [123,132,213,231]
| | removing 3 from set
| \ returning [123,132,213,231]
| removing 2 from set
| looping, i = 2
| adding 3 to set
| / entering recursion with input = [1,2], set = [3], result = [123,132,213,231]
| | looping, i = 0
| | adding 1 to set
| | / entering recursion with input = [2], set = [3,1], result = [123,132,213,231]
| | | looping, i = 0
| | | adding 2 to set
| | | / entering recursion with input = [], set = [3,1,2], result = [123,132,213,231]
| | | | adding 312 to result
| | | \ returning [123,132,213,231,312]
| | | removing 2 from set
| | \ returning [123,132,213,231,312]
| | removing 1 from set
| | looping, i = 1
| | adding 2 to set
| | / entering recursion with input = [1], set = [3,2], result = [123,132,213,231,312]
| | | looping, i = 0
| | | adding 1 to set
| | | / entering recursion with input = [], set = [3,2,1], result = [123,132,213,231,312]
| | | | adding 321 to result
| | | \ returning [123,132,213,231,312,321]
| | | removing 1 from set
| | \ returning [123,132,213,231,312,321]
| | removing 2 from set
| \ returning [123,132,213,231,312,321]
| removing 3 from set
\ returning [123,132,213,231,312,321]
You can see how I added the logging to your code in this snippet:
const log = (depth, message) =>
console .log ('| '.repeat (depth) + message)
function recursion(input, set = [], result = [], depth = 0) {
log (depth, `/ entering recursion with input = [${input}], set = [${set}], result = [${result}]`)
if (!input.length) {
log (depth, `| adding ${[...set].join('')} to result`)
result.push([...set].join(''));
}
for (let i = 0; i < input.length; i++) {
log (depth, `| looping, i = ${i}`)
const newArr = input.filter((n, index) => index !== i);
log (depth, `| adding ${input[i]} to set` )
set.push(input[i]);
recursion(newArr, set, result, depth + 1);
log (depth, `| removing ${input[i]} from set` )
set.pop();
}
log (depth, `\\ returning [${result}]`)
return result.join(', ');
}
console .log (recursion([1, 2, 3]))
.as-console-wrapper {min-height: 100% !important; top: 0}
(but the console output there is limited to the last 50 lines.)

Strange behaviour of Array.reduce() with negation and sum

Data:
arr = [0, 0, 0, 1, 1]
Code:
1
!arr[0] + !arr[1] + !arr[2] + !arr[3] + !arr[4]
// 3, correct!
but ...
2
arr.reduce((a, b) => (!a + !b));
// 1, bullshit?!
Question:
Why is 1. and 2. not the same? Makes no sense to me? How can I reduce() my array to give me the same as in 1.
Update:
arr = [{test: 0}, {test: 0}, {test: 0}, {test: 1}, {test: 1}]
try1: (why doesnt this work?)
arr.reduce((a, b) => a.test + !b.test, {test: 0});
// NaN
arr.reduce((a, b) => a.test + !b.test, 0);
//NaN
You are negating the previous sum on each iteration so it will be treated as 1(true) or 0(false).
It's working like:
+-------------+-------------+--------------+-----------------------------+
| callback | accumulator | currentValue | return value |
+-------------+-------------+--------------+-----------------------------+
| first call | 0 | 0 | !0 + !0 = true + true = 2 |
| second call | 2 | 0 | !2 + !0 = false + true = 1 |
| third call | 1 | 1 | !1 + !1 = false + false = 0 |
| fourth call | 0 | 1 | !0 + !1 = true + false = 1 |
+-------------+-------------+--------------+-----------------------------+
So set an initial value with Array#reduce method and negate the next value and add with the previous sum.
arr.reduce((a, b) => a + !b, 0);
const arr = [0, 0, 0, 1, 1]
console.log(arr.reduce((a, b) => a + !b, 0));
Right now it would work like :
+-------------+-------------+--------------+------------------------+
| callback | accumulator | currentValue | return value |
+-------------+-------------+--------------+------------------------+
| first call | 0 | 0 | 0 + !0 = 0 + true = 1 |
| second call | 1 | 0 | 1 + !0 = 1 + true = 2 |
| third call | 2 | 0 | 2 + !0 = 2 + true = 3 |
| fourth call | 3 | 1 | 3 + !1 = 3 + false = 3 |
| fifth call | 3 | 1 | 3 + !1 = 3 + flase = 3 |
+-------------+-------------+--------------+------------------------+
UPDATE : With a nested object it would be like,
arr.reduce((a, b) => a + !b.test, 0);

Calculate Number from a loop increment number

+---+---+---+
| 1 | 0 | 0 |
+---+---+---+
| 2 | 1 | 0 |
+---+---+---+
| 3 | 2 | 0 |
+---+---+---+
| 4 | 0 | 1 |
+---+---+---+
| 5 | 1 | 1 |
+---+---+---+
| 6 | 2 | 1 |
+---+---+---+
| 7 | 0 | 2 |
+---+---+---+
| 8 | 1 | 2 |
+---+---+---+
| 9 | 2 | 2 |
+---+---+---+
The code I am trying
var loop = 1;
while(loop < 10) {
console.log(loop, loop%3, "I can't calculate this")
loop++;
}
I have a loop increment variable loop and it counting 1,2,3,4,5....
I need to calculate 2 number from incremental variable: one is rounding 0,1,2 (loop % 3) I can do this but other 0,0,0 or 1,1,1
I mean I need to hold a number according to my round number.
Fixed your code ((loop-1)%3) and you just need a division with rounding down:
var loop = 1;
while(loop < 10) {
console.log(loop, (loop-1)%3, Math.floor((loop-1)/3))
loop++;
}
Some other languages support "integer division", where the Math.floor thing would not be necessary.
Use bellow code snippet:
var loop = 1;
var round = 3;
while (loop < 10) {
console.log(loop, (loop - 1) % round, Math.floor((loop - 1) / round));
loop++;
}
So easy
Actually you have an need for getting a number in a different decimal system, here, you want to get a number to base of three with the slight complication of a one based starting value, where the number usually starts at zero and a reversed result by taking the lower indicators at left and the higer indicator at right.
Now you get first the straight conversion.
var i;
for (i = 0; i < 9; i++) {
console.log(i, i.toString(3).padStart(2, '0'));
}
And now the shifted result by adding one to the output and by reversing the result.
var i;
for (i = 0; i < 9; i++) {
console.log(i + 1, Array.from(i.toString(3).padStart(2, '0')).reverse().join(''));
}

Categories