I'm doing this function " Pentabonaci " which is almost the same as fibonaci but instead of calling n=f(n-1)+f(n-2)
it does the same until 5...( n=f(n-1)+f(n-2)+...+ f(n-5)
) then counts the number of odd numbers in the sequence and returns this value. The function works .. the problem is that it is very slow... for ~45 of the sequence the console hangs for me... and my function must work for quite large numbers...
Note: The objective of the function is not to calculate the numbers of the sequence, it is the amount of odd numbers of the sequence...
function countOddPentaFib(n) {
let arr = []
for (let i = 1; i <= n; i++) {
arr.push(calcPentaFib(i))
}
return arr.filter(n => n % 2)
}
function calcPentaFib(n) {
if (n == 0) return 0;
else if (n == 1 || n == 2) return 1;
else if (n == 3) return 2;
else if (n == 4) return 4;
else {
return calcPentaFib(n - 1) + calcPentaFib(n - 2) + calcPentaFib(n - 3) + calcPentaFib(n - 4) + calcPentaFib(n - 5)
}
}
console.log(countOddPentaFib(30));
We are going to use Memoization to implement a solution. This will require creating a structure to store the pre-calculated data and thus optimize the response time.
RECURSION: "to understand recursion you must first understand recursion" .
One of the characteristics of recursion (or recursion) is that it allows you to write clean code and also allows you to execute complex tasks that would otherwise become quite tedious.
However, recursion can become very costly in resources if the problem grows in complexity and steps are not taken to optimize the process.
JavaScript (ECMAScript 6) supports queue optimization for recursion, however not all (almost any) JS engines in different browsers (including NodeJS) support this feature.
Therefore, we should leave the use of optimized recursion to languages that do support it. (At least for now
22-10-2019
).You can read a bit more about ES6 and queue optimization for recursion, in this post.
MEMOIZATION, or was it memorization... I don't remember
The memoization technique (not to be confused with memorization ) is an optimization technique used to speed up processes when you have heavy function calls . There are several areas in which it is used, not only in recursion.
To solve the problem we will use this technique since it seems to be the right one, although there could be another even more efficient one.
What we will do is create a cache of results stored in a list. This way we don't need to use recursion, and the process becomes very intuitive.
The problem definition asks to calculate a series called Penta-Fibonacci, that is, applying the Fibonacci series but up to
n-5
.Recall that the value of each element of the Fibonacci series is calculated as follows:
therefore our Penta-Fibonacci series is defined as:
The fibonacci series starts with the values 0 and 1 and the rest is calculated from these two numbers. Being:
With this we already have the values that will serve as the basis for calculating the rest of our Penta-Fibonacci series.
We need to work with a row or queue , to always store the last 5 elements of the series to calculate the next one. Javascript does not have an object of type row, but instead has an object of type
Array
, which has two methods that will help us work as if it were a row. These arepush()
, which allows us to add elements to the end of the array, andshift()
which allows us to remove the first element from the array and reduces the size of the array.the code please
We are going to implement this solution without using recursion, we will simply use a loop
for
.Let's take a good look at what's going on here.
cachedValues
, it contains the first five elements of our series.array
result.With this you have applied Memoization, in a non-recursive algorithm.
EDITION
Since the CodeWars exercise (kata) that this question comes from asks for the number of odds that exist in the string, then other logic could be used to find the result.
Thanks to the contribution of @gbianchi , the idea proposed by him is to use a deterministic method to know if given a value
n
the result off(n)
will be even or odd.If we analyze the first 17 elements of the series we have the following.
We see a pattern that repeats every 6 elements starting with
n=1
. Therefore, the way to count these odd elements is given by calculating the times that 6 fits inn
and the rest of dividing n / 6.For every time 6 fits entirely in
n
, 2 odds must be added to the count.And if the division of n/6 is inexact, an odd must be added to the count according to the following:
Finally, we must take into account that the value 1 is repeated and therefore we must eliminate it from the account.
Let's see the solution:
I hope this helps you solve the problem.
To calculate the value for n = 7 you have to calculate the value of the previous 5, so you make 5 recursive calls, but each of these calls will make another 5, so for the value of n=30 you will have an incredible amount of recursive calls that will kill the stack. Many of these calls will have been made before, so the ideal would be to reuse them.
A simple solution is to use a structure that saves the previous results to prevent the recursion from getting out of hand:
Once you calculate PentaFib of N, you save it. As you will have had to calculate PentaFb(N-1), PentaFb(N-2), ... PentaFb(N-5), you will have all those values already saved, so calculating PentaFib(N+1) is accessing the 5 previously saved values and add them. This is what is called memorization
Since we only need to know how many odd elements there are, we can further simplify the calculation:
We will save 0 if the number is even and 1 if it is odd. Thus we have the initial values:
become
And every time we calculate the next value we will have the following:
And with our new f prime function :
So in the end we will only have to count the number of 1 in the array:
Mauricio's solution is totally correct.
Based on the data determination and the recurring pattern, the following can be done:
The code is in c#, but it is translatable to which language...
Mauricio used a mathematical function, even more beautiful. However, using only "trivial" code can be solved as well.
There is an issue if the input value is even or odd, and that is why the while takes one more value than there is to count. I couldn't find where the problem is in those edges, but I think Mauricio's solution applies.