Dynamic Programming: Child running up a staircase - recursion

I'm starting to practice Dynamic Programming and I just can't wrap my head around this question:
Question:
A child is running up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. Implement a method to count how many possible ways the child can run up the stairs.
The solution from the cracking the coding interview book is like this:
"If we thought about all the paths to the nth step, we could just build them off the paths to the three previous steps. We can get up to the nth stop by any of the following:
Going to the (n-1) step and hopping 1 step
Going to the (n-2) step and hopping 2 steps
Going to the (n-3) step and hopping 3 steps"
Therefor to find the solution you just add the number of these path together !
That's what loses me ! Why isn't the answer like this: add number of those paths then add 3 ? Since if you are on step n-1 or n-2 or n-3, there are 3 ways to get the nth step? I understand that if you write down the answers for the first 4 bases cases (assuming that n=0 returns 1) You can see the fibonacci-like pattern. But you may not also see it so it's difficult.
And then they came up with this code:
public static int countWaysDP(int n, int[] map) {
if (n < 0)
return 0;
else if (n == 0)
return 1;
else if (map[n] > -1)
return map[n];
else {
map[n] = countWaysDP(n - 1, map) + countWaysDP(n - 2, map) + countWaysDP(n - 3, map);
return map[n]; }
}
So my second question. How does it return 1 when n == 0. Even if I accept that fact, I still can't figure out a way to solve it if I return 0 when n == 1.
Hope this makes sense.
Thank you

Here is how I wrapped my head around this-
From the book -
On the very last hop, up to the nth step, the child could have
done either a single, double, or triple step hop. That is, the last
move might have been a single step hop from step n-1, a double
step hop from step n-2, or a triple step hop from n-3. The
total number of ways of reaching the last step is therefore the sum of
the number of ways of reaching each of the last three steps
You are correctly contemplating -
Why isn't the answer like this: add number of those paths then add 3 ?
Since if you are on step n-1 or n-2 or n-3, there are 3 ways to get
the nth step?
The problem with such a base case is that it will be applicable only if n >= 3. You clearly will not add 3 if there are only 2 steps.
Let's break down the individual cases and understand what exactly is the base case here.
n=0
There are no stairs to climb.
Total number of ways = 0
n=1
Total number of ways = 1StepHop from (n-1)
Number of ways to do 1StepHop from Step 0(n-1) = 1
Total number of ways = 1
n=2
Total number of ways = 2StepHop from (n-2) + 1StepHop from (n-1)
Number of ways to do 2StepHop to reach Step 2 from Step 0(n-2) = 1
Number of ways to do 1StepHop to reach Step 2 from Step 1(n-1) = 1 (Previous answer for n=1)
Total number of ways = 1 + 1 = 2
n=3
Total number of ways = 3StepHop from (n-3) + 2StepHop from (n-2) + 1StepHop from (n-1)
Number of ways to do 3StepHop to reach Step 3 from Step 0(n-3) = 1
Number of ways to do 2StepHop to reach Step 3 from Step 1(n-2) = 2 (From previous answer for n = 2)
Number of ways to do 1StepHop to reach Step 3 from Step 2 = 1 (From previous answer for n=1)
Total number of ways = 1 + 2 + 1 = 4
Observation -
As you can see from above, we are correctly accounting for the last step in each case. Adding one for each of -> 1StepHop from n-1, 2StepHop from n-2 and 3StepHop from n-3.
Now looking at the code, the case where we return 1 if n==0 is a bit counter-intuitive since we already saw that the answer should be 0 if n==0. -
public static int countWaysDP(int n, int[] map) {
if (n < 0)
return 0;
else if (n == 0)
return 1; <------------- this case is counter-intuitive
else if (map[n] > -1)
return map[n];
else {
map[n] = countWaysDP(n - 1, map) + countWaysDP(n - 2, map) + countWaysDP(n - 3, map);
return map[n];
}
From the observation, you can see that this counter intuitive case of n==0 is actually the one which is accounting for the final step - 1StepHop from n-1, 2StepHop from n-2 and 3StepHop from n-3.
So hitting n==0 case makes sense only during recursion - which will happen only when the initial value of n is greater than 0.
A more complete solution to this problem may have a driver method which handles that case outside of the core recursive algorithm -
int countWays(int n) {
if (n <= 0 ) return 0;
int[] map = new int[n+1];
for(int i = 0; i<n+1; i++){
map[i] = -1;
}
return countWaysDP(n, map);
}
Hope this is helpful.

You can find the solution on
https://github.com/CrispenGari/Triple-Step-Algorithim/blob/master/main.cpp .
int count_Ways(int n){
if(n<0){
return 0;
}else if(n==0){
return 1;
}else{
return count_Ways(n-1) +count_Ways(n-2) + count_Ways(n-3);
}
}
int main(){
cout<<"Enter number of stairs: ";
int n;
cin>>n;
cout<<"There are "<< count_Ways(n)<<" possible ways the child can run up
thestairs."<<endl;
return 0;
}

Related

Dynamic programming to solve the fibwords problem

Problem Statement: The Fibonacci word sequence of bit strings is defined as:
F(0) = 0, F(1) = 1
F(n − 1) + F(n − 2) if n ≥ 2
For example : F(2) = F(1) + F(0) = 10, F(3) = F(2) + F(1) = 101, etc.
Given a bit pattern p and a number n, how often does p occur in F(n)?
Input:
The first line of each test case contains the integer n (0 ≤ n ≤ 100). The second line contains the bit
pattern p. The pattern p is nonempty and has a length of at most 100 000 characters.
Output:
For each test case, display its case number followed by the number of occurrences of the bit pattern p in
F(n). Occurrences may overlap. The number of occurrences will be less than 2^63.
Sample input: 6 10 Sample output: Case 1: 5
I implemented a divide and conquer algorithm to solve this problem, based on the hints that I found on the internet: We can think of the process of going from F(n-1) to F(n) as a string replacement rule: every '1' becomes '10' and '0' becomes '1'. Here is my code:
#include <string>
#include <iostream>
using namespace std;
#define LL long long int
LL count = 0;
string F[40];
void find(LL n, char ch1,char ch2 ){//Find occurences of eiher "11" / "01" / "10" in F[n]
LL n1 = F[n].length();
for (int i = 0;i+1 <n1;++i){
if (F[n].at(i)==ch1&&F[n].at(i+1)==ch2) ++ count;
}
}
void find(char ch, LL n){
LL n1 = F[n].length();
for (int i = 0;i<n1;++i){
if (F[n].at(i)==ch) ++count;
}
}
void solve(string p, LL n){//Recursion
// cout << p << endl;
LL n1 = p.length();
if (n<=1&&n1>=2) return;//return if string pattern p's size is larger than F(n)
//When p's size is reduced to 2 or 1, it's small enough now that we can search for p directly in F(n)
if (n1<=2){
if (n1 == 2){
if (p=="00") return;//Return since there can't be two subsequent '0' in F(n) for any n
else find(n,p.at(0),p.at(1));
return;
}
if (n1 == 1){
if (p=="1") find('1',n);
else find('0',n);
return;
}
}
string p1, p2;//if the last character in p is 1, we can replace it with either '1' or '0'
//p1 stores the substring ending in '1' and p2 stores the substring ending in '0'
for (LL i = 0;i<n1;++i){//We replace every "10" with 1, "1" with 0.
if (p[i]=='1'){
if (p[i+1]=='0'&&(i+1)!= n1){
if (p[i+2]=='0'&&(i+2)!= n1) return;//Return if there are two subsequent '0'
p1.append("1");//Replace "10" with "1"
++i;
}
else {
p1.append("0");//Replace "1" with "0"
}
}
else {
if (p[i+1]=='0'&&(i+1)!= n1){//Return if there are two subsequent '0'
return;
}
p1.append("1");
}
}
solve(p1,n-1);
if (p[n1-1]=='1'){
p2 = p1;
p2.back() = '1';
solve(p2,n-1);
}
}
main(){
F[0] = "0";F[1] = "1";
for (int i = 2;i<38;++i){
F[i].append(F[i-1]);
F[i].append(F[i-2]);
}//precalculate F(0) to F(37)
LL t = 0;//NumofTestcases
int n; string p;
while (cin >> n >> p) {
count = 0;
solve(p,n);
cout << "Case " << ++t << ": " << count << endl;
}
}
The above program works fine, but with small inputs only. When i submitted the above program to codeforces i got an answer wrong because although i shortened the pattern string p and reduces n to n', the size of F[n'] is still very large (n'>=50). How can i modify my code to make it works in this case, or is there another approach (such as dynamic programming?). Many thanks for any advice.
More details about the problem can be found here: https://codeforces.com/group/Ir5CI6f3FD/contest/273369/problem/B
I don't have time now to try to code this up myself, but I have a suggested approach.
First, I should note, that while that hint you used is certainly accurate, I don't see any straightforward way to solve the problem. Perhaps the correct follow-up to that would be simpler than what I'm suggesting.
My approach:
Find the first two ns such that length(F(n)) >= length(pattern). Calculating these is a simple recursion. The important insight is that every subsequent value will start with one of these two values, and will also end with one of them. (This is true for all adjacent values -- for any m > n, F(m) will begin either with F(n) or with F(n - 1). It's not hard to see why.)
Calculate and cache the number of occurrences of the pattern in this these two Fs, but whatever index shifting technique makes sense.
For F(n+1) (and all subsequent values) calculate by adding together
The count for F(n)
The count for F(n - 1)
The count for those spanning both F(n) and F(n - 1). We can achieve that by testing every breakdown of pattern into (nonempty) prefix and suffix values (i.e., splitting at every internal index) and counting those where F(n) ends in prefix and F(n - 1) starts with suffix. But we don't have to have all of F(n) and F(n - 1) to do this. We just need the tail of F(n) and the head of F(n - 1) of the length of the pattern. So we don't need to calculate all of F(n). We just need to know which of those two initial values our current one ends with. But the start is always the predecessor, and the end oscillates between the previous two. It should be easy to keep track.
The time complexity then should be proportional to the product of n and the length of the pattern.
If I find time tomorrow, I'll see if I can code this up. But it won't be in C -- those years were short and long gone.
Collecting the list of prefix/suffix pairs can be done once ahead of time

Confused about prime number checking function

I came across a question on stack overflow about how to check if a number is prime. The answer was the code below. The function int is_prime(int num) returns 1 when the number is prime 0 is returned otherwise.
int is_prime(int num)
{
if (num <= 1) return 0;
if (num % 2 == 0 && num > 2) return 0;
for(int i = 3; i < num / 2; i+= 2)
{
if (num % i == 0)
return 0;
}
return 1;
}
All the logic in the if statements makes sense to me except for the for loop expressions. I don't get why the division i < num / 2 happens and why i+= 2 is being used. Sure one is there to advance the counter and the other is to halt the loop. but why half the number and why increment by two. Any reasonable explanation will be appreciated. Thanks.
Regarding the loop's increment:
The second if (if (num % 2 == 0)) checks if the number is even, and terminates the function if it is. If the function isn't terminated, we know that it's odd, and thus, may only be divisible by other odd numbers. Hence, the loop starts at 3 and checks the number against a series of odd numbers - i.e., increments the potential divisor by 2 on each iteration.
Regarding the loop's stop condition:
The smallest integer larger than 1 is 2. Thus, the largest integer that could ever divide an integer n is n/2. Thus, the loop works it's way up to num/2. If it didn't find a divisor for num by the time it reaches num/2, it has no chance to ever find such a divisor, so it's pointless to keep on going.

I am trying to solve recursion by hand

I am self taught and thought that I understood recursion, but I can not solve this problem:
What is returned by the call recur(12)?
What is returned by the call recur (25)?
public static int recur (int y)
{
if(y <=3)
return y%4;
return recur(y-2) + recur(y-1) + 1;
}
Would someone please help me with understanding how to solve these problems?
First of all, I assume you mean:
public static int recur(int y)
but the results of this method are discovered by placing a print statement at the beginning of the method:
public static int recur(int y)
{
System.out.println(y);
if(y <=3)
return y % 4;
return recur(y-2) + recur(y-1) + 1;
}
I am not sure what you mean by what is returned because there are several returns, though. Anyway, these are the steps to figure this out:
is 12 <= 3? No
recur(10) Don't proceed to the next recursion statement yet
is 10 <= 3? No
recur(8) Don't proceed to the next recursion statement yet
Continue this pattern until y <= 3 is true. Then you return y % 4 (whatever that number may be).
Now you are ready to go to the second recursive statement in the most recent recur() call. So, recur(y - 1).
Is y <= 3? If so, return y % 4. If not, do a process similar to step 1
once you return you add the result of recur(y - 2) + recur(y - 1) + 1. This will be a number of course.
continue this process for many iterations.
Recursion is difficult to follow and understand sometimes even for advanced programmers.
Here is a very common (and similar) problem for you to look into:
Java recursive Fibonacci sequence
I hope this helps!
Good luck!
I have removed the modulus there since any nonegative n less than 4 will just become n so I ended up with:
public static int recur (int y)
{
return y <= 3 ?
y :
recur(y-2) + recur(y-1) + 1;
}
I like to start off by testing the base case so what happens for y when they are 0,1,2,3? well the argument so 0,1,2,3 of course.
What about 4? Well then it's not less or equal to 3 and you get to replace it with recur(4-2) + recur(4-1) + 1 which is recur(2) + recur(3) + 1. Now you can solve each of the recur ones since we earlier established became its argument so you end up with 2 + 3 + 1.
Now doing this for 12 or 25 is exactly the same just with more steps. Here is 5 which has just one more step:
recur(5); //=>
recur(3) + recur(4) + 1; //==>
recur(3) + ( recur(2) + recur(3) + 1 ) + 1; //==>
3 + 2 + 3 + 1 + 1; // ==>
10
So in reality the recursion halts the process in the current iteration until you have an answer that the current iteration adds together so I could have done this in the opposite way, but then I would have paused every time I now have used a previous calculated value.
You should have enough info to do any y.
This is nothing more than an augmented Fibonacci sequence.
The first four terms are defined as 0, 1, 2, 3. Thereafter, each term is the sum of the previous two terms, plus one. This +1 augmentation is where it differs from the classic Fibonacci sequence. Just add up the series by hand:
0
1
2
3
3+2+1 = 6
6+3+1 = 10
10+6+1 = 17
17+10+1 = 28
...

Sum of combinations of numbers

I want to solve a mathematical problem in a fastest possible way.
I have a set of natural numbers between 1 to n, for example {1,2,3,4,n=5} and I want to calculate a formula like this:
s = 1*2*3*4+1*2*3*5+1*2*4*5+1*3*4*5+2*3*4*5
as you can see, each element in the sum is a multiplications of n-1 numbers in the set. For example in (1*2*3*4), 5 is excluded and in (1*2*3*5), 4 is excluded. I know some of the multiplications are repeated, for example (1*2) is repeated in 3 of the multiplications. How can I solve this problem with least number of multiplications.
Sorry for bad English.
Thanks.
Here is a way that does not "cheat" by replacing multiplication with repeated addition or by using division. The idea is to replace your expression with
1*2*3*4 + 5*(1*2*3 + 4*(1*2 + 3*(1 + 2)))
This used 9 multiplications for the numbers 1 through 5. In general I think the multiplication count would be one less than the (n-1)th triangular number, n * (n - 1) / 2 - 1. Here is Python code that stores intermediate factorial values to reduce the number of multiplications to just 6, or in general 2 * n - 4, and the addition count to the same (but half of them are just adding 1):
def f(n):
fact = 1
term = 2
sum = 3
for j in range(2, n):
fact *= j
term = (j + 1) * sum
sum = fact + term
return sum
The only way to find which algorithm is the fastest is to code all of them in one language, and run each using a timer.
The following would be the most straightforward answer.
def f(n):
result = 0
nList = [i+1 for i in range(n)]
for i in range(len(nList)):
result += reduce(lambda x, y: x*y,(nList[:i]+nList[i+1:]))
return result
Walkthrough - use the reduce function to multiply all list's of length n-1 and add to the variable result.
If you just want to minimise the number of multiplications, you can replace all the multiplications by additions, like this:
// Compute 1*2*…*n
mult_all(n):
if n = 1
return 1
res = 0
// by adding 1*2*…*(n-1) an entirety of n times
for i = 1 to n do
res += mult_all(n-1)
return res
// Compute sum of 1*2*…*(i-1)*(i+1)*…*n
sum_of_mult_all_but_one(n):
if n = 1
return 0
// by computing 1*2*…*(n-1) + (sum 1*2*…*(i-1)*(i+1)*…*(n-1))*n
res = mult_all(n-1)
for i = 1 to n do
res += sum_of_mult_all_but_one(n-1)
return res
Here is an answer that would work with javascript. It is not the fastest way because it is not optimized, but it should work if you want to just find the answer.
function combo(n){
var mult = 1;
var sum = 0;
for (var i = 1; i <= n; i++){
mult = 1;
for (var j = 1; j<= n; j++){
if(j != i){
mult = mult*j;
}
}
sum += mult;
}
return (sum);
}
alert(combo(n));

Determining the big Oh for (n-1)+(n-1)

I have been trying to get my head around this perticular complexity computation but everything i read about this type of complexity says to me that it is of type big O(2^n) but if i add a counter to the code and check how many times it iterates per given n it seems to follow the curve of 4^n instead. Maybe i just misunderstood as i placed an count++; inside the scope.
Is this not of type big O(2^n)?
public int test(int n)
{
if (n == 0)
return 0;
else
return test(n-1) + test(n-1);
}
I would appreciate any hints or explanation on this! I completely new to this complexity calculation and this one has thrown me off the track.
//Regards
int test(int n)
{
printf("%d\n", n);
if (n == 0) {
return 0;
}
else {
return test(n - 1) + test(n - 1);
}
}
With a printout at the top of the function, running test(8) and counting the number of times each n is printed yields this output, which clearly shows 2n growth.
$ ./test | sort | uniq -c
256 0
128 1
64 2
32 3
16 4
8 5
4 6
2 7
1 8
(uniq -c counts the number of times each line occurs. 0 is printed 256 times, 1 128 times, etc.)
Perhaps you mean you got a result of O(2n+1), rather than O(4n)? If you add up all of these numbers you'll get 511, which for n=8 is 2n+1-1.
If that's what you meant, then that's fine. O(2n+1) = O(2⋅2n) = O(2n)
First off: the 'else' statement is obsolete since the if already returns if it evaluates to true.
On topic: every iteration forks 2 different iterations, which fork 2 iterations themselves, etc. etc. As such, for n=1 the function is called 2 times, plus the originating call. For n=2 it is called 4+1 times, then 8+1, then 16+1 etc. The complexity is therefore clearly 2^n, since the constant is cancelled out by the exponential.
I suspect your counter wasn't properly reset between calls.
Let x(n) be a number of total calls of test.
x(0) = 1
x(n) = 2 * x(n - 1) = 2 * 2 * x(n-2) = 2 * 2 * ... * 2
There is total of n twos - hence 2^n calls.
The complexity T(n) of this function can be easily shown to equal c + 2*T(n-1). The recurrence given by
T(0) = 0
T(n) = c + 2*T(n-1)
Has as its solution c*(2^n - 1), or something like that. It's O(2^n).
Now, if you take the input size of your function to be m = lg n, as might be acceptable in this scenario (the number of bits to represent n, the true input size) then this is, in fact, an O(m^4) algorithm... since O(n^2) = O(m^4).

Resources