113 Non-bouncy numbers.pl 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. #!/usr/bin/perl
  2. # Count of non-bouncy numbers <= limit.
  3. # Works fast with small limits (say 10^10), but not with very large ones, like 10^100.
  4. # TODO: find a faster approach.
  5. # The count of increasing and decreasing numbers, have something to do with sums of triangular numbers.
  6. # https://projecteuler.net/problem=113
  7. use 5.036;
  8. use ntheory qw(:all);
  9. no warnings 'recursion';
  10. #use Math::GMP qw(:constant);
  11. #use Math::AnyNum qw(:overload);
  12. sub increasing_numbers_count ($limit, $base = 10) {
  13. my sub f ($n, $min_d) {
  14. my $count = 1;
  15. foreach my $d ($min_d .. $base - 1) {
  16. my $v = ($n * $base + $d);
  17. $v <= $limit or last;
  18. $count += __SUB__->($v, $d);
  19. }
  20. return $count;
  21. }
  22. my $count = 0;
  23. foreach my $d (1 .. vecmin($limit, $base - 1)) {
  24. $count += f($d, $d);
  25. }
  26. return $count;
  27. }
  28. sub decreasing_numbers_count ($limit, $base = 10) {
  29. my sub f ($n, $max_d) {
  30. my $count = 1;
  31. foreach my $d (0 .. $max_d) {
  32. my $v = ($n * $base + $d);
  33. $v <= $limit or last;
  34. $count += __SUB__->($v, $d);
  35. }
  36. return $count;
  37. }
  38. my $count = 0;
  39. foreach my $d (1 .. vecmin($limit, $base - 1)) {
  40. $count += f($d, $d);
  41. }
  42. return $count;
  43. }
  44. sub non_bouncy_count ($limit, $base = 10) {
  45. if ($limit < $base) {
  46. return vecmax(0, $limit - 1);
  47. }
  48. my @D = todigits($limit, $base);
  49. my $t = (increasing_numbers_count($limit, $base) + decreasing_numbers_count($limit, $base));
  50. $t -= (($base - 1) * (scalar(@D) - 1));
  51. my $r = divint($base**scalar(@D) - 1, $base - 1);
  52. foreach my $d (1 .. $base - 1) {
  53. $r * $d > $limit and last;
  54. --$t;
  55. }
  56. return $t;
  57. }
  58. say non_bouncy_count(10**10 - 1); # 277032