# ETOOBUSY ðŸš€ minimal blogging for the impatient

# PWC126 - Count Numbers

**TL;DR**

Here we are with TASK #1 from The Weekly Challenge #126. Enjoy!

# The challenge

You are given a positive integer

`$N`

.Write a script to print count of numbers from 1 to

`$N`

that donâ€™t contain digit`1`

.

Example`Input: $N = 15 Output: 8 There are 8 numbers between 1 and 15 that don't contain digit 1. 2, 3, 4, 5, 6, 7, 8, 9. Input: $N = 25 Output: 13 There are 13 numbers between 1 and 25 that don't contain digit 1. 2, 3, 4, 5, 6, 7, 8, 9, 20, 22, 23, 24, 25.`

# The questions

One question I would ask is whether there is a reasonable limit for the
input number `$N`

. I mean, this is extremely simple to break with a
brute-force approach, but it becomes unfeasable with large number. On
the same tune there would be the question about how many times should
this be called in a speciic time slot.

Nit-picking a bit, I guess that we have to count the *integers* between
the two input numbers. I would double check that `$N`

sould be indeed
included, although the second example makes it pretty clear that it is
indeed.

Last, I would also double check that weâ€™re talking base 10 here. Were it base 2â€¦ the answer would be trivial ðŸ¤“

# The solution

The basic idea to address this puzzle is the observation that we can
proceed *in chunks*. As an example, letâ€™s take `5314`

as an example
input.

It is greater than or equal to `5000`

, so for sure it will contain all
matching number between `1`

and `4999`

, plus those between `5000`

and
`5314`

.

Calculating the first slot can be simplified by observing that weâ€™re
dealing with all the numbers whose first digit ranges from `0`

to `4`

and the rest of three digits range from `0`

to `9`

. This also includes
`0000`

of course, but we can just subtract 1 from the result.

Now:

- the first digit must not contain
`1`

, so out of the`4`

in our example, we have to ignore that and be left with`0`

,`2`

,`3`

, and`4`

. That isâ€¦ 4 numbers. Where does the 4 come from, anyway? It is the first digit $F$ of our input number, less 1, so there we have it; - the other digits can range from
`0`

to`9`

, skipping`1`

. So each slot can only contain 9 possible candidates, all*independent*of one another. If there are 3 slot as in our case, we have a total of $9^3$ allowed arrangement. In general, if the input number`$N`

has $k$ digits, this part will have $9^k$ possible arrangements.

All in all, then, we end up with $F \cdot 9^k$ possible allowed arrangements.

At this point, we can chop off the first digit `5`

and do the same with
the remaining part `314`

.

Super-easyâ€¦ right?!? Wellâ€¦ *almost*.

This is the general gist, but there are a *ton* of shady corners and
special cases to consider. As an example, as soon as we arrive to a
residual number starting with `1`

, it has no use to move further the
basic calculation because all numbers will be prefixed with that `1`

.

Considering that there are so many poorly lit corners, I decided to
start with a brute-force baseline calculator, just to be able and double
check my *smarter* solutions. I started with Raku:

```
sub count-like-no-one-bf (Int:D $N where * > 0) {
(2 .. $N).grep({! /1/}).elems
}
```

I donâ€™t know if itâ€™s *idiomatic*, but it works and itâ€™s *readable*. We
start with the list of candidates (starting from 2 because 1 is out of
the game, right?), remove any candidate with at least one `1`

and count
how many items weâ€™re left with.

Theoretically speaking we would be done here, at least for inputs say
below `1000`

(arbitrary) and few calls. Practically speaking, though,
itâ€™s time to move on.

For technical reasons I switched to Perl, again starting with the brute force approach to have a baseline:

```
sub count_like_no_ones_bf ($N) { scalar grep {! m{1}mxs} 2 .. $N }
```

The description of the algorithm made me think about a recursive implementation at first thought, then I realized that chopping off the first digit was actually very easy to accomplish with a loop, so I ended up with this:

```
sub count_like_no_ones ($N) {
my $count = 0;
my @digits = split m{}mxs, $N;
while (@digits) {
my $first = shift @digits;
if (@digits) { # more to go after, use chunking
my $factor = $first > 1 ? $first - 1 : $first;
$count += $factor * 9 ** @digits;
}
else { # last digit, count all including 0
$count += $first > 1 ? $first : 1;
}
last if ($first == 1);
}
# we took into account sequence of all 0, so we remove it
return $count - 1;
}
```

I will never admit that it took me way more than I anticipated. Donâ€™t even ask please.

Anyway, it works although itâ€™s not as elegant as I would have liked. All the special cases are there, but they behave differently for the last digit and this is somehow itchy. Anyway.

At this point I wondered how the recursive implementation would look like. So, of course, I coded it:

```
sub count_like_no_ones_r ($N) {
return($N > 1 ? $N - 1 : 0) if $N < 10;
my $first = substr $N, 0, 1, '';
my $factor = $first > 1 ? $first - 1 : $first;
my $count = $factor * 9 ** length($N);
$count += 1 + count_like_no_ones_r($N) if $first != 1;
return $count - 1;
}
```

This is probably *slightly* more intuitive. Making a difference between
the general case and the one with one digit only is much simpler (first
line in the sub). There is one little catch where we have to add 1 to
the result of the recursive call, but itâ€™s needed to have the right
value eventually so there it is.

I think that the Benchmark is interesting in this case. I first started with computing all numbers from 1 to 9999:

```
use Benchmark 'cmpthese';
...
my @inputs = 0 .. 9999;
cmpthese(-5,
{
recursive => sub { count_like_no_ones_r($_) for @inputs },
iterative => sub { count_like_no_ones($_) for @inputs },
}
);
```

and, *surprise surprise*, the *recursive* function worked better:

```
Rate iterative recursive
iterative 35.4/s -- -8%
recursive 38.4/s 8% --
```

Time and again Iâ€™m baffled by the fact that the recursive function works
faster. *Except* that it does not scale as well as the iterative one,
which can be seen by adding a few `9`

before:

```
my @inputs = 99999999990000 .. 99999999999999;
cmpthese(-5,
{
recursive => sub { count_like_no_ones_r($_) for @inputs },
iterative => sub { count_like_no_ones($_) for @inputs },
}
);
```

Running it now yields:

```
Rate recursive iterative
recursive 8.67/s -- -22%
iterative 11.0/s 27% --
```

which is what I was expecting to be honest.

Last, I returned to Raku, only porting the iterative version in what is basically a straight translation with very few changes:

```
sub count-like-no-one (Int:D $N where * > 0) {
my $count = 0;
my @digits = $N.comb;
while (@digits) {
my $first = @digits.shift;
if (@digits) {
my $factor = $first > 1 ?? $first - 1 !! $first;
$count += $factor * 9 ** @digits;
}
else {
$count += $first > 1 ?? $first !! 1;
}
last if $first == 1;
}
return $count - 1;
}
```

And with thisâ€¦ I guess itâ€™s all for this puzzle! I hope you enjoyed
the ride and that will accept a recommendationâ€¦ *to stay safe*!

*Comments? Octodon, Twitter, GitHub, Reddit, or drop me a line!*