digits_to_number_subquadratic_algorithm_mpz.pl 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. #!/usr/bin/perl
  2. # Subquadratic algorithm for converting a given list of digits in a given base, to an integer.
  3. # Algorithm presented in the book:
  4. #
  5. # Modern Computer Arithmetic
  6. # - by Richard P. Brent and Paul Zimmermann
  7. #
  8. use 5.036;
  9. use Math::GMPz;
  10. use ntheory qw(:all);
  11. sub FastIntegerInput ($digits, $base = 10) {
  12. my $L = [map { Math::GMPz->new("$_") } reverse @$digits];
  13. my $B = Math::GMPz->new("$base");
  14. # Subquadratic Algorithm 1.25 FastIntegerInput from "Modern Computer Arithmetic v0.5.9"
  15. for (my $k = scalar(@$L) ; $k > 1 ; $k = ($k >> 1) + ($k & 1)) {
  16. my @T;
  17. for (0 .. ($k >> 1) - 1) {
  18. my $t = Math::GMPz::Rmpz_init_set($L->[2 * $_]);
  19. Math::GMPz::Rmpz_addmul($t, $L->[2 * $_ + 1], $B);
  20. push(@T, $t);
  21. }
  22. push(@T, $L->[-1]) if ($k & 1);
  23. $L = \@T;
  24. Math::GMPz::Rmpz_mul($B, $B, $B);
  25. }
  26. return $L->[0];
  27. }
  28. foreach my $B (2 .. 100) { # run some tests
  29. my $N = factorial($B); # int(rand(~0));
  30. my @a = todigits($N, $B);
  31. my $K = FastIntegerInput(\@a, $B);
  32. if ($N != $K) {
  33. die "Error for N = $N -> got $K";
  34. }
  35. }
  36. say join ', ', FastIntegerInput([todigits(5040, 10)], 10); #=> 5040
  37. say join ', ', FastIntegerInput([todigits(5040, 11)], 11); #=> 5040
  38. say join ', ', FastIntegerInput([todigits(5040, 12)], 12); #=> 5040
  39. say join ', ', FastIntegerInput([todigits(5040, 13)], 13); #=> 5040