Indicator of the method of ruby ​​numbers

I solve some problems of Project Euler using Ruby, and specifically here I am talking about problem 25 (What is the index of the first term in a Fibonacci sequence that contains 1000 digits?).

I used Ruby first 2.2.3, and I encoded the problem as such:

number = 3
a = 1
b = 2

while b.to_s.length < 1000
  a, b = b, a + b
  number += 1
end
puts number

But then I found out that the version 2.4.2has a method called digits, which is exactly what I need. I converted the code to:

while b.digits.length < 1000

And when I compared the two methods, it digitswas much slower.

Time

./025/problem025.rb 0.13s user 0.02s system 80% cpu 0.190 total

./025/problem025.rb 2.19s user 0.03s system 97% cpu 2.275 total

Does anyone have an idea why?

+4
source share
4 answers

Ruby digits

Ruby to_s

n 1.585 1 1000 15:

(1..1000).sum { |i| i**2 } / (1..1000).sum { |i| i**1.585 }
=> 15.150583254950678

, . , , , .

GMP, -, / " O (n * log (n)) FFT" .

@Drenmi answer, . , , , Ruby. , : -P

+8

Integer#digits "" . :

, , int.

, base . :

# ruby/numeric.c:4809

while (!FIXNUM_P(num) || FIX2LONG(num) > 0) {
    VALUE qr = rb_int_divmod(num, base);
    rb_ary_push(digits, RARRAY_AREF(qr, 1));
    num = RARRAY_AREF(qr, 0);
}

, , , , .

+1

ruby ​​ (, ..) "".

, to_s , digits , while.

, :

# create the smallest possible 1000 digits number
max = 10**999

number = 3
a = 1
b = 2

# do not create objects in while condition
while b < max
  a, b = b, a + b
  number += 1
end
puts number
0

, , . n .

  • f ( "FNs" ), n .
  • f th (f-1) st FNs, m f th FN.
  • m >= n (f-1) st FN , (f-1) st FN n , f th FN FN, n .
  • m < n f th FN , n , FN n .

f .

AVG_FNs_PER_DIGIT = 4.784971966781667

def first_fibonacci_with_n_digits(n)
  return [1, 1] if n == 1
  idx = (n * AVG_FNs_PER_DIGIT).round
  fn, prev_fn = fib(idx)
  fn.to_s.size >= n ? fib_down(n, fn, prev_fn, idx) : fib_up(n, fn, prev_fn, idx)
end

def fib(idx)
  a = 1
  b = 2
  (idx - 2).times {a, b = b, a + b }
  [b, a] 
end

def fib_up(n, b, a, idx)
  loop do
    a, b = b, a + b
    idx += 1
    break [idx, b] if b.to_s.size == n
  end
end

def fib_down(n, b, a, idx)
  loop do
    a, b = b - a, a
    break [idx, b] if a.to_s.size == n - 1
    idx -= 1
  end
end

:

  • , , ( , "" , );
  • .

, .

n.digits.size n.to_s.size ? , .

def use_to_s(ndigits)
  case ndigits
  when 1
    [1, 1]
  else
    a = 1
    b = 2
    idx = 3
    loop do
      break [idx, b] if b.to_s.length == ndigits
      a, b = b, a + b
      idx += 1
    end
  end
end

def use_digits(ndigits)
  case ndigits
  when 1
    [1, 1]
  else
    a = 1
    b = 2
    idx = 3
    loop do
      break [idx, b] if b.digits.size == ndigits
      a, b = b, a + b
      idx += 1
    end
  end
end

require 'fruity'

def test(ndigits)
  nfibs, last_fib = use_to_s(ndigits)
  puts "\nndigits = #{ndigits}, nfibs=#{nfibs}, last_fib=#{last_fib}"
  compare do
    try_use_to_s   { use_to_s(ndigits) }
    try_use_digits { use_digits(ndigits) }
    try_estimate   { first_fibonacci_with_n_digits(ndigits) }
  end
end

test 20
ndigits = 20, nfibs=93, last_fib=12200160415121876738
Running each test 128 times. Test will take about 1 second.
try_estimate is faster than try_use_to_s by 2x ± 0.1
try_use_to_s is faster than try_use_digits by 80.0% ± 10.0%

test 100
ndigits = 100, nfibs=476, last_fib=13447...37757 (90 digits omitted)
Running each test 16 times. Test will take about 4 seconds.
try_estimate is faster than try_use_to_s by 5x ± 0.1
try_use_to_s is faster than try_use_digits by 10x ± 1.0

test 500
ndigits = 500, nfibs=2390, last_fib=13519...63145 (490 digits omitted)
Running each test 2 times. Test will take about 27 seconds.
try_estimate is faster than try_use_to_s by 9x ± 0.1
try_use_to_s is faster than try_use_digits by 60x ± 1.0

test 1000
ndigits = 1000, nfibs=4782, last_fib=10700...27816 (990 digits omitted)
Running each test once. Test will take about 1 minute.
try_estimate is faster than try_use_to_s by 12x ± 10.0
try_use_to_s is faster than try_use_digits by 120x ± 100.0

:

  • "try_estimate" , ;
  • to_s , digits.

, FN, , , :

  • 20 : 96 93
  • 100 : 479 476
  • 500 : 2392 2390
  • 1000 : 4785 4782

3, 3 FN .

, " ", - AVG_FNs_PER_DIGIT, FN, .

. (Wiki FN.)

, 7 FN ( ) ; FN 4 5 FN (.. 4, 5). , , , FN n n >= 2 , 4*n th FN. n = 1000 4000. (, 4882- - , 1000 .) , 4000 FN. .

n , 10**n...10**(n+1) (n - ), 5 FN, , 4 FN, .

LOG_10 = Math.log(10)
  #=> 2.302585092994046
GR = (1 + Math.sqrt(5))/2
  #=> 1.618033988749895
LOG_GR = Math.log(GR)
  #=> 0.48121182505960347

RATIO_5to4 = (LOG_10 - 4*LOG_GR)/(5*LOG_GR - LOG_10)
  #=> 3.6505564183095474

GR - .

n- n 4 , 4 FN, n 5 - , 5 FN. FN (n 4 * 4 + n 5 * 5)/(n 4 + n 5 > ). n 5/n 4 RATIO_5to4, n 5 RATIO_5to4 * n 4 ( ). n 5,

b = 1/(1 + RATIO_5to4)
  #=> 0.21502803321833364

, FN n-

avg = b * 4 + (1-b) *5
  #=> 4.784971966781667

If it fnis the first FN having decimal numbers n, the number of FNs in the sequence before switching on fnmay be close to

n * avg

If, for example, the index score of the first FN with 1000 decimal digits will be 1000 * 4.784971966781667).round #=> 4785.

0
source

Source: https://habr.com/ru/post/1691274/


All Articles