Intuition behind the base case for counting the number of steps - c++

I coded the following solution with some online help. The aim is basically to find the number of ways in which a person can climb a ladder of n steps, if at each step he can climb 1 or 2 steps.
class Solution {
public:
int climbStairs(int n) {
if(n<0)
return 0;
//what is the logic used for the following return?
if(n==0)
return 1;
return climbStairs(n-1)+climbStairs(n-2);
}
};
While I am more or less aware about what exactly I am doing, I fail to understand the intuition behind returning 1 if the number of steps to be climbed is 0. Since we can take strides of length either 1 or 2, if the total number of steps to be climbed is 0, then shouldn't we return just a 0 (since no steps of length 1 or 2 can be taken)? Unfortunately, I don't get the correct answer if I return 0.
Could someone please explain what exactly is going on and the intuition behind returning 1 (instead of a 0)?

Instead of thinking about it in terms of how many different ways you can take steps to the top, think of it in terms of how many ways you can get to the top when you have n steps left to climb. If n == 0 you have one way to get to the top: stay where you are. That's the intuition.
The practical reason is that without that definition for n == 0, you'd need two more base cases, for n == 1 and n == 2 to get the right answer for all n > 0. And then you would be free to puzzle over what the right answer should be for n == 0.
Per request, here's why you'd need additional base cases if climbStairs(0) was 0. (Well, either you need additional base cases or you need to alter your recursion formula.) Whenever n is not a base case, climbStairs(n) is defined in terms of climbStairs(n-1) and climbStairs(n-2). If you define the n == 0 case as being 0, then, as you noticed, you don't get the right answer for n == 1 or n == 2. Therefore you'd have to define those as additional base cases. (Just fixing the n == 1 still wouldn't give the right answer for n == 2.) Once those additional base cases are established, the recursion formula will continue to give the correct answer for all n > 2.

There's one way to climb no stairs: do nothing. Or you can think of it mathematically: an empty sum is 0.
If you feel uncomfortable with that intuition, you can rephrase your code to eliminate the zero case:
int climbStairs(int n) {
if (n == 1) return 1; // 1
if (n == 2) return 2; // 1+1 or 2
return climbStairs(n-1) + climbStairs(n-2);
}
Incidental to your actual question, but this function is the Fibonacci sequence, and there are better ways (eg: linear or log time rather than exponential) to compute it.

When a person climbs the stairs, Let's say he goes from point A to point B by climbing n stairs. Now as one can easily understand there's some an vertical distance and bn horizontal distance between A and B. Where a and b are dimensions of each stair.
'Could someone please explain what exactly is going on and the intuition behind returning 1 (instead of a 0)?'
Now when there is 0 stairs there is no vertical distance between A and B. But there is 'b' horizontal distance that has to be covered and which will require 1 step.

If we returned 0 when n == 0 and when n < 0, then both our base cases would return 0 and we would always get 0 as our final answer, since return climbStairs(n-1)+climbStairs(n-2); would always perform 0+0. Don't worry about how a ladder with zero steps can even be "climbed" at all - it just has to do with making the program give the correct answer for higher cases.

Related

Stroustrup's C++ Book Challenge, Can someone help me understand this code?

I saw this code from the Stroustrup's book, but I can't understand how it works.
I just can't get how it increases by " 0, 1, 4, 9..."
int archaic_square(int v) {
int total = 0;
for (int i = 0; i < v; ++i) {
total += v;
}
return total;
}
int main() {
for (int i = 0; i < 100; ++i) {
cout << i << '\t' << archaic_square(i) << '\n';
}
return 0;
}
The code in archaic_square is starting total off as zero, then adding v to it v times (in the loop).
By definition, it will then end up as:
0 + v + v + … + v
\___________/
v times
which is 0 + v * v, or v2.
In more explicit detail:
adding zero to zero, zero times, gives you zero (0);
adding one to zero, once, gives you one (0, 1);
adding two to zero, two times, gives you four (0, 2, 4);
adding three to zero, three times, gives you nine (0, 3, 6, 9);
adding four to zero, four times, gives you sixteen (0, 4, 8, 12, 16);
and so on, ad infinitum.
Remember from arithmetic that multiplication is repeated just a addition (or rather a repeated addition by definition)? That's all that's happening here.
Since v is getting added v times, it is the same as v * v, or v squared.
That code calculates squares by the method of differences. It's an alternative way of evaluating functions, and has some benefits over the usual plug-in-the-values approach. It was used in Babbage's difference engine, which was designed in the early 1800s to calculate mathematical tables for logarithms, trig functions, etc. to 40 (!) digits.
The underlying idea is that you begin with a list of values that you know how to evaluate. You subtract each value from its neighboring value, giving you a list of first differences. Then you subtract each difference value from its neighboring difference value, giving you a list of second differences. Continue until you reach a level where all the difference values are equal. For a second-order polynomial (such as x^2) the second differences will all be equal. For a third-order polynomial, the third differences will all be equal. And so on.
So for calculating squares, you end up with this:
Value First Difference Second Difference
0
1 1
4 3 2
9 5 2
16 7 2
25 9 2
Now you can reverse the process. Start with a result of 0. Add the first difference (1), giving the next result (1). Then increase the first difference by the second difference (2), giving the next first difference (3); add that to the previous result (1), giving the next result (4). Then increase the new first difference (3) by the second difference (2), giving the next first difference (5); add that to the previous result (4), giving the next result (9). Continue until done.
So with only the first value (0), the first difference (1), and the (constant) second difference (2), we can generate as long a list of squares as we would like.
When you need a list of results, calculating them one after another like this replaces multiplication with addition, which, back in the olden days, was much faster. Further, if a computer (back when a computer was a person who did tedious calculations to produce mathematical) made a mistake, all the results after that mistake would be wrong, too, so the mathematician in charge of the project didn't have to provide for checking every result; spot checking was sufficient.
Calculating trig functions, of course, is a bit more tricky, because they aren't defined by polynomials. But over a small enough region they can be approximated by a polynomial.
Babbage's engine would have calculated 40 digit values, with up to 7 levels of differences. It mechanically went through the sequence of steps I mentioned above, grinding out results at a rate of one every few seconds. Babbage didn't actually build the full difference engine; he got an inspiration for a much more powerful "Analytical engine" which he also never built. It would have been a precursor to modern digital computers, with 1000 40-digit storage units, an arithmetic processor, and punched cards to control the sequence of operations.

Intuition behind this initialization in Dynamic Programming

I am solving the following question:
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
by constructing the following code snippet:
class Solution {
public:
int climbStairs(int n) {
if(n==0)
return 0;
vector<int> dp(n+1);
dp[0]=0;
dp[1]=1;
for(int i=2; i<=n; i++)
dp[i] = dp[i-1] + dp[i-2];
return dp[n];
}
};
When I looked at the solutions, I found out that they make this initialization:
dp[0]=0;
dp[1]=1;
dp[2]=2; //--> Why? And when to do this?
Because of the way I initialized it, I get lower values (like the answer for n is at dp[n-1] and so on). All this, just because I didn't initialize dp[2]=2. Could someone please point out the intuition behind this particular initialization?
Thanks!
Note: Question is taken from LeetCode.com.
The code that’s posted here is incorrect, which is why you don’t see dp[2] = 2.
I believe that the DP table here is such that dp[k] represents the number of ways to climb from step n - k to step n using the rules described here. As a result, dp[0] should be 1, not 0. There’s one way to get from step n to step n by following these rules: namely, don’t take any steps!
Once you’ve initialized that value properly, then you’d have dp[2] = dp[0] + dp[1] = 1 + 1 = 2. There’s no need to explicitly initialize dp[2] since the value follows from the general case.

Not sure what this recursive function returns

I am reading about recursive functions, and I have been trying to figure out the mathematical formula for this one. I thought maybe it was a logarithmic function, but that does not seem to be it. If anyone could point me in the right direction, I would appreciate it.
unsigned f(unsigned n){
if(n<2)
return 1;
return 1 + f(n/2);
}
It is a logarithm function, just to the base 2. More specifically, it's the ceil 1 + floor(log2(n)).
Since you are concerned about the math formula that the function is based on:
Here it is: f is a function of n:
f(n) = 1 + f(n/2) if n >=2
f(n) = 1 if n <=1 //n is unsigned so n >=0
With the above formula in mind, the underlying algorithm then have logarithmic complexity. The reason is that at every step, it will reduce the size of n by half and it will take log(n) (base 2) steps to reach 1, therefore, it is logarithmic complexity with O(logn).
I know you said "mathematical function" and that's set the perspective for existing answers, but to show another perspective, it returns:
number of bits needed to store n (if you consider 1 bit necessary to meaningfully encode number 0 distinct from no number at all)
1-based index of the most significant bit set in n|1, aka
minimum of (1) and (the 1-based index of the most significant bit set in n)
Sometimes when you're looking at code it can help to see that a function could be used with different intent - either perspective might make it easier to understand the context of use.
This function returns the number of times the function executes by dividing the whole number by 2 until reaching zero
EX:
f(8)-
return f(4) //continue on
return f(2) //continue on
return f(1) //we know this is 1
return f(2) //now we can do this one, 2
return f(4) //and this one, 3
More precisely, this implements floor(logbase2(n))

while (i<=sqrt(static_cast<double>(n))

In the "C++ Without Fear: A Beginner's Guide That Makes You Feel Smart" book, in Chapter (2): Decisions, Decisions, you can see this lin of code as part of the prime number program:
while (i<=sqrt(static_cast<double>(n))
Provided that "i" was initialized to "2", and "n" is the user's input.
Why are we comparing to the "sqrt" of "n" and not to "n" itself?
Thanks.
Because you won't get any factors for non-primes which are > sqrt(n) (you would have already found the other, smaller factor).
It's a really bad test though, it would be much better to write it as:
while (i*i <= n)
Because if a number has factors other than itself and 1, then at least one of those factors will be less than the number's sqrt.
while (i<=sqrt(static_cast<double>(n))
Is equivalent to
while(n >= i*i)
Why the author choose the first solution may depend on other parts of the code.
The code goes like this:
i = 2;
while (i <= sqrt(static_cast<double>(n)) {
if (n % i == 0) is_prime = false;
i++;
}
So the loop is checking if n is divisible by i without remainder. Obviously, only has to check this up to (and including) the square root of n (because if n / p = q then also n / q = p).
Algorithmically it is correct to check possible factors up to the square root of your target.
If N is a number that may or may not be prime, if there are no factors (not including 1) up to sqrt(N) then N must be prime. sqrt(N) itself may well be its only prime factor (eg 9 which is 3*3).
If we are going to test to see if 17 is prime, we know that sqrt(17) is just above 4. 2, 3 and 4 do not divide into 17 so it must be prime as 5 is greater.
This must be the case because 17/5 will be less than 5 and would have to be a factor too, but we know there are no factors less than 5.
Programmatically of course the code is not optimal, as you would not use doubles and square-roots but something like (i*i <= N)

Dynamic programming algorithm N, K problem

An algorithm which will take two positive numbers N and K and calculate the biggest possible number we can get by transforming N into another number via removing K digits from N.
For ex, let say we have N=12345 and K=3 so the biggest possible number we can get by removing 3 digits from N is 45 (other transformations would be 12, 15, 35 but 45 is the biggest). Also you cannot change the order of the digits in N (so 54 is NOT a solution). Another example would be N=66621542 and K=3 so the solution will be 66654.
I know this is a dynamic programming related problem and I can't get any idea about solving it. I need to solve this for 2 days, so any help is appreciated. If you don't want to solve this for me you don't have to but please point me to the trick or at least some materials where i can read up more about some similar issues.
Thank you in advance.
This can be solved in O(L) where L = number of digits. Why use complicated DP formulas when we can use a stack to do this:
For: 66621542
Add a digit on the stack while there are less than or equal to L - K digits on the stack:
66621. Now, remove digits from the stack while they are less than the currently read digit and put the current digit on the stack:
read 5: 5 > 2, pop 1 off the stack. 5 > 2, pop 2 also. put 5: 6665
read 4: stack isnt full, put 4: 66654
read 2: 2 < 4, do nothing.
You need one more condition: be sure not to pop off more items from the stack than there are digits left in your number, otherwise your solution will be incomplete!
Another example: 12345
L = 5, K = 3
put L - K = 2 digits on the stack: 12
read 3, 3 > 2, pop 2, 3 > 1, pop 1, put 3. stack: 3
read 4, 4 > 3, pop 3, put 4: 4
read 5: 5 > 4, but we can't pop 4, otherwise we won't have enough digits left. so push 5: 45.
Well, to solve any dynamic programming problem, you need to break it down into recurring subsolutions.
Say we define your problem as A(n, k), which returns the largest number possible by removing k digits from n.
We can define a simple recursive algorithm from this.
Using your example, A(12345, 3) = max { A(2345, 2), A(1345, 2), A(1245, 2), A(1234, 2) }
More generally, A(n, k) = max { A(n with 1 digit removed, k - 1) }
And you base case is A(n, 0) = n.
Using this approach, you can create a table that caches the values of n and k.
int A(int n, int k)
{
typedef std::pair<int, int> input;
static std::map<input, int> cache;
if (k == 0) return n;
input i(n, k);
if (cache.find(i) != cache.end())
return cache[i];
cache[i] = /* ... as above ... */
return cache[i];
}
Now, that's the straight forward solution, but there is a better solution that works with a very small one-dimensional cache. Consider rephrasing the question like this: "Given a string n and integer k, find the lexicographically greatest subsequence in n of length k". This is essentially what your problem is, and the solution is much more simple.
We can now define a different function B(i, j), which gives the largest lexicographical sequence of length (i - j), using only the first i digits of n (in other words, having removed j digits from the first i digits of n).
Using your example again, we would have:
B(1, 0) = 1
B(2, 0) = 12
B(3, 0) = 123
B(3, 1) = 23
B(3, 2) = 3
etc.
With a little bit of thinking, we can find the recurrence relation:
B(i, j) = max( 10B(i-1, j) + ni , B(i-1, j-1) )
or, if j = i then B(i, j) = B(i-1, j-1)
and B(0, 0) = 0
And you can code that up in a very similar way to the above.
The trick to solving a dynamic programming problem is usually to figuring out what the structure of a solution looks like, and more specifically if it exhibits optimal substructure.
In this case, it seems to me that the optimal solution with N=12345 and K=3 would have an optimal solution to N=12345 and K=2 as part of the solution. If you can convince yourself that this holds, then you should be able to express a solution to the problem recursively. Then either implement this with memoisation or bottom-up.
The two most important elements of any dynamic programming solution are:
Defining the right subproblems
Defining a recurrence relation between the answer to a sub-problem and the answer to smaller sub-problems
Finding base cases, the smallest sub-problems whose answer does not depend on any other answers
Figuring out the scan order in which you must solve the sub-problems (so that you never use the recurrence relation based on uninitialized data)
You'll know that you have the right subproblems defined when
The problem you need the answer to is one of them
The base cases really are trivial
The recurrence is easy to evaluate
The scan order is straightforward
In your case, it is straightforward to specify the subproblems. Since this is probably homework, I will just give you the hint that you might wish that N had fewer digits to start off with.
Here's what i think:
Consider the first k + 1 digits from the left. Look for the biggest one, find it and remove the numbers to the left. If there exists two of the same biggest number, find the leftmost one and remove the numbers to the left of that. store the number of removed digits ( name it j ).
Do the same thing with the new number as N and k+1-j as K. Do this until k+1 -j equals to 1 (hopefully, it will, if i'm not mistaken).
The number you end up with will be the number you're looking for.