almost_prime_numbers_in_range_mpz.pl 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 15 February 2021
  4. # Edit: 04 April 2024
  5. # https://github.com/trizen
  6. # Generate all the k-almost prime numbers in range [A,B].
  7. # See also:
  8. # https://en.wikipedia.org/wiki/Almost_prime
  9. use 5.036;
  10. use ntheory qw(:all);
  11. use Math::GMPz;
  12. sub almost_prime_numbers ($A, $B, $k) {
  13. my $u = Math::GMPz::Rmpz_init();
  14. my $v = Math::GMPz::Rmpz_init();
  15. Math::GMPz::Rmpz_setbit($u, $k);
  16. $A = vecmax($A, $u);
  17. $A = Math::GMPz->new("$A");
  18. $B = Math::GMPz->new("$B");
  19. my @values = sub ($m, $lo, $k) {
  20. Math::GMPz::Rmpz_tdiv_q($u, $B, $m);
  21. Math::GMPz::Rmpz_root($u, $u, $k);
  22. my $hi = Math::GMPz::Rmpz_get_ui($u);
  23. if ($lo > $hi) {
  24. return;
  25. }
  26. my @lst;
  27. if ($k == 1) {
  28. Math::GMPz::Rmpz_cdiv_q($u, $A, $m);
  29. if (Math::GMPz::Rmpz_fits_ulong_p($u)) {
  30. $lo = vecmax($lo, Math::GMPz::Rmpz_get_ui($u));
  31. }
  32. elsif (Math::GMPz::Rmpz_cmp_ui($u, $lo) > 0) {
  33. if (Math::GMPz::Rmpz_cmp_ui($u, $hi) > 0) {
  34. return;
  35. }
  36. $lo = Math::GMPz::Rmpz_get_ui($u);
  37. }
  38. if ($lo > $hi) {
  39. return;
  40. }
  41. foreach my $p (@{primes($lo, $hi)}) {
  42. my $v = Math::GMPz::Rmpz_init();
  43. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  44. push @lst, $v;
  45. }
  46. return @lst;
  47. }
  48. my $z = Math::GMPz::Rmpz_init();
  49. foreach my $p (@{primes($lo, $hi)}) {
  50. Math::GMPz::Rmpz_mul_ui($z, $m, $p);
  51. Math::GMPz::Rmpz_cdiv_q($u, $A, $z);
  52. Math::GMPz::Rmpz_tdiv_q($v, $B, $z);
  53. if (Math::GMPz::Rmpz_cmp($u, $v) <= 0) {
  54. push @lst, __SUB__->($z, $p, $k - 1);
  55. }
  56. }
  57. return @lst;
  58. }
  59. ->(Math::GMPz->new(1), 2, $k);
  60. sort { Math::GMPz::Rmpz_cmp($a, $b) } @values;
  61. }
  62. # Generate 5-almost primes in the range [50, 1000]
  63. my $k = 5;
  64. my $from = 50;
  65. my $upto = 1000;
  66. my @arr = almost_prime_numbers($from, $upto, $k);
  67. my @test = grep { is_almost_prime($k, $_) } $from .. $upto; # just for testing
  68. join(' ', @arr) eq join(' ', @test) or die "Error: not equal!";
  69. say join(', ', @arr);