AVL Tree max and min node - java

how do i get the max and min number of nodes in an AVL tree when given the height of 8.
i can't seem to be able to trace it out properly from the formula f(8)=f(7)+f(6)+1
2*f(6)+f(5)+2
2*[f(5)+f(4)+1]+f(5)+2
3*f(5)+2*f4+4
3*[f(4)+f(3)+1]+2*f(4)+4
5*f(4)+3*f(3)+7
5*[f(3)+f(2)+1]+3*f(3)+7
8*f(3)+5*f(2)+12
8*[f(2)+f(1)+1]+5*f(2)+12
13*f(2)+8*f(1)+20
13*[f(1)+f(0)+1]+8*f(1)+20
21*f(1)+13*f(0)+33=54 whereas answer is 88 is the minimum

For every node in AVL tree we know that the depths of the left and the right subtree differs by at most 1 (this is given by definition).
From that, the next step is quite obvious : we take the minimum trees of depths N and N - 1 and place them as subtrees for a new root. It's clear that the AVL rules still hold and that the tree is contains as little nodes as possible (obvious from the induction base case).
From that, we've got the recursion formula : minnodes(depth) = 1 + minnodes(depth-1) + minnodes(depth - 2). That's a simple recursive equation, Wolfram Alpha can solve that for you (link).
The second case is trivial - a perfect binary tree of depth h contains as many nodes as possible for the depth given and trivially satisfies the AVL conditions.

You miscalculated a step somewhere, looks like near the end:
f(0) = 1
f(1) = 2
f(2) = f(1) + f(0) + 1 = 4
f(3) = f(2) + f(1) + 1 = 4 + 2 + 1 = 7
f(4) = f(3) + f(2) + 1 = 7 + 4 + 1 = 12
f(5) = f(4) + f(3) + 1 = 12 + 7 + 1 = 20
f(6) = f(5) + f(4) + 1 = 20 + 12 + 1 = 34
f(7) = f(6) + f(5) + 1 = 34 + 20 + 1 = 55
f(8) = f(7) + f(6) + 1 = 55 + 34 + 1 = 88
And if you don't believe it, you can always cook up a quick snippet to check:
#Test
public void testGetMax() {
assertEquals(88, getMax(8));
}
int getMax(int x) {
switch (x) {
case 0:
return 1;
case 1:
return 2;
default:
return getMax(x - 1) + getMax(x - 2) + 1;
}
}

Related

Recursive approach to finding out all the ways to change a €5 note?

I've been given a problem about finding all the possible combinations to change a 5 euro note. I've written a program that fails to result the correct number of combinations.
My approach was inspired from the following:
500 can be divided into 200, 200 and 100.
200 can be divided into 100 and 100.
100 can be divided into 50 and 50.
After I've written my code, I've realised that 100 can also be divided into 5 20's. This is a fault which I am aware of but I do not know how to fix using my approach.
My approach was a recursive one as can be seen below, it simply checks the first digit and divides it accordingly.
Here is what I tried:
public class Q1 {
public static int counter;
public static void main(String[] args) {
divide(500);
System.out.println(counter);
}
private static void divide(int x) {
System.out.println("Dividing " + x);
if(x == 1) {
return;
}
counter++;
int length = String.valueOf(x).length();
int fd = Integer.parseInt(Integer.toString(x).substring(0, 1));
String zeros;
if(fd != 1) {
zeros = Integer.toString(x).substring(1, length);
}else {
zeros = Integer.toString(x).substring(1, length-1);
}
if(fd == 5) {
divide(Integer.parseInt(2 + "" + zeros));
divide(Integer.parseInt(2 + "" + zeros));
divide(Integer.parseInt(1 + "" + zeros));
}else if(fd == 2) {
divide(Integer.parseInt(1 + "" + zeros));
divide(Integer.parseInt(1 + "" + zeros));
}else if(fd == 1) {
divide(Integer.parseInt(5 + "" + zeros));
divide(Integer.parseInt(5 + "" + zeros));
}
}
}
For example using the above program misses
10 = 2 + 2 + 2 + 2 + 2
I am aware of working solutions already present like this one but I would like to maintain my approach if possible.
Using the program to finding out the combinations for 500 cents results 388 ways, where the correct answer is 6295435. Something tells me I'm forgetting something else other than the above example.
Here are some hints about why you get the wrong number:
A correct way to determine all possibilities
Try to split 5 instead of 500 for simplicity. Notice that there are 4 possibilities, namely 5 =
5
2 + 2 + 1
2 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1
Now try dividing 10 instead of 500.
Notice that this can be split up into 11 different ways:
10 =
10
5 + 5
5 + 2 + 2 + 1
5 + 2 + 1 + 1 + 1
5 + 1 + 1 + 1 + 1 + 1
2 + 2 + 2 + 2 + 2
2 + 2 + 2 + 2 + 1 + 1
2 + 2 + 2 + 1 + 1 + 1 + 1
2 + 2 + 1 + 1 + 1 + 1 + 1 + 1
2 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
This solution follows the following pattern:
The number x you want to split is already an answer.
Decrease the amount of lowest numbers by 1 by splitting one of these numbers into as many next largest numbers as possible. Always ignore ones. If there is only one number (and ones) left, split x into as many next lowest numbers as possible and continue.
For example, x = 10. Then: 10 is the lowest number -> split it up into 5 + 5 -> 5 is the lowest number ->split it up into 2 + 2 + 1 -> 2 is the lowest number since ones are ignored -> split it up into 1 + 1 -> we have another 2, split it up into 1 + 1 (this is equal to the solution 5 + 1 + 1 + 1 + 1 so now we have 5 as only one number apart from ones. Next lowest number is 2)-> split x=10 into 2 + 2 + 2 + 2 + 2 -> 2 is the lowest number; split it into 1 + 1 -> we have another 2 ...
This can be done in an recursive approach.
Things that went wrong in your code
What you code does with the example of dividing 10 is the following:
10 is divided into 5 + 5
the first 5 is divided into 2 + 2 + 1
one of these twos is divided into 1 + 1
the other two is divided into 1 + 1
Now, the second 5 from the division into 5 + 5 is divided into 2 + 2 + 1
One of these twos is divided into 1 + 1
The other two is divided into 1 + 1
Giving it a total score of 7 possibilities.
Trying to map These 7 possibilities to the 11 above, you count 10 =
10 once
5 + 5 once
5 + 2 + 2 + 1 twice
5 + 2 + 1 + 1 + 1 twice
5 + 1 + 1 + 1 + 1 + 1 twice
and missing the other 6 options.
So the assumption that this question can be solved by an approach like this one:
10 = 5 + 5 -> evaluate the first 5, then evaluate the second 5
is wrong because in both cases this leads to a distribution of 10 = 5 + evaluation of 5, counting only the options where at least one 5 is contained in the final distribution of 10 (counting it multiple times whearas distributions without fives are not evaluated).
Another mistake is that the code says there is no possible distribution of 1 where there actually exists one (1 = 1).
Also, the question is unclear about
can x equal all possible whole numbers? -> 4 is not split up into 2 + 2 in your Code
is 1 + 1 + 2 a different solution from 1 + 2 + 1 and 2 + 1 + 1?
(I don't have the reputation to ask this in a comment yet).
Remaining an recursive approach can only be done with some significant changes. A possible way to do so is stated above.

How does this approximation of division using bit shift operations work?

In java.util.DualPivotQuicksort, the following line of code appears:
// Inexpensive approximation of length / 7
int seventh = (length >> 3) + (length >> 6) + 1;
The variable length is an int greater than or equal to 47.
I am familiar with how the signed right shift operator works. But I do not know why these particular operations result in an approximation of division by 7. Could someone explain please?
>> is bitshift. Every bit you shift right, in effect divides the number of 2.
Therefore, (length >> 3) is length/8 (rounded down), and (length >> 6) is length/64.
Take (length/8)+(length/64) is approximately length*(1/8+1/64) = length*0.140625 (approximately)
1/7 = 0.142857...
The +1 at the end can be split into +0.5 for each term, so that length/8 is rounded to nearest (instead of down), and length/64 is also rounded to nearest.
In general, you can easily approximate 1/y, where y = 2^n+-1 with a similar bit-shift approximation.
The infinite geometric series is:
1 + x + x^2 + x^3 + ... = 1 / (1 - x)
Multiplying by x:
x + x^2 + x^3 + ... = x/(1 - x)
And substituting x = 1/2^n
1/2^n + 1/2^2n + 1/2^3n + ... = (1/2^n) / (1 - 1/2^n)
1/2^n + 1/2^2n + 1/2^3n + ... = (1/2^n) / ((2^n - 1)/2^n)
1/2^n + 1/2^2n + 1/2^3n + ... = 1 / (2^n - 1)
This approximates y = 2^n - 1.
To approximate y = 2^n + 1, substitute x = -1/2^n instead.
- 1/2^n + 1/2^2n - 1/2^3n + ... = (-1/2^n) / (1 + 1/2^n)
1/2^n - 1/2^2n + 1/2^3n - ... = (1/2^n) / ((2^n + 1)/2^n)
1/2^n - 1/2^2n + 1/2^3n - ... = 1 / (2^n + 1)
Then just truncate the infinite series to the desired accuracy.
Set x = 1/8 in the well-known equality
1 + x + x^2 + x^3 + ... = 1 / (1 - x)
and simplify, to give
1/8 + 1/64 + 1/512 + ... = 1/7
Multiply both sides of this by length in your example, to give
length / 7 = length / 8 + length / 64 + length / 512 + ...
Note that this is "exact" division, not integer division - I'm writing mathematics, not Java code.
Then the approximation assumes that the third and subsequent terms will be too small to matter, and that on average one of length / 8 and length / 64 is likely to need rounding up, rather than rounding down. So, now using integer division, length / 7 = length / 8 + length / 64 + 1 is a very good approximation.
The expression you gave, using bitwise operators, is just an alternative way of writing this, provided length is positive.
To put a mathematical background to ronalchn's answer:
Since 7=8-1=8*(1-1/8), by the geometric series division by 7 is the same as multiplication by
1/7 = 1/8·(1+1/8+1/8²+1/8³+…) = 1/8+1/8²+1/8³+…
To do the same for the division by 5, one would use that 3·5=16-1 and thus
1/5 = 3/16·(1+1/16+1/16²+…)
which would invite a formula like
(3*n)<<4 + (3*n) << 8 + 1
Computing all values of
n/8 + n/64 - n/7
the error grows linearly, while staying negative.
The list below shows the first time a given error appears
n = 7 e = -1
n = 63 e = -2
n = 511 e = -3
n = 959 e = -4
n = 1407 e = -5
n = 1855 e = -6
n = 2303 e = -7
n = 2751 e = -8
n = 3199 e = -9
n = 3647 e = -10
n = 4095 e = -11
n = 4543 e = -12
n = 4991 e = -13
n = 5439 e = -14
n = 5887 e = -15
n = 6335 e = -16
n = 6783 e = -17
n = 7231 e = -18
n = 7679 e = -19
n = 8127 e = -20
n = 8575 e = -21
n = 9023 e = -22
n = 9471 e = -23
n = 9919 e = -24
...
The ratio obviously tends to 1/448 = 1/8 + 1/64 - 1/7.

How can I optimize this class that solves this math sequence

Given an infinite sequence like so (commas inserted to make pattern more apparent):
1, 1 2, 1 2 3, 1 2 3 4, 1 2 3 4 5, 1 2 3 4 5 6 ,1 2 3 4 5 6 7, 1 2 3 4 5 6 7 8, 1 2 3 4 5 6 7 8 9, 1 2 3 4 5 6 7 8 9 1 0, 1 2 3 4 5 6 7 8 9 1 0 1 1, 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2, 1 2 3 . . . . . . . . . .
I am given an index (1 <= index <= 10^10) and I need to find what digit is in that index.
I have wrote this working code but it is too slow. I have optimized it as much as I can but it's still not enough. Is there any other way I can make this run faster?
public class Foo {
private static Scanner sc = new Scanner(System.in);
private static long input;
private static long inputCounter = 0;
private static int numberOfInputs;
public static void main(String[] args) {
numberOfInputs = Integer.parseInt(sc.nextLine().trim());
while (inputCounter != numberOfInputs) {
input = Long.parseLong(sc.nextLine().trim());
System.out.println(step());
inputCounter++;
}
}
public static char step() {
int incrementor = 1;
long _counter = 1L;
while (true) {
for (int i = 1; i <= incrementor; i++) {
_counter += getNumberOfDigits(i);
if (_counter > input) {
return ((i + "").charAt((int)(input - _counter
+ getNumberOfDigits(i))));
}
}
incrementor++;
}
}
private static long getNumberOfDigits(int n) {
// 5 or less
if (n < 100) {
// 1 or 2
if (n < 10)
return 1;
else
return 2;
} else {
// 3 or 4 or 5
if (n < 1000)
return 3;
else {
// 4 or 5
if (n < 10000)
return 4;
else
return 5;
}
}
}
}
EDIT: Credit to Marian's method of getting the number of digits in a number. His divide and conquer method which I've named getNumberOfDigits(int n) sped up my program execution a lot. Initially I was converting the number to a String then calling length() and that was taking a lot longer than I expected
EDIT2: Some sample I/O:
1 : 1
2 : 1
3 : 2
4 : 1
5 : 2
6 : 3
7 : 1
8 : 2
9 : 3
10 : 4
11 : 1
12 : 2
13 : 3
14 : 4
15 : 5
16 : 1
17 : 2
18 : 3
19 : 4
20 : 5
21 : 6
22 : 1
23 : 2
24 : 3
25 : 4
26 : 5
27 : 6
28 : 7
29 : 1
30 : 2
31 : 3
32 : 4
33 : 5
34 : 6
35 : 7
36 : 8
37 : 1
38 : 2
39 : 3
40 : 4
41 : 5
42 : 6
43 : 7
44 : 8
45 : 9
46 : 1
47 : 2
48 : 3
49 : 4
50 : 5
51 : 6
52 : 7
53 : 8
54 : 9
55 : 1
56 : 0
57 : 1
58 : 2
59 : 3
60 : 4
61 : 5
62 : 6
63 : 7
64 : 8
65 : 9
66 : 1
67 : 0
68 : 1
69 : 1
70 : 1
71 : 2
72 : 3
73 : 4
74 : 5
75 : 6
76 : 7
77 : 8
78 : 9
79 : 1
80 : 0
81 : 1
82 : 1
83 : 1
84 : 2
85 : 1
86 : 2
87 : 3
88 : 4
89 : 5
90 : 6
91 : 7
92 : 8
93 : 9
94 : 1
95 : 0
96 : 1
97 : 1
98 : 1
99 : 2
I think the triangular numbers come into play here if we look at the positions of the digits:
Position: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, 16 17 18 19 20 21, 22 23 24 25 26 27 28
Number: 1, 1 2, 1 2 3, 1 2 3 4, 1 2 3 4 5, 1 2 3 4 5 6, 1 2 3 4 5 6 7,
Call this sequence N(p).
Now look at the triangular numbers which have formula k(k+1)/2
k : 1 2 3 4 5 6
k(k+1)/2 : 1 3 6 10 15 21 triangle numbers
k(k+1)/2+1 : 2 4 7 11 16 22 plus one
N(k(k+1)/2+1): 1 1 1 1 1 1 item at this position
so the item just after the n'th triangular number is always 1.
Give a position p we can find nearest k so that k(k+1)/2 +1 <= p. We can solve the quadratic x(x+1)/2+1=p by rearranging
0.5 x^2 + 0.5 x + 1 - p = 0.
So a=0.5, b=0.5 and c=1-p. Solving for x gives
x = -0.5 +/- sqrt( 0.25 - 2 * (1-p) )
take the positive sign this gives these values
1 0
2 1
3 1.5615528128
4 2
5 2.3722813233
6 2.7015621187
7 3
8 3.2749172176
9 3.5311288741
10 3.7720018727
11 4
12 4.216990566
13 4.4244289009
14 4.623475383
15 4.8150729064
16 5
So if we take k=floor(-0.5 +/- sqrt( 2 p - 1.75 ) ) we find the number k. Next find l = p-k(k+1)/2 which gives the digit in the p-th place.
As pointed out this fails as soon as we get two digit numbers. But we could make an adjustment. We can get a formula "triangular-digit-number" TD(k). Which behaves like triangular numbers, T(k), for k < 10, but adds the extra digits.
k : 1 ... 9 10 11 12
T(k) : 1 45 55 66 78
change 1 3 6
TD(k) : 2 45 56 69 84
We see that for 10 <= k <= 99 we just need to add T(k)+T(k-9). This should give us another quadratic we could be solved. Similar happens for 100<=k<=999 with T(k)+T(k-9)+T(k-99).
Now T(k)+T(k-9) + 1 = k(k+1)/2 +(k-9)(k-8)/2 + 1
= 0.5 k^2 + 0.5 k + 0.5 k^2 - 17/2 k + 72/2 + 1
= k^2 -8 k + 37
Solve x^2 -8 k + 37 - p =0 gives
x = ( 8 +/- sqrt(64 - 4 *(37-p) ) ) /2
= ( 8 +/- sqrt(4 p - 64) )/2
= 4 +/- sqrt(p - 21)
taking the floor of this gives us the k value.
We want to find the sum of triangles T(k) + T(k-9) + T(k-99) + ....
To a first approximation T(k-n) = T(n) for any n. So the sum is simply
d * T(k) where d in the number of digits of k. T(k) is approximately k^2/2 so the sum is approx d * k^2/2. This is easy to solve let d be the number of digits of the position p then k = sqrt(2*p/d). You could use this to get a rough guess for k.
The following code is a nearly direct calculation. It produces the exact same results as that of #maaartinus (see results below) but does it in < 1ms as opposed to 30ms.
See the code comments for details on how it works. Let me know if I need to explain a bit more.
package com.test.www;
import java.util.ArrayList;
import java.util.List;
public final class Test {
/** <p>
* Finds digit at {#code digitAt} position. Runs in O(n) where n is the max
* digits of the 'full' number (see below), e.g. for {#code digitAt} = 10^10,
* n ~ 5, for 10^20, n ~ 10.
* <p>
* The algorithm is thus basically a direct 'closed form' calculation.
* It finds the quadratic equation to calculate triangular numbers (x(x+1)/2) but also
* takes into a account the transitions from 9 to 10, from 99 to 100, etc. and
* adjusts the quadratic equation accordingly. This finds the last 'full' number
* on each 'line' (see below). The rest follows from there.
*
*/
public static char findDigitAt(long digitAt) {
/* The line number where digitAt points to, where:
* 1, 1 2, 1 2 3, 1 2 3 4, etc. ->
* 1 <- line 1
* 1 2 <- line 2
* 1 2 3 <- line 3
* 1 2 3 4 <- line 4
*/
long line;
// ---- Get number of digits of 'full' numbers where digitAt at points, e.g.
// if digitAt = 55 or 56 then digits = the number of digits in 10 which is 2.
long nines = 0L; // = 9 on first iteration, 99 on second, etc.
long digits = 0;
long cutoff = 0; // Cutoff of digitAt where number of digits change
while (digitAt > cutoff) {
digits++;
nines = nines + Math.round(Math.pow(10L, digits-1L)) * 9L;
long nines2 = 0L;
cutoff = 0L;
for (long i = 1L; i <= digits; i++) {
cutoff = cutoff + ((nines-nines2)*(nines-nines2+1)/2);
nines2 = nines2 + Math.round(Math.pow(10L, i-1L)) * 9L;
}
}
/* We build a quadratic equation to take us from digitAt to line */
double r = 0; // Result of solved quadratic equation
// Must be double since we're using Sqrt()
// even though result is always an integer.
// ---- Define the coefficients of the quadratic equation
long xSquared = digits;
long x = 0L;
long c = 0L;
nines = 0L; // = 9 on first iteration, 99 on second, etc.
for (long i = 1L; i <= digits; i++) {
x = x + (-2L*nines + 1L);
c = c + (nines * (nines - 1L));
nines = nines + Math.round(Math.pow(10L, i-1L)) * 9L;
}
// ---- Solve quadratic equation, i.e. y - ax^2 + bx + c => x = [ -b +/- sqrt(b^2 - 4ac) ] / 2
r = (-x + Math.sqrt(x*x - 4L*xSquared*(c-2L*digitAt))) / (2L*xSquared);
// Make r an integer
line = ((long) r) + 1L;
if (r - Math.floor(r) == 0.0) { // Simply takes care of special case
line = line - 1L;
}
/* Now we have the line number ! */
// ---- Calculate the last number on the line
long lastNum = 0;
nines = 0;
for (int i = 1; i <= digits; i++) {
long pline = line - nines;
lastNum = lastNum + (pline * (pline+1))/2;
nines = nines + Math.round(Math.pow(10, i-1)) * 9;
}
/* The hard work is done now. The piece of cryptic code below simply counts
* back from LastNum to digitAt to find first the 'full' number at that point
* and then finally counts back in the string representation of 'full' number
* to find the actual digit.
*/
long fullNumber = 0L;
long line_decs = 1 + (int) Math.log10(line);
boolean done = false;
long nb;
long a1 = Math.round(Math.pow(10, line_decs-1));
long count_back = 0;
while (!done) {
nb = lastNum - (line - a1) * line_decs;
if (nb-(line_decs-1) <= digitAt) {
fullNumber = line - (lastNum - digitAt) / line_decs;
count_back = (lastNum - digitAt) % line_decs;
done = true;
} else {
lastNum = nb-(line_decs);
line = a1-1;
line_decs--;
a1 = a1 / 10;
}
}
String numStr = String.valueOf(fullNumber);
char digit = numStr.charAt(numStr.length() - (int) count_back - 1);
//System.out.println("digitAt = " + digitAt + " - fullNumber = " + fullNumber + " - digit = " + digit);
System.out.println("Found " + digit + " at position " + digitAt);
return digit;
}
public static void main(String... args) {
long t = System.currentTimeMillis();
List<Long> testList = new ArrayList<Long>();
testList.add(1L); testList.add(2L); testList.add(3L); testList.add(9L);
testList.add(2147483647L);
for (int i = 1; i <= 18; i++) {
testList.add( Math.round(Math.pow(10, i-1)) * 10);
}
//testList.add(4611686018427387903L); // OVERFLOW OCCURS
for (Long testValue : testList) {
char digit = findDigitAt(testValue);
}
long took = t = System.currentTimeMillis() - t;
System.out.println("Calculation of all above took: " + t + "ms");
}
}
Results
Found 1 at position 1
Found 1 at position 2
Found 2 at position 3
Found 3 at position 9
Found 2 at position 2147483647
Found 4 at position 10
Found 1 at position 100
Found 4 at position 1000
Found 9 at position 10000
Found 2 at position 100000
Found 6 at position 1000000
Found 2 at position 10000000
Found 6 at position 100000000
Found 8 at position 1000000000
Found 1 at position 10000000000
Found 1 at position 100000000000
Found 9 at position 1000000000000
Found 8 at position 10000000000000
Found 3 at position 100000000000000
Found 7 at position 1000000000000000
Found 6 at position 10000000000000000
Found 1 at position 100000000000000000
Found 1 at position 1000000000000000000
Calculation of all above took: 0ms
I've added some code that vastly improves the running time - skip to the bottom to see the examples.
A key insight I found is that you can skip sub-sequences if the input doesn't lie anywhere in it. For example, if you are looking for the 1,000,000,000th number, you know it isn't in the 5th subsequence {1,2,3,4,5}. So why iterate over it? This version seems to be much faster (try running it with an input of 1000000000 and see the time difference), and as far as I can tell it returns the same result in all cases.
Thus, my algorithm keeps track of the length of the subsequence (Add the number of digits on each iteration), and the subsequence we're on. If the input is larger than the subsequence's length, just subtract that length and iterate again. If it is smaller (or equal, since the problem is 1-indexed), start breaking down that sub-sequence.
A minor note: I also updated the getNumberOfDigits so that it can handle any number by doing it recursively, but both the new and old versions rely on this new method so it doesn't get credit for the time improvement.
public class Foo {
private static Scanner sc = new Scanner(System.in);
private static long input;
private static long inputCounter = 0;
private static int numberOfInputs;
/** Updated main method that calls both the new and old step() methods
* to compare their outputs and their respective calculation times.
* #param args
*/
public static void main(String[] args) {
numberOfInputs = Integer.parseInt(sc.nextLine().trim());
while (inputCounter != numberOfInputs) {
long i = Long.parseLong(sc.nextLine().trim());
input = i;
System.out.println("Processing " + input);
long t = System.currentTimeMillis();
System.out.println("New Step result - " + newStep() + " in " + (System.currentTimeMillis() - t)+"ms");
input = i;
t = System.currentTimeMillis();
System.out.println("Old Step result - " + step() + " in " + (System.currentTimeMillis() - t)+"ms");
inputCounter++;
}
}
/** Old version of step() method given in question. Used for time comparison */
public static char step() {
int incrementor = 1;
long _counter = 1L;
while (true) {
for (int i = 1; i <= incrementor; i++) {
_counter += getNumberOfDigits(i);
if (_counter > input) {
return ((i + "").charAt((int)(input - _counter
+ getNumberOfDigits(i))));
}
}
incrementor++;
}
}
/** New version of step() method.
* Instead of iterating one index at a time, determines if the result lies within this
* sub-sequence. If not, skips ahead the length of the subsequence.
* If it does, iterate through this subsequence and return the correct digit
*/
public static int newStep() {
long subSequenceLength = 0L;
long subSequenceIndex = 1L;
while(true){
//Update to the next subsequence length
subSequenceLength += getNumberOfDigits(subSequenceIndex);
if(input <= subSequenceLength){
//Input lies within this subsequence
long element = 0L;
do{
element++;
long numbDigits = getNumberOfDigits(element);
if(input > numbDigits)
input -= numbDigits;
else
break;
}while(true);
//Correct answer is one of the digits in element, 1-indexed.
//Speed isn't that important on this step because it's only done on return
return Integer.parseInt(("" + element).substring((int)input-1, (int)input));
} else{
//Input does not lie within this subsequence - move to next sequence
input -= subSequenceLength;
subSequenceIndex++;
}
}
}
/** Updated to handle any number - hopefully won't slow down too much.
* Won't handle negative numbers correctly, but that's out of the scope of the problem */
private static long getNumberOfDigits(long n){
return getNumberOfDigits(n, 1);
}
/** Helper to allow for tail recursion.
* #param n - the number of check the number of digits for
* #param i - the number of digits thus far. Accumulator. */
private static long getNumberOfDigits(long n, int i) {
if(n < 10) return i;
return getNumberOfDigits(n/10, i+1);
}
}
Sample output showing time improvement:
> 8
> 10000
Processing 10000
New Step result - 9 in 0ms
Old Step result - 9 in 2ms
> 100000
Processing 100000
New Step result - 2 in 0ms
Old Step result - 2 in 4ms
> 1000000
Processing 1000000
New Step result - 6 in 0ms
Old Step result - 6 in 3ms
> 10000000
Processing 10000000
New Step result - 2 in 1ms
Old Step result - 2 in 22ms
> 100000000
Processing 100000000
New Step result - 6 in 1ms
Old Step result - 6 in 178ms
> 1000000000
Processing 1000000000
New Step result - 8 in 4ms
Old Step result - 8 in 1765ms
> 10000000000
Processing 10000000000
New Step result - 1 in 11ms
Old Step result - 1 in 18109ms
> 100000000000
Processing 100000000000
New Step result - 1 in 5ms
Old Step result - 1 in 180704ms
I wrote a program without much thinking...
length(n) computes the number of digits of the decimal representation of n
cummulativLength(n) computes the total number of digits for a sequence ending with n
doublyCummulativLength(n) computes the total number of digits for all sequence ending with at most n
fullSequenceBefore(pos) computes the longest full sequence before the position pos using binary search
digitAt(n) computes the digit at position n by first computing fullSequenceBefore and subtracting its length; it then uses another binary search for the last sequence
I used long everywhere as it's damn fast. There's a rudimentary test and a demo producing the following results
Found 1 at position 1
Found 1 at position 2
Found 2 at position 3
Found 3 at position 9
Found 2 at position 2147483647
Found 4 at position 10
Found 1 at position 100
Found 4 at position 1000
Found 9 at position 10000
Found 2 at position 100000
Found 6 at position 1000000
Found 2 at position 10000000
Found 6 at position 100000000
Found 8 at position 1000000000
Found 1 at position 10000000000
Found 1 at position 100000000000
Found 9 at position 1000000000000
Found 8 at position 10000000000000
Found 3 at position 100000000000000
Found 7 at position 1000000000000000
Found 6 at position 10000000000000000
Found 1 at position 100000000000000000
Found 1 at position 1000000000000000000
Found 7 at position 4611686018427387903
Computed in 0.030 seconds.
The biggest number I tried it for is Long.MAX_VALUE/2. In theory it could work for Long.MAX_VALUE as well, but I'm getting overflow there.

How to read recursion methods accurately

I'm preparing for a java programming exam that's coming up in a few days and I've been tryna get my head around this:
The initial starting value of int num is 8
public int func1(int num)
{
if(num <=1) return 1;
return num + func1 (num - 3);
}
How do you go through through the return num + func1 (num - 3) part?
I don't get how that line of code works
I hope this will help you understand recursion.
return 8 + func1(5)
return 8 + 5 + func1(2)
return 8 + 5 + 2 + func1(-1)
return 8 + 5 + 2 + 1
and finally returns like
return 8 + 5 + 3
return 8 + 8
return 16
Easiest way to understand recursion is
Assume that the whole public int func1(int num) is inserted in the return
so ur function returns somewhat in this way
return 8 + func1((8) - 3) // i.e return 8 + func1(5)
return 8 + 5 + func1((5) - 3) // i.e return 8 + 5 + func1(2)
return 8 + 5 + 2 + func1((2) - 3) // i.e return 8 + 5 + 2 + func1(-1)
return 8 + 5 + 2 + 1
So total will be 16
Just trace through it the exact same as for any other method call. It's just calling itself which is another way to do a loop.
i.e.
func1(8)
calls func1(8-3)
calls func1(5-3)
calls func1(2-3)
returns 1 as -1 < 1
returns 2+1
returns 5+3
returns 8+8
So the final result is 16.
"The only way to understand recursion is to understand recursion".
Now seriously, you need to think of the execution of a recursive program as a stack with a basic case and a general case:
For instance the factorial method:
Basic case: f(n) = 1, if n = 0.
General case: f(n) = n*f(n-1), otherwise.
Result would be:
f(n) = n*f(n-1)
f(n-1) = (n-1)*f(n-2)
...
f(2) = 2*f(1)
f(1) = 1*f(0)
f(0) = 1
Basically you need to go down until the basic case and then reconstruc the result via the partial results you are finding.
Recursive algorithms are very useful for some methods which otherwise would require lots of memory to store intermediates value.

How did this get 8?

Here's the code:
class qual
{
public static int fibonacci(int n)
{
if (n == 0 || n == 1)
{
return 1;
}
else
{
return fibonacci(n-1) + fibonacci(n-2);
}
}
public static void main(String[] arg)
{
System.out.println(fibonacci(5));
}
}
The output was 8.
The output should be 8 but when I look at this I think it should be 7 ((5-1) +(5-2)).
Why was the output 8? I think the reasoning behind getting 8 will make recursion maybe stop being confusing for me.
Let's treat this like algebra, I'll write f(n) instead of fibonacci(n) to save space:
f(5) = f(4) + f(3)
f(5) = f(3) + f(2) + f(2) + f(1)
f(5) = f(2) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)
f(5) = f(1) + f(0) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)
f(5) = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
Because it's a recursive call, so each call where the argument is not 0 or 1 calls it again.
fibonacci(7)
-> fibonacci(6) // recursively calls itself with (7-1)
-> fibonacci(5) // recursively calls itself with (6-1)
-> fibonacci(4) // recursively calls itself with (5-1)
-> fibonacci(3) // recursively calls itself with (4-1)
-> fibonacci(2) // recursively calls itself with (3-1)
-> fibonacci(1) // recursively calls itself with (2-1)
-> fibonacci(4) // recursively calls itself with (6-2)
...
-> fibonacci(5) // recursively calls itself with (7-2)
-> fibonacci(4) // recursively calls itself with (5-1)
-> fibonacci(3) // recursively calls itself with (4-1)
-> fibonacci(2) // recursively calls itself with (3-1)
-> fibonacci(1) // recursively calls itself with (2-1)
-> fibonacci(3) // recursively calls itself with (5-2)
...
and so on.
Think about the logic like this, and you should be able to work out what it actually returns to the initial caller.
It's returning fibonacci(n-1), not n-1. When you call this with 5, you get:
return fib(4) + fib(3);
fib(4) returns:
return fib(3) + fib(2);
fib(3) returns:
return fib(2) + fib(1);
fib(2) returns:
return fib(1) + fib(0);
As soon as you reach fib(1) or fib(0), you return 1;
Working backwards, fib(2) returns 2:
return 1 /*fib(1)*/ + 1 /*fib(0)*/;
By the same logic, fib(3) returns 2 + 1, or 3. Fib(4) returns 3 + 2, or 5. Fib(5) therefor returns 5 + 3, which is your 8.
Perhaps this illustration adapted from the Structure and Interpretation of Computer Programs (SICP, or the Wizard book) will help:
Going off on a tangent, SICP is a fantastically deep though at times difficult introduction to programming. Since it uses Lisp (rather, Scheme) as its teaching language, recursion is used throughout. Even iterative processes in Lisp are based on recursive calls:
(define (factorial n)
(define (fact-iter n product)
(if (> n 1)
(fact-iter (- n 1) (* product n))
product
) )
(fact-iter n 1)
)
(factorial 5)
; returns 120
is actually iterative. Notes: car returns the head of a list, while cdr returns the tail. Operators use prefix notation; (- a b) is "a - b", (* a b) is "a * b".
Here's what your Fibonacci program looks like in Scheme:
(define (fibonacci n)
(if (or (= n 1) (= n 2))
1
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))
)
It's not ((5-1) + (5-2)), but rather (finonacci(5-1) + fibonacci(5-2))
And finonacci(5-1) reduces to fibonacci(4), which becomes (finonacci(4-1) + fibonacci(4-2)), etc.
return fibonacci (n-1) + fibonacci (n-2);
That's actually not just doing (n-1) + (n-2), it's recursively calling the fibonnacci function again.
So it's doing fibonacci (4) + fibonacci (3). That fib(4) is then evaluated to be fib(3) + fib(2), so it ends up returning fib (3) + fib (2) + fib (3). Again, each of those fib(3)'s are actually fib (2) + fib (1), and so on. It keeps breaking down like that, until it hits
if (n == 0 || n == 1)
so it ends up being a bunch of fib (1) + fib (0) + fib (1)..., which is 1 + 1 + 1..., which will end up as 8 if you actually break it all the way down.
I haven't seen this approach yet. Imagine you are storing results and building it up, where f[i] is the result of calling fibonacci(i). 0 and 1 are base cases, and the rest build on it:
f[0] = 1
f[1] = 1
f[2] = f[1] + f[0] = 1 + 1 = 2
f[3] = f[2] + f[1] = 2 + 1 = 3
f[4] = f[3] + f[2] = 3 + 2 = 5
f[5] = f[4] + f[3] = 5 + 3 = 8
The result of the function is not (5 - 1) + (5 - 2), but fibonacci( 5 - 1 ) + fibonacci( 5 - 2 ) or fibonacci( 4 ) + fibonacci( 3 ), which is 5 + 3. The sequence is:
1 1 2 3 5 8
0 1 2 3 4 5
Recursion is actually closely related to the mathematical concept of proof by induction -- and in fact the Fibonacci sequence is recursively defined, so on some level you have to already think in recursive terms to understand how it works.
To better understand the code, you can apply a beta reduction -- that is, replace each function call with the body of the function itself.
If fibonacci(n) translates to fibonacci(n - 1) + fibonacci(n - 2) then:
fibonacci(5) = fibonacci(4) + fibonacci(3)
fibonacci(5) = (fibonacci(3) + fibonacci(2)) + (fibonacci(2) + fibonacci(1))
It's easy to see that this would go on forever, unless we made a special case. Here, we know that fibonacci(1) translates to 1, and fibonacci(0) also translates to 1. So we keep beta-reducing until all we have left are ones, which looks like:
fibonacci(5) = ((1 + 1) + (1 + (1 + 1))) + ((1 + 1) + 1)
Therefore:
fibonacci(5) = 8
The first fibonacci number are 1,1,2,3,5,8,... any other numbers would be unexpected.

Categories

Resources