Skip to content

Commit

Permalink
implemented 'fibonacciDoublingFromRecursive2'
Browse files Browse the repository at this point in the history
math not suitable for sequence members greater than '3'
  • Loading branch information
GlulkAlex committed Aug 28, 2015
1 parent 331cccf commit dcaf4b0
Show file tree
Hide file tree
Showing 5 changed files with 420 additions and 18 deletions.
278 changes: 276 additions & 2 deletions src/main/scala/fibonacciFindingEasy/Solution.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package fibonacciFindingEasy

import java.math.BigInteger

import scala.annotation.tailrec

/**
Expand Down Expand Up @@ -63,6 +65,45 @@ F5=21+11=32
F6=32+21=53
F7=53+32=85
*/
/*
It is interesting to note that
the n-th Fibonacci number grows so fast that
F47 exceeds the 32-bit signed integer range.
The fastest way to accurately compute `Fibonacci numbers` is
by using a `matrix-exponentiation` method.
We need to
calculate MN to
calculate the Nth `fibonacci number`.
We can
calculate it in O(log(N)) using `fast exponentiation`.
*/
/*
>>>Using `Matrix Exponentiation`:
//Calculating A^p in O(log(P))
Matrix_pow ( Matrix A,int p )
if(p=1)
return A
if(p%2=1)
return A*Matrix_pow(A,p-1)
Matrix B = Matrix_pow(a,p/2);
return B * B
fibonacci(n)
if(n=0)
return 0;
if(n=1)
return 1;
Matrix M[2][2]={{1,1},{1,0}}
Matrix res=matrix_pow(M,n-1);
return res[0][0];
//Time Complexity = O(log(n))
*/
/*
also from:
http://www.nayuki.io/page/fast-fibonacci-algorithms
*/
object Solution {
def fibFrom(a: Int, b: Int):
Stream[Int] =
Expand Down Expand Up @@ -95,9 +136,242 @@ object Solution {
/*sequence start*/
a = b,
b = a + b,
elemOrder = elemOrder - 1
)
elemOrder = elemOrder - 1
)
}
}

/*
algorithms use multiplication,
so it become even faster when
`Karatsuba multiplication` is used.
it takes Θ(n * logbase2of3) ≈ Θ(n^1.58) time
instead of quadratic,
which gives a significant speed-up for large numbers.
*/
//@tailrec
def fibonacciDoublingFromRecursive2(
/*sequence start*/
a: Int,
b: Int,
/*resultAccum:
Stream[Int] =
Stream.empty,*/
elemOrder: Int = 0
):
Int = {
assume(elemOrder >= 0, s"'elemOrder' must be positive")
/*
cases:
> even
F(2 * n) = F(n) * (2 * F(n + 1) - F(n))
>> odd
F(2 * n + 1) = F(n + 1)^2 + F(n)^2
*/
if (elemOrder == 0) {
a
} else if (elemOrder == 1) {
b
} else if (elemOrder == 2) {
a + b
} else /*if (elemOrder > ?1? 2)*/ {
if (elemOrder % 2 != 0) {
//elemOrder > 2 == 2 * (n >=1) + 1
//3 = 2 * 1 + 1 => F(1 + 1)^2 + F(1)^2
//5 = 2 * 2 + 1 => F(2 + 1)^2 + F(2)^2
//>> odd
val halfOrder: Int = (elemOrder - 1) / 2
val halfOrderPlusOne: Int = halfOrder + 1
lazy val fibN: Int =
fibonacciDoublingFromRecursive2(
a = a,
b = b,
elemOrder = halfOrder
)
lazy val fibN_PlusOne: Int =
fibonacciDoublingFromRecursive2(
a = a,
b = b,
elemOrder = halfOrderPlusOne
)

fibN * fibN + fibN_PlusOne * fibN_PlusOne
} else /*if (elemOrder % 2 == 0)*/ {
//elemOrder == 2 * (n >=1)
/*'1 + 1' `dead` lock*/
//F(2 * 1) = F(1) * (2 * F(1 + 1) - F(n))
//> even
val halfOrder: Int = elemOrder / 2
val halfOrderPlusOne: Int = halfOrder + 1
lazy val fibN: Int =
fibonacciDoublingFromRecursive2(
a = a,
b = b,
elemOrder = halfOrder
)
lazy val fibN_PlusOne: Int =
fibonacciDoublingFromRecursive2(
a = a,
b = b,
elemOrder = halfOrderPlusOne
)

fibN * (2 * fibN_PlusOne - fibN)
}
}
}

//@tailrec
def fibonacciDoublingFromRecursive3(
/*sequence start*/
a: Int,
b: Int,
elemOrder: Int = 0,
/*must be neutral to operation*/
fibN: Int = 0,
fibN_PlusOne: Int = 0,
result: Int = 0
):
Int = {
assume(elemOrder >= 0, s"'elemOrder' must be positive")
/*
cases:
> even
F(2 * n) = F(n) * (2 * F(n + 1) - F(n))
>> odd
F(2 * n + 1) = F(n + 1)^2 + F(n)^2
*/
/*elemOrder < 2 are base cases
? where actual result was return ?
*/
if (elemOrder == 0) {
a + result
} else if (elemOrder == 1) {
b + result
} else /*if (elemOrder > 1)*/ {
if (elemOrder % 2 != 0) {
//>> odd
val fibN: Int =
fibonacciDoublingFromRecursive2(
a = a,
b = b,
elemOrder = elemOrder / 2
)
val fibN_PlusOne: Int =
fibonacciDoublingFromRecursive2(
a = a,
b = b,
elemOrder = elemOrder / 2 + 1
)

fibN * fibN + fibN_PlusOne * fibN_PlusOne
} else /*if (elemOrder % 2 == 0)*/ {
//> even
val fibN: Int =
fibonacciDoublingFromRecursive2(
/*sequence start*/
a = b,
b = a + b,
elemOrder = elemOrder / 2
)
fibN *
(
2 *
fibonacciDoublingFromRecursive2(
/*sequence start*/
a = b,
b = a + b,
elemOrder = elemOrder / 2 + 1
) -
fibN
)
}
}
}

/*matrix multiplication operation must be implemented / available*/
def Matrix_pow(
//Matrix
A: Stream[Stream[Int]],
/*power*/
p: Int):
Stream[Stream[Int]] =
if (p == 1) {
//return
A
} else if (p % 2 == 1) {
//return
A //* Matrix_pow(A, p - 1)
/*??? else ???*/
} else {
//Matrix
val B: Stream[Stream[Int]] =
Matrix_pow(A, p / 2)
//return
B //* B
}

/*
* Fast doubling method. Faster than the matrix method.
* F(2n) = F(n) * (2*F(n+1) - F(n)).
* F(2n+1) = F(n+1)^2 + F(n)^2.
* This implementation is the non-recursive version. See the web page and
* the other programming language implementations for the recursive version.
*/
def fastFibonacciDoubling(
/*sequence start*/
initA: Int,
initB: Int,
n: Int
): BigInteger = {
var a: BigInteger =
BigInteger
//.ZERO
.valueOf(initA.toLong)
var b: BigInteger =
BigInteger
//.ONE
.valueOf(initB)
var m: Int = 0

for (
//int i = 31 - Integer.numberOfLeadingZeros(n); i >= 0; i--
/*see
http://www.tutorialspoint.com/java/lang/integer_numberofleadingzeros.htm
*/
i <- 31 - Integer.numberOfLeadingZeros(n) to 0 by -1
) {
// Loop invariant: a = F(m), b = F(m+1)
//assert( a.equals(slowFibonacci(m)))
//assert( b.equals(slowFibonacci(m+1)))

// Double it
val d: BigInteger =
a.multiply(
b
.shiftLeft(1)
.subtract(a)
)
val e: BigInteger =
a.multiply(a)
.add(b.multiply(b))
a = d
b = e
m *= 2
//assert( a.equals(slowFibonacci(m)))
//assert( b.equals(slowFibonacci(m+1)))

// Advance by one conditionally
if (((n >>> i) & 1) != 0) {
val c: BigInteger = a.add(b)
a = b
b = c
m += 1
//assert( a.equals(slowFibonacci(m)))
//assert( b.equals(slowFibonacci(m+1)))
}
}
//return
a
}
}
6 changes: 6 additions & 0 deletions src/main/scala/fibonacciFindingEasy/testBigInteger.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import java.math.BigInteger

var a: BigInteger =
BigInteger.ZERO
val b: BigInteger =
BigInteger.valueOf(5)
68 changes: 68 additions & 0 deletions src/main/scala/fibonacciFindingEasy/testInt.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
val n0: Int = 0
val n1: Int = 1
val n2: Int = 2
val n3: Int = 3
val n4: Int = 4
val n5: Int = 5
2 * n0
2 * n0 + 1
2 * n1
2 * n1 + 1
2 * n2
2 * n2 + 1
2 * n3
2 * n3 + 1
2 * n4
2 * n4 + 1
2 * n5
2 * n5 + 1
/*
The
java.lang.Integer.numberOfLeadingZeros() method
returns
the number of
zero bits preceding the highest-order
("leftmost") one-bit in
the two's complement binary representation of
the specified int value.
It returns
'32' if
the specified value has no one-bits
in its two's complement representation,
in other words
if
it is equal to zero.
*/
val str32Zeros: String = "0" * 32
31 - Integer.numberOfLeadingZeros(5)
Integer.numberOfLeadingZeros(0)
val i: Int = 170
"Number = " + i
/* returns
the string representation of
the unsigned integer value
represented by
the argument in binary (base 2) */
"Binary = " + Integer.toBinaryString(i)
// returns the number of one-bits
"Number of one bits = " + Integer.bitCount(i)
/* returns
an int value with
at most a single one-bit,
in the position
of the highest-order ("leftmost") one-bit
in the specified int value */
"Highest one bit = " + Integer.highestOneBit(i)
/* returns
an int value with
at most a single one-bit,
in the position
of the lowest-order ("rightmost") one-bit in
the specified int value.*/
"Lowest one bit = " + Integer.lowestOneBit(i)
/*returns
the number of zero bits
preceding
the highest-order ("leftmost") one-bit */
"Number of leading zeros = " +
Integer.numberOfLeadingZeros(i)
3 changes: 2 additions & 1 deletion src/main/scala/fibonacciFindingEasy/testLong.sc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
val longNumber: Long = Math.pow(10.0,9.0).toLong
val doubleNumber: Double = Math.pow(10.0,9.0)
val intNumber: Int = Math.pow(10.0,9.0).toInt
val intNumber: Int = Math.pow(10.0,9.0).toInt
5.toLong
Loading

0 comments on commit dcaf4b0

Please sign in to comment.