solve_modular_cubic_equation.pl 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. #!/usr/bin/perl
  2. # Author: Trizen
  3. # Date: 04 May 2022
  4. # https://github.com/trizen
  5. # Solve modular quadratic equations of the form:
  6. # a*x^3 + b*x^2 + c*x + d == 0 (mod m)
  7. # Work in progress! Not all solutions are found.
  8. # Sometimes, no solution is found, even if solutions do exist...
  9. # See also:
  10. # https://en.wikipedia.org/wiki/Cubic_equation
  11. use 5.020;
  12. use strict;
  13. use warnings;
  14. use ntheory qw(:all);
  15. use List::Util qw(uniq);
  16. use Math::AnyNum qw(:overload);
  17. use experimental qw(signatures);
  18. sub modular_cubic_equation ($A, $B, $C, $D, $M) {
  19. my $D0 = ($B * $B - 3 * $A * $C) % $M;
  20. my $D1 = (2 * $B**3 - 9 * $A * $B * $C + 27 * $A * $A * $D) % $M;
  21. my @S2 = allsqrtmod(($D1**2 - 4 * $D0**3) % (4 * $M), (4 * $M));
  22. my @S3;
  23. foreach my $s2 (@S2) {
  24. foreach my $r ($D1 + $s2, $D1 - $s2) {
  25. foreach my $s3 (allrootmod(($r / 2) % $M, 3, $M)) {
  26. my $nu = -($B + $s3 + $D0 / $s3) % $M;
  27. my $de = (3 * $A);
  28. my $x = ($nu / $de) % $M;
  29. if (($A * $x**3 + $B * $x**2 + $C * $x + $D) % $M == 0) {
  30. push @S3, $x;
  31. }
  32. }
  33. }
  34. }
  35. return sort { $a <=> $b } uniq(@S3);
  36. }
  37. say join ' ', modular_cubic_equation(5, 3, -12, -640196464320, 432); #=> 261
  38. say join ' ', modular_cubic_equation(1, 1, 1, -10**10 + 42, 10**10); #=> 9709005706
  39. say join ' ', modular_cubic_equation(1, 4, 6, 13 - 10**10, 10**10); #=> 8614398889
  40. say join ' ', modular_cubic_equation(1, 1, 1, -10**10 - 10, 10**10); #=> 8013600910