I came across this problem Unlucky number 13! recently but could not think of efficient solution this.
N is taken as input.
Let f(n)
be the number of sequences of length n that have no "13" in them, and g(n)
be the number of sequences of length n that have "13" in them.
Then f(n) = 10^n - g(n)
(in mathematical notation), because it's the number of possible sequences (10^n
) minus the ones that contain "13".
Base cases:
f(0) = 1
g(0) = 0
f(1) = 10
g(1) = 0
When looking for the sequences with "13", a sequence can have a "13" at the beginning. That will account for 10^(n-2)
possible sequences with "13" in them. It could also have a "13" in the second position, again accounting for 10^(n-2)
possible sequences. But if it has a "13" in the third position, and we'd assume there would also be 10^(n-2)
possible sequences, we could those twice that already had a "13" in the first position. So we have to substract them. Instead, we count 10^(n-4)
times f(2)
(because those are exactly the combinations in the first two positions that don't have "13" in them).
E.g. for g(5):
g(5) = 10^(n-2) + 10^(n-2) + f(2)*10^(n-4) + f(3)*10^(n-5)
We can rewrite that to look the same everywhere:
g(5) = f(0)*10^(n-2) + f(1)*10^(n-3) + f(2)*10^(n-4) + f(3)*10^(n-5)
Or simply the sum of f(i)*10^(n-(i+2))
with i
ranging from 0
to n-2
.
In Python:
from functools import lru_cache
@lru_cache(maxsize=1024)
def f(n):
return 10**n - g(n)
@lru_cache(maxsize=1024)
def g(n):
return sum(f(i)*10**(n-(i+2)) for i in range(n-1)) # range is exclusive
The lru_cache
is optional, but often a good idea when working with recursion.
>>> [f(n) for n in range(10)]
[1, 10, 99, 980, 9701, 96030, 950599, 9409960, 93149001, 922080050]
The results are instant and it works for very large numbers.