754 Product of Gauss Factorials.pl 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #!/usr/bin/perl
  2. # Product of Gauss Factorials
  3. # https://projecteuler.net/problem=754
  4. # See also:
  5. # https://oeis.org/A001783
  6. # Using the identity:
  7. # A001783(n) = A250269(n) / A193679(n)
  8. # = (Prod_{d|n} (d!)^mu(n/d)) / (Prod_{p|n} p^(phi(n) / (p-1)))
  9. # WARNING: requires about 5.2 GB of RAM!
  10. # Runtime: ~25 minutes.
  11. use 5.020;
  12. use strict;
  13. use warnings;
  14. use ntheory qw(:all);
  15. use List::Util qw(reduce uniq);
  16. use experimental qw(signatures);
  17. sub F ($n, $m) {
  18. my $prod1 = 1;
  19. my $prod2 = 1;
  20. my $nfac = 1;
  21. my @facmod = (1);
  22. say ":: Building factorial table for n = $n";
  23. foreach my $k (1 .. $n) {
  24. $nfac = mulmod($nfac, $k, $m);
  25. push @facmod, $nfac;
  26. }
  27. say ":: Computing the product of Gaussian factorials...";
  28. forfactored {
  29. my $k = $_;
  30. if (is_prime($k)) {
  31. $prod1 = mulmod($prod1, $facmod[$k - 1], $m);
  32. }
  33. else {
  34. my $phi = euler_phi($k);
  35. $prod1 = mulmod(
  36. $prod1,
  37. (
  38. reduce { mulmod($a, $b, $m) }
  39. map { powmod($facmod[divint($k, $_)], moebius($_), $m) } divisors(vecprod(uniq(@_)))
  40. ),
  41. $m
  42. );
  43. $prod2 = mulmod(
  44. $prod2,
  45. (
  46. reduce { mulmod($a, $b, $m) }
  47. map { powmod($_, divint($phi, $_ - 1), $m) } uniq(@_)
  48. ),
  49. $m
  50. );
  51. }
  52. } 2, $n;
  53. return divmod($prod1, $prod2, $m);
  54. }
  55. use Test::More tests => 6;
  56. is(F(10, next_prime(powint(10, 20))), 23044331520000);
  57. is(F(10, 1_000_000_007), 331358692);
  58. is(F(1e2, 1_000_000_007), 777776709);
  59. is(F(1e3, 1_000_000_007), 297877340);
  60. is(F(1e4, 1_000_000_007), 517055464);
  61. is(F(1e5, 1_000_000_007), 516871211);
  62. #say F(1e3, 1000000007); # takes 0.07s
  63. #say F(1e4, 1000000007); # takes 0.4s
  64. #say F(1e5, 1000000007); # takes 2s
  65. #say F(1e6, 1000000007); # takes 15s
  66. #say F(1e7, 1000000007); # takes 2.5 minutes
  67. say "\n:: Computing: G(10^8)";
  68. say F(1e8, 1000000007);