Talk:Fibonacci matrix-exponentiation

From Rosetta Code

Clarify Task

The task to implement fib(n) using matrix multiplication is a great task. However, given that fib(2^32) has hundreds of million of digits, the task is unachievable. Many implementations use fibMod for the last digits, and another methodology for the first digits. Recommend a selecting smaller values of n, and sticking to the task of fib(n) using matrix multiplication, not fibMod or other implementations.

--DavidFashion (talk) 00:04, 13 February 2020 (UTC)

I will add fib(2^16) in the main task. If only fib(2^64) is not calculated, the task is considered accomplished.

The goal of the task is to show that the iterative method is slow to calculate large fibonacci numbers. Smaller values like fib(2^16) is easily found using the iterative method.

I think fib(2^32) is reachable using matrix exponentiation and even if it remains slow, Julia solution shows a faster method using Lucas sequence.

I confess fib(2^64) seems unreachable using matrix exponentiation. Nevertheless it allowed to see some creative solution like the Sidef solution using fibmod.

Ruby

Ruby's fast exponentiation operator duck-typing applied to Ruby's built-in Integer Class.

require 'matrix'
start_time = Time.now
[10,100,256, 1_000, 1024, 10_000, 2 ** 16, 100_000, 1_000_000,10_000_000 ].each {|n|
 fib_Num=(Matrix[[0,1],[1,1]] ** (n))[0,1]
 fib_Str= fib_Num.to_s()
 if fib_Str.length <= 21
   p [n,fib_Str.length.to_s + ' digits' , fib_Str,  "Took #{Time.now - start_time}s"]
 else
   p [n,fib_Str.length.to_s + ' digits' , fib_Str.slice(0,20) + " ... " + fib_Str[-20 ... -1] , "Took #{Time.now - start_time}s"]
 end
}
puts  "Took #{Time.now - start_time}s"

Ruby's built-in Integer Class outputs:

[10, "2 digits", "55", "Took 6.836e-05s"]
[100, "21 digits", "354224848179261915075", "Took 0.000259665s"]
[256, "54 digits", "14169381771405651323 ... 1965770779495819986", "Took 0.000390585s"]
[1000, "209 digits", "43466557686937456435 ... 7613779516684922887", "Took 0.000576685s"]
[1024, "214 digits", "45066996336778198131 ... 0410363155392540524", "Took 0.001018916s"]
[10000, "2090 digits", "33644764876431783266 ... 6607331005994736687", "Took 0.001502226s"]
[65536, "13696 digits", "73199214460290552832 ... 9727019095530746322", "Took 0.003220941s"]
[100000, "20899 digits", "25974069347221724166 ... 4989537465342874687", "Took 0.006312s"]
[1000000, "208988 digits", "19532821287077577316 ... 6899652683824254687", "Took 0.076104599s"]
[10000000, "2089877 digits", "11298343782253997603 ... 8699867368638054687", "Took 1.112832817s"]
Took 1.112902545s

Ruby's Matlix duck-typeing with Head-tail-BigNumber class

Here, the HeadTailBignum class holds the digits of the head part in Ruby's bigDecimal class and tail part in the BigInteger class. and HeadTailBignum defines only the add and multply operators and the coerce method.

Matlix exponentiation operator is fast and can apply duck-typing to HeadTailBignum.

require 'matrix'
require 'bigdecimal'

class HeadTailBignum < Numeric
	attr_accessor :hi, :lo
   @@degitsLimit = 20
   @@hiDegitsLimit = (@@degitsLimit + 1) * 2
	@@loDegitsBase = 10 ** @@degitsLimit
	BigDecimal::limit(@@hiDegitsLimit)
   def initialize(other, loValue = nil)
   	if other.kind_of?(BigDecimal) && loValue.kind_of?(Integer)
   		@hi = other
   		@lo = loValue % @@loDegitsBase
   	elsif other.kind_of?(HeadTailBignum)
   		@hi = other.hi
   		@lo = other.lo
   	elsif other.kind_of?(Integer)
   		@hi = BigDecimal(other)
   		@lo = other % @@loDegitsBase
   	else
           raise StandardError.new("HeadTailBignum initialize Type Error (" + other.class.to_s + "," + loValue.class.to_s + ")")    		
   	end	
   end
   def clone
   	HeadTailBignum(self.hi, self.lo)
   end

   def integer?()
   	true
   end
	def coerce(other)
		if other.kind_of?(Integer)
          return HeadTailBignum.new(other), self
		else
          super
		end
   end

   def +(other)
   	if other.kind_of?(Integer)
   		return HeadTailBignum.new(self.hi + other, self.lo + other)
   	elsif other.kind_of?(HeadTailBignum)
   	    return HeadTailBignum.new(self.hi + other.hi , self.lo + other.lo)
   	else
           raise StandardError.new("HeadTailBignum add Type Error (" + other.class.to_s + ")")    		
       end
   end
   def *(other)
   	if other.kind_of?(Integer)
   		return HeadTailBignum.new(self.hi * other, self.lo * other)
   	elsif other.kind_of?(HeadTailBignum)
  	    	return HeadTailBignum.new(self.hi * other.hi , self.lo * other.lo)
   	else
           raise StandardError.new("HeadTailBignum mulply Type Error (" + other.class.to_s + ")")    		
       end

   end

   def exponent
   	@hi.exponent
   end
   def to_s
   	if @hi < @@loDegitsBase
   		return @lo.inspect
   	else
   		return @hi.to_s("E").slice(2,@@degitsLimit ) + " ... " + @lo.to_s
       end
   end
end

start_time = Time.now
[8,16,32,64].each {|h|
   n = (2**h)
   fib_Num=(Matrix[[ HeadTailBignum.new(0),1],[1,1]] ** (n))[0,1]
   puts "Fibonacci(2^#{h.to_s}) = #{fib_Num.to_s} are #{fib_Num.exponent} degits"  
}
puts "Took #{Time.now - start_time}s"

Outputs HeadTailBignum:

Fibonacci(2^8) = 14169381771405651323 ... 19657707794958199867 are 54 degits
Fibonacci(2^16) = 73199214460290552832 ... 97270190955307463227 are 13696 degits
Fibonacci(2^32) = 61999319689381859818 ... 39623735538208076347 are 897595080 degits
Fibonacci(2^64) = 11175807536929528424 ... 17529800348089840187 are 609335284 degits
Took 0.009357194s