fibonacci_encoding.pl 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 22 January 2018
  4. # https://github.com/trizen
  5. # Encode positive integers in binary format, using the Fibonacci numbers.
  6. # Example:
  7. # 30 = 10100010 = 1×21 + 0×13 + 1×8 + 0×5 + 0×3 + 0×2 + 1×1 + 0×1
  8. # See also:
  9. # https://projecteuler.net/problem=473
  10. # https://en.wikipedia.org/wiki/Fibonacci_coding
  11. # https://en.wikipedia.org/wiki/Zeckendorf%27s_theorem
  12. # https://en.wikipedia.org/wiki/Golden_ratio_base
  13. use 5.010;
  14. use strict;
  15. use warnings;
  16. use ntheory qw(lucasu);
  17. use experimental qw(signatures);
  18. sub fib ($n) {
  19. lucasu(1, -1, $n);
  20. }
  21. sub fibonacci_encoding ($n) {
  22. return '0' if ($n == 0);
  23. my $phi = sqrt(1.25) + 0.5;
  24. my $log = int((log($n) + log(5)/2) / log($phi));
  25. my ($f1, $f2) = (fib($log), fib($log - 1));
  26. if ($f1 + $f2 <= $n) {
  27. ($f1, $f2) = ($f1 + $f2, $f1);
  28. }
  29. my $enc = '';
  30. while ($f1 > 0) {
  31. if ($n >= $f1) {
  32. $n -= $f1;
  33. $enc .= '1';
  34. }
  35. else {
  36. $enc .= '0';
  37. }
  38. ($f1, $f2) = ($f2, $f1 - $f2);
  39. }
  40. return $enc;
  41. }
  42. sub fibonacci_decoding($enc) {
  43. my $len = length($enc);
  44. my ($f1, $f2) = (fib($len), fib($len - 1));
  45. my $dec = 0;
  46. foreach my $i (0 .. $len - 1) {
  47. my $bit = substr($enc, $i, 1);
  48. $dec += $f1 if $bit;
  49. ($f1, $f2) = ($f2, $f1 - $f2);
  50. }
  51. return $dec;
  52. }
  53. say fibonacci_encoding(30); #=> 10100010
  54. say fibonacci_decoding('10100010'); #=> 30
  55. say fibonacci_decoding(fibonacci_encoding(144)); #=> 144
  56. say fibonacci_decoding(fibonacci_encoding(144 - 1)); #=> 143
  57. say fibonacci_decoding(fibonacci_encoding(144 + 1)); #=> 145
  58. # Transparent support for arbitrary large integers
  59. say fibonacci_decoding(fibonacci_encoding('81923489126412312421758612841248123'));
  60. # Verify the encoding/decoding algorithm
  61. foreach my $n (0 .. 10000) {
  62. if (fibonacci_decoding(fibonacci_encoding($n)) != $n) {
  63. die "Error for $n";
  64. }
  65. }