inverse_of_sigma_function_fast.pl 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #!/usr/bin/perl
  2. # Given a positive integer `n`, this algorithm finds all the numbers k
  3. # such that sigma(k) = n, where `sigma(k)` is the sum of divisors of `k`.
  4. # Based on "invphi.gp" code by Max Alekseyev.
  5. # See also:
  6. # https://home.gwu.edu/~maxal/gpscripts/
  7. use utf8;
  8. use 5.020;
  9. use strict;
  10. use warnings;
  11. use Math::Prime::Util::GMP qw(:all);
  12. use List::Util qw(uniq);
  13. use experimental qw(signatures);
  14. binmode(STDOUT, ':utf8');
  15. sub inverse_sigma {
  16. my ($n) = @_;
  17. my %cache;
  18. my %factor_cache;
  19. my %divisor_cache;
  20. my $results = sub ($n, $m) {
  21. return [1] if ($n == 1);
  22. my $key = "$n $m";
  23. if (exists $cache{$key}) {
  24. return $cache{$key};
  25. }
  26. my (@R, @D);
  27. $divisor_cache{$n} //= [divisors($n)];
  28. foreach my $d (@{$divisor_cache{$n}}) {
  29. if ($d >= $m) {
  30. push @D, $d;
  31. $factor_cache{$d} //= do {
  32. my %factors;
  33. @factors{factor(subint($D[-1], 1))} = ();
  34. [keys %factors];
  35. };
  36. }
  37. }
  38. foreach my $d (@D) {
  39. foreach my $p (@{$factor_cache{$d}}) {
  40. my $r = addint(mulint($d, subint($p, 1)), 1);
  41. my $k = valuation($r, $p) - 1;
  42. next if ($k < 1);
  43. my $s = powint($p, $k + 1);
  44. next if ("$r" ne "$s");
  45. my $z = powint($p, $k);
  46. my $u = divint($n, $d);
  47. my $arr = __SUB__->($u, $d);
  48. foreach my $v (@$arr) {
  49. if (modint($v, $p) != 0) {
  50. push @R, mulint($v, $z);
  51. }
  52. }
  53. }
  54. }
  55. $cache{$key} = \@R;
  56. }->($n, 3);
  57. sort { $a <=> $b } uniq(@$results);
  58. }
  59. my %tests = (
  60. 6 => 6187272, 10 => 196602, 11 => 8105688, 16 => 2031554,
  61. 25 => 1355816, 31 => 8880128, 80 => 11532, 97 => 5488,
  62. );
  63. while (my ($n, $k) = each %tests) {
  64. my @inverse = inverse_sigma($k);
  65. say "σ−¹($k) = [@inverse]";
  66. if (gcd(@inverse) != $n) {
  67. die "Error for k = $k";
  68. }
  69. }
  70. use Test::More;
  71. plan tests => 4;
  72. is(join(' ', inverse_sigma(42)), join(' ', 20, 26, 41));
  73. is(join(' ', inverse_sigma(7688)), join(' ', 2800, 2928, 4575, 7687));
  74. is(join(' ', inverse_sigma("110680464442257309690")), "46116860184273879040");
  75. is(join(' ', inverse_sigma("9325257382230393314439814176")), "3535399776779654608221686964 4302950338161146561477374638 4637009852153025247015401018 4661529533007908774933879778 4884658628787348878992283572 5187814889839710566412258045 5311639278156872382400698772 5326520187917077557965023252 5328493035801953244119300732 5495240957385767488866317781 6208298641832871739558373002 6411114450283395403677372213 6417519160023938256228496989 6454455748546107757077838269 6799666841209661791779031135 6938435552254756930386764875 6992294299511863162400845113 7215972974344947207602237095 8501184728947212952861568533 8546137477181166087378779593 9130981186767260120388984667 9214242413394317203553625829 9323102747899933426890262757 9325091641128050246166715829 9325201015147968294835238387");
  76. __END__
  77. σ−¹(6187272) = [2855646 2651676]
  78. σ−¹(196602) = [105650 81920]
  79. σ−¹(8105688) = [4953454 4947723]
  80. σ−¹(2031554) = [845200 999424]
  81. σ−¹(8880128) = [6389751 7527079]
  82. σ−¹(5488) = [3783 2716]
  83. σ−¹(11532) = [4880 4400]
  84. σ−¹(1355816) = [457500 390000 811875 624700]