sum_of_digits_subquadratic_algorithm_mpz.pl 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. #!/usr/bin/perl
  2. # Subquadratic algorithm for computing the sum of digits of a given integer in a given base.
  3. # Based on the FastIntegerOutput algorithm presented in the book:
  4. #
  5. # Modern Computer Arithmetic
  6. # - by Richard P. Brent and Paul Zimmermann
  7. #
  8. use 5.020;
  9. use strict;
  10. use warnings;
  11. use Math::GMPz;
  12. use ntheory qw(:all);
  13. use experimental qw(signatures);
  14. sub FastSumOfDigits ($A, $B) {
  15. $A = Math::GMPz->new("$A");
  16. # Find k such that B^(2k - 2) <= A < B^(2k)
  17. my $k = (logint($A, $B) >> 1) + 1;
  18. my $Q = Math::GMPz::Rmpz_init();
  19. my $R = Math::GMPz::Rmpz_init();
  20. sub ($A, $k) {
  21. if (Math::GMPz::Rmpz_cmp_ui($A, $B) < 0) {
  22. return Math::GMPz::Rmpz_get_ui($A);
  23. }
  24. my $w = ($k + 1) >> 1;
  25. my $t = Math::GMPz::Rmpz_init();
  26. Math::GMPz::Rmpz_ui_pow_ui($t, $B, $k);
  27. Math::GMPz::Rmpz_divmod($Q, $R, $A, $t);
  28. Math::GMPz::Rmpz_set($t, $Q);
  29. __SUB__->($R, $w) + __SUB__->($t, $w);
  30. }->($A, $k);
  31. }
  32. foreach my $B (2 .. 300) { # run some tests
  33. my $N = factorial($B); # int(rand(~0));
  34. my $x = vecsum(todigits($N, $B));
  35. my $y = FastSumOfDigits($N, $B);
  36. if ($x != $y) {
  37. die "Error for FastSumOfDigits($N, $B): $x != $y";
  38. }
  39. }
  40. say join ', ', FastSumOfDigits(5040, 10); #=> 9
  41. say join ', ', FastSumOfDigits(5040, 11); #=> 20
  42. say join ', ', FastSumOfDigits(5040, 12); #=> 13
  43. say join ', ', FastSumOfDigits(5040, 13); #=> 24