modular_fibonacci_cassini_fast.pl 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. #!/usr/bin/perl
  2. # An efficient algorithm for computing the nth-Fibonacci number (mod m).
  3. # Algorithm from:
  4. # https://metacpan.org/source/KRYDE/Math-NumSeq-72/lib/Math/NumSeq/Fibonacci.pm
  5. # See also:
  6. # https://en.wikipedia.org/wiki/Fibonacci_number
  7. use 5.020;
  8. use warnings;
  9. use experimental qw(signatures);
  10. use Math::GMPz;
  11. use Math::Prime::Util::GMP qw(gcd consecutive_integer_lcm);
  12. sub fibmod ($n, $m) {
  13. $n = Math::GMPz->new("$n");
  14. $m = Math::GMPz->new("$m");
  15. my ($f, $g, $w) = (
  16. Math::GMPz::Rmpz_init_set_ui(0),
  17. Math::GMPz::Rmpz_init_set_ui(1),
  18. );
  19. my $t = Math::GMPz::Rmpz_init();
  20. foreach my $bit (split(//, substr(Math::GMPz::Rmpz_get_str($n, 2), 1))) {
  21. Math::GMPz::Rmpz_powm_ui($g, $g, 2, $m);
  22. Math::GMPz::Rmpz_powm_ui($f, $f, 2, $m);
  23. Math::GMPz::Rmpz_mul_2exp($t, $g, 2);
  24. Math::GMPz::Rmpz_sub($t, $t, $f);
  25. $w
  26. ? Math::GMPz::Rmpz_add_ui($t, $t, 2)
  27. : Math::GMPz::Rmpz_sub_ui($t, $t, 2);
  28. Math::GMPz::Rmpz_add($f, $f, $g);
  29. if ($bit) {
  30. Math::GMPz::Rmpz_sub($f, $t, $f);
  31. Math::GMPz::Rmpz_set($g, $t);
  32. $w = 0;
  33. }
  34. else {
  35. Math::GMPz::Rmpz_sub($g, $t, $f);
  36. $w = 1;
  37. }
  38. }
  39. Math::GMPz::Rmpz_mod($g, $g, $m);
  40. return $g;
  41. }
  42. sub fibonacci_factorization ($n, $B = 10000) {
  43. my $k = consecutive_integer_lcm($B); # lcm(1..B)
  44. my $F = fibmod($k, $n); # Fibonacci(k) (mod n)
  45. return gcd($F, $n);
  46. }
  47. say fibonacci_factorization("121095274043", 700); #=> 470783 (p+1 is 700-smooth)
  48. say fibonacci_factorization("544812320889004864776853", 3000); #=> 333732865481 (p-1 is 3000-smooth)