inverse_of_multiplicative_functions.pl 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #!/usr/bin/perl
  2. # Computing the inverse of some multiplicative functions.
  3. # Translation of invphi.gp ver. 2.1 by Max Alekseyev.
  4. # See also:
  5. # https://home.gwu.edu/~maxal/gpscripts/
  6. use utf8;
  7. use 5.020;
  8. use strict;
  9. use warnings;
  10. use ntheory qw(:all);
  11. use experimental qw(signatures);
  12. sub dynamicPreimage ($N, $L, %opt) {
  13. my %r = (1 => [1]);
  14. foreach my $l (@$L) {
  15. my %t;
  16. foreach my $pair (@$l) {
  17. my ($x, $y) = @$pair;
  18. foreach my $d (divisors(divint($N, $x))) {
  19. if (exists $r{$d}) {
  20. my @list = @{$r{$d}};
  21. if ($opt{unitary}) {
  22. @list = grep { gcd($_, $y) == 1 } @list;
  23. }
  24. push @{$t{mulint($x, $d)}}, map { mulint($_, $y) } @list;
  25. }
  26. }
  27. }
  28. while (my ($k, $v) = each %t) {
  29. push @{$r{$k}}, @$v;
  30. }
  31. }
  32. return if !exists $r{$N};
  33. sort { $a <=> $b } @{$r{$N}};
  34. }
  35. sub dynamicLen ($N, $L) {
  36. my %r = (1 => 1);
  37. foreach my $l (@$L) {
  38. my %t;
  39. foreach my $pair (@$l) {
  40. my ($x, $y) = @$pair;
  41. foreach my $d (divisors(divint($N, $x))) {
  42. if (exists $r{$d}) {
  43. $t{mulint($x, $d)} += $r{$d};
  44. }
  45. }
  46. }
  47. while (my ($k, $v) = each %t) {
  48. $r{$k} += $v;
  49. }
  50. }
  51. $r{$N} // 0;
  52. }
  53. sub dynamicMin ($N, $L) {
  54. my %r = (1 => 1);
  55. foreach my $l (@$L) {
  56. my %t;
  57. foreach my $pair (@$l) {
  58. my ($x, $y) = @$pair;
  59. foreach my $d (divisors(divint($N, $x))) {
  60. if (exists $r{$d}) {
  61. my $k = mulint($x, $d);
  62. my $v = $r{$d} * $y;
  63. if (not defined($t{$k})) {
  64. $t{$k} = $v;
  65. }
  66. else {
  67. $t{$k} = $v if ($v < $t{$k});
  68. }
  69. }
  70. }
  71. }
  72. while (my ($k, $v) = each %t) {
  73. if (not defined($r{$k})) {
  74. $r{$k} = $v;
  75. }
  76. else {
  77. $r{$k} = $v if ($v < $r{$k});
  78. }
  79. }
  80. }
  81. $r{$N};
  82. }
  83. sub dynamicMax ($N, $L) {
  84. my %r = (1 => 1);
  85. foreach my $l (@$L) {
  86. my %t;
  87. foreach my $pair (@$l) {
  88. my ($x, $y) = @$pair;
  89. foreach my $d (divisors(divint($N, $x))) {
  90. if (exists $r{$d}) {
  91. my $k = mulint($x, $d);
  92. my $v = $r{$d} * $y;
  93. if (not defined($t{$k})) {
  94. $t{$k} = $v;
  95. }
  96. else {
  97. $t{$k} = $v if ($v > $t{$k});
  98. }
  99. }
  100. }
  101. }
  102. while (my ($k, $v) = each %t) {
  103. if (not defined($r{$k})) {
  104. $r{$k} = $v;
  105. }
  106. else {
  107. $r{$k} = $v if ($v > $r{$k});
  108. }
  109. }
  110. }
  111. $r{$N};
  112. }
  113. sub cook_sigma ($N, $k) {
  114. my %L;
  115. foreach my $d (divisors($N)) {
  116. next if ($d == 1);
  117. foreach my $p (map { $_->[0] } factor_exp(subint($d, 1))) {
  118. my $q = addint(mulint($d, subint(powint($p, $k), 1)), 1);
  119. my $t = valuation($q, $p);
  120. next if ($t <= $k or ($t % $k) or $q != powint($p, $t));
  121. push @{$L{$p}}, [$d, powint($p, subint(divint($t, $k), 1))];
  122. }
  123. }
  124. [values %L];
  125. }
  126. sub cook_phi ($N) {
  127. my %L;
  128. foreach my $d (divisors($N)) {
  129. my $p = addint($d, 1);
  130. is_prime($p) || next;
  131. my $v = valuation($N, $p);
  132. push @{$L{$p}}, map { [mulint($d, powint($p, $_ - 1)), powint($p, $_)] } 1 .. $v + 1;
  133. }
  134. [values %L];
  135. }
  136. sub cook_psi ($N) {
  137. my %L;
  138. foreach my $d (divisors($N)) {
  139. my $p = subint($d, 1);
  140. is_prime($p) || next;
  141. my $v = valuation($N, $p);
  142. push @{$L{$p}}, map { [mulint($d, powint($p, $_ - 1)), powint($p, $_)] } 1 .. $v + 1;
  143. }
  144. [values %L];
  145. }
  146. sub cook_usigma ($N) {
  147. my @list;
  148. foreach my $d (divisors($N)) {
  149. if (is_prime_power(subint($d, 1))) {
  150. push @list, [[$d, subint($d, 1)]];
  151. }
  152. }
  153. return \@list;
  154. }
  155. sub cook_uphi ($N) {
  156. my @list;
  157. foreach my $d (divisors($N)) {
  158. if (is_prime_power(addint($d, 1))) {
  159. push @list, [[$d, addint($d, 1)]];
  160. }
  161. }
  162. return \@list;
  163. }
  164. # Inverse of sigma function
  165. sub inverse_sigma ($N, $k = 1) {
  166. dynamicPreimage($N, cook_sigma($N, $k));
  167. }
  168. sub inverse_sigma_min ($N, $k = 1) {
  169. dynamicMin($N, cook_sigma($N, $k));
  170. }
  171. sub inverse_sigma_max ($N, $k = 1) {
  172. dynamicMax($N, cook_sigma($N, $k));
  173. }
  174. sub inverse_sigma_len ($N, $k = 1) {
  175. dynamicLen($N, cook_sigma($N, $k));
  176. }
  177. # Inverse of Euler phi function
  178. sub inverse_phi ($N) {
  179. dynamicPreimage($N, cook_phi($N));
  180. }
  181. sub inverse_phi_min ($N) {
  182. dynamicMin($N, cook_phi($N));
  183. }
  184. sub inverse_phi_max ($N) {
  185. dynamicMax($N, cook_phi($N));
  186. }
  187. sub inverse_phi_len ($N) {
  188. dynamicLen($N, cook_phi($N));
  189. }
  190. # Inverse of Dedekind psi function
  191. sub inverse_psi ($N) {
  192. dynamicPreimage($N, cook_psi($N));
  193. }
  194. sub inverse_psi_min ($N) {
  195. dynamicMin($N, cook_psi($N));
  196. }
  197. sub inverse_psi_max ($N) {
  198. dynamicMax($N, cook_psi($N));
  199. }
  200. sub inverse_psi_len ($N) {
  201. dynamicLen($N, cook_psi($N));
  202. }
  203. # Inverse of unitary sigma function
  204. sub inverse_usigma ($N) {
  205. dynamicPreimage($N, cook_usigma($N), unitary => 1);
  206. }
  207. # Inverse of unitary phi function
  208. sub inverse_uphi ($N) {
  209. dynamicPreimage($N, cook_uphi($N), unitary => 1);
  210. }
  211. ## Usage example
  212. say join ', ', inverse_sigma(120); #=> [54, 56, 87, 95]
  213. say join ', ', inverse_usigma(120); #=> [60, 87, 92, 95, 99]
  214. say join ', ', inverse_uphi(120); #=> [121, 143, 144, 155, 164, 183, 220, 231, 240, 242, 286, 310, 366, 462]
  215. say join ', ', inverse_phi(120); #=> [143, 155, 175, 183, 225, 231, 244, 248, 286, 308, 310, 350, 366, 372, 396, 450, 462]
  216. say join ', ', inverse_psi(120); #=> [75, 76, 87, 95]
  217. say join ', ', inverse_sigma(22100, 2); #=> [120, 130, 141]
  218. say '';
  219. say inverse_sigma_min(factorial(10)); #=> 876960
  220. say inverse_sigma_max(factorial(10)); #=> 3624941
  221. say inverse_sigma_len(factorial(10)); #=> 1195
  222. say '';
  223. say inverse_phi_min(factorial(10)); #=> 3632617
  224. say inverse_phi_max(factorial(10)); #=> 19969950
  225. say inverse_phi_len(factorial(10)); #=> 3802
  226. say '';
  227. say inverse_psi_min(factorial(10)); #=> 1160250
  228. say inverse_psi_max(factorial(10)); #=> 3624941
  229. say inverse_psi_len(factorial(10)); #=> 1793
  230. say '';