inverse_of_factorial.pl 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 18 July 2016
  4. # Edit: 23 October 2017
  5. # https://github.com/trizen
  6. # Compute the inverse of n-factorial.
  7. # The function is defined only for factorial numbers.
  8. # It may return non-sense for non-factorials.
  9. # See also:
  10. # https://oeis.org/A090368
  11. use 5.010;
  12. use strict;
  13. use warnings;
  14. use experimental qw(signatures);
  15. use ntheory qw(valuation factor factorial);
  16. sub factorial_prime_pow ($n, $p) {
  17. my $count = 0;
  18. my $ppow = $p;
  19. while ($ppow <= $n) {
  20. $count += int($n / $ppow);
  21. $ppow *= $p;
  22. }
  23. return $count;
  24. }
  25. sub p_adic_inverse ($p, $k) {
  26. my $n = $k * ($p - 1);
  27. while (factorial_prime_pow($n, $p) < $k) {
  28. $n -= $n % $p;
  29. $n += $p;
  30. }
  31. return $n;
  32. }
  33. sub inverse_of_factorial ($f) {
  34. return 1 if $f == 1;
  35. my $t = valuation($f, 2); # largest power of 2 in f
  36. my $z = p_adic_inverse(2, $t); # smallest number z such that 2^t divides z!
  37. my $d = (factor($z + 1))[-1]; # largest factor of z+1
  38. if (valuation($f, $d) != factorial_prime_pow($z + 1, $d)) {
  39. return $z;
  40. }
  41. return $z + 1;
  42. }
  43. foreach my $n (1 .. 30) {
  44. my $f = factorial($n);
  45. my $i = inverse_of_factorial($f);
  46. say "$i! = $f";
  47. if ($i != $n) {
  48. die "error: $i != $n";
  49. }
  50. }