k-powerful_numbers_in_range.pl 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. #!/usr/bin/perl
  2. # Author: Trizen
  3. # Date: 28 February 2021
  4. # Edit: 11 April 2024
  5. # https://github.com/trizen
  6. # Fast recursive algorithm for generating all the k-powerful numbers in a given range [A,B].
  7. # A positive integer n is considered k-powerful, if for every prime p that divides n, so does p^k.
  8. # Example:
  9. # 2-powerful = a^2 * b^3, for a,b >= 1
  10. # 3-powerful = a^3 * b^4 * c^5, for a,b,c >= 1
  11. # 4-powerful = a^4 * b^5 * c^6 * d^7, for a,b,c,d >= 1
  12. # OEIS:
  13. # https://oeis.org/A001694 -- 2-powerful numbers
  14. # https://oeis.org/A036966 -- 3-powerful numbers
  15. # https://oeis.org/A036967 -- 4-powerful numbers
  16. # https://oeis.org/A069492 -- 5-powerful numbers
  17. # https://oeis.org/A069493 -- 6-powerful numbers
  18. use 5.036;
  19. use ntheory qw(:all);
  20. sub divceil ($x, $y) { # ceil(x/y)
  21. (($x % $y == 0) ? 0 : 1) + divint($x, $y);
  22. }
  23. sub powerful_numbers ($A, $B, $k = 2) {
  24. my @powerful;
  25. sub ($m, $r) {
  26. my $from = 1;
  27. my $upto = rootint(divint($B, $m), $r);
  28. if ($r <= $k) {
  29. if ($A > $m) {
  30. # Optimization by Dana Jacobsen (from Math::Prime::Util::PP)
  31. my $l = divceil($A, $m);
  32. if (($l >> $r) == 0) {
  33. $from = 2;
  34. }
  35. else {
  36. $from = rootint($l, $r);
  37. $from++ if (powint($from, $r) != $l);
  38. }
  39. }
  40. foreach my $j ($from .. $upto) {
  41. push @powerful, mulint($m, powint($j, $r));
  42. }
  43. return;
  44. }
  45. foreach my $v ($from .. $upto) {
  46. gcd($m, $v) == 1 or next;
  47. is_square_free($v) or next;
  48. __SUB__->(mulint($m, powint($v, $r)), $r - 1);
  49. }
  50. }
  51. ->(1, 2 * $k - 1);
  52. sort { $a <=> $b } @powerful;
  53. }
  54. require Math::Sidef;
  55. my $A = int rand 1e5;
  56. my $B = int rand 1e7;
  57. foreach my $k (2 .. 5) {
  58. say "Testing: k = $k";
  59. my @a1 = powerful_numbers($A, $B, $k);
  60. my @a2 = Math::Sidef::powerful($k, $A, $B);
  61. my @a3 = grep { $_ >= $A } powerful_numbers(1, $B, $k);
  62. "@a1" eq "@a2" or die "error for: powerful_numbers($A, $B, $k)";
  63. "@a1" eq "@a3" or die "error for: powerful_numbers($A, $B, $k)";
  64. }
  65. say join(', ', powerful_numbers(1e6 - 1e4, 1e6, 2)); #=> 990025, 990125, 990584, 991232, 992016, 994009, 995328, 996004, 996872, 998001, 998784, 1000000