379 Least common multiple count.pl 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 09 January 2021
  4. # https://github.com/trizen
  5. # Let f(n) be the number of couples (x,y) with x and y positive integers, x ≤ y and the least common multiple of x and y equal to n.
  6. # Let a(n) = A007875(n), with a(1) = 1, for n > 1 (due to Vladeta Jovovic, Jan 25 2002):
  7. # a(n) = (1/2)*Sum_{d|n} abs(mu(d))
  8. # = 2^(omega(n)-1)
  9. # = usigma_0(n)/2
  10. # This gives us f(n) as:
  11. # f(n) = Sum_{d|n} a(d)
  12. # This script implements a sub-linear formula for computing partial sums of f(n):
  13. # S(n) = Sum_{k=1..n} f(k)
  14. # = Sum_{k=1..n} Sum_{d|k} a(d)
  15. # = Sum_{k=1..n} a(k) * floor(n/k)
  16. # See also:
  17. # https://oeis.org/A007875
  18. # https://oeis.org/A064608
  19. # https://oeis.org/A182082
  20. # Problem URL:
  21. # https://projecteuler.net/problem=379
  22. # Several values for S(10^n):
  23. # S(10^1) = 29
  24. # S(10^2) = 647
  25. # S(10^3) = 11751
  26. # S(10^4) = 186991
  27. # S(10^5) = 2725630
  28. # S(10^6) = 37429395
  29. # S(10^7) = 492143953
  30. # S(10^8) = 6261116500
  31. # S(10^9) = 77619512018
  32. # S(10^10) = 942394656385
  33. # S(10^11) = 11247100884096
  34. # Runtime: ~9 minutes.
  35. # WARNING: the program requires ~3 GB of RAM.
  36. use 5.020;
  37. use strict;
  38. use warnings;
  39. use ntheory qw(:all);
  40. use experimental qw(signatures);
  41. sub S ($n) {
  42. my $lookup_size = 2 + 2 * rootint($n, 3)**2;
  43. $lookup_size = 50000000 if ($lookup_size > 50000000);
  44. $lookup_size = sqrtint($n) if ($lookup_size < sqrtint($n));
  45. my @omega_lookup = (0);
  46. my @omega_sum_lookup = (0);
  47. for my $k (1 .. $lookup_size) {
  48. $omega_lookup[$k] = ($k == 1) ? 0 : (1 << (factor_exp($k) - 1));
  49. $omega_sum_lookup[$k] = $omega_sum_lookup[$k - 1] + $omega_lookup[$k];
  50. }
  51. my $s = sqrtint($n);
  52. my @mu = moebius(0, $s);
  53. my sub R ($n) {
  54. if ($n <= $lookup_size) {
  55. return $omega_sum_lookup[$n];
  56. }
  57. my $total = 0;
  58. foreach my $k (1 .. sqrtint($n)) {
  59. $mu[$k] || next;
  60. my $t = 0;
  61. my $r = sqrtint(divint($n, $k * $k));
  62. foreach my $j (1 .. $r) {
  63. $t += divint($n, $j * $k * $k);
  64. }
  65. $total += $mu[$k] * (2 * $t - $r * $r);
  66. }
  67. return (($total - 1) >> 1);
  68. }
  69. my $total = 0;
  70. for my $k (1 .. $s) {
  71. $total += $omega_lookup[$k] * divint($n, $k);
  72. $total += R(divint($n, $k));
  73. }
  74. $total -= R($s) * $s;
  75. return $total + $n;
  76. }
  77. say "S(10^6) = ", S(powint(10, 6));
  78. say "S(10^12) = ", S(powint(10, 12));