If you care about the result more than the method, an equivalent function can be written that runs in the same time as a power function (possibly O(log(n))) rather than O(n*log(n)).
To start, we solve the related problem of counting all 1s in all numbers between 0 and 10^x, exclusive (where "^" means exponentiation). Let this number be dc(x) (think "digit count"). Thinking recursively, note dc(1)=1. Assume we can calculate dc(x-1). We can then calculate dc(x) by breaking up the 1s we need to count into leading digits and trailing digits. That is, count the leading 1s in numbers 10^(x-1) through 2*10^(x-1) - 1, inclusive (below, these numbers are written as the patterns "10*" and "19*"), and calculate the trailing 1s in all the numbers between 0 and 10^x, which can be done using dc(x-1). With double angle brackets indicating the sections of numbers to count, dc(x) can be calculated by counting «1»0* .. «1»9* and 0«0*» .. 9«9*». In the first case, there are 10^(x-1) numbers, hence 10^(x-1) leading ones. In the latter case, there are dc(x-1) trailing digits of interest for any given leading digit, and 10 leading digits, for a total of 10 * dc(x-1) digits of interest.
Code:
dc(1) = 1
(«1»0* .. «1»9*) (0«0*» .. 9«9*»)
dc(x) = 10 ^ (x-1) + 10 * dc(x-1)
We can do one better, writing dc as the explicit function "x * 10^(x-1)" rather than a recursive function. Note dc(1) = 1 = 1 * 10^0. Now assume dc(x-1) = (x-1) * 10^(x-2). Then
Code:
dc(x) = 10 ^ (x-1) + 10 * dc(x-1)
= 10 ^ (x-1) + 10 * ((x-1) * 10^(x-2))
= (1 + x-1) * 10 ^ (x-1)
= x * 10^(x-1) QED
Note that though we set out to count 1s, the above works for any digit from 1 through 9. It also works for 0, if we include the leading 0s normally left out of the representation (e.g. "0001").
Now for the original problem: counting a digit d in numbers from 1 through k*10^x. Actually, let me amend this to not include k*10^x, as it's simpler to solve without and can be covered in a special case. We'll call this number pdc(k,x,d) (think "partial digit count"). Note that these two functions use x differently, as dc(x) doesn't include 10^x, whereas pdc(k,x,d) does include 10^x.
To solve this problem, we can take the same approach of figuring out how to count these digits by breaking down the numbers into patterns. If d<k, there are basically the same two cases: «d»0* .. «d»9* and 0«0*»..(k-1)«9*». However, the number of d this time is 10^x, and each «0*»..«9*» is dc(x), for a total of 10^x+k*dc(x). If d≥k, then there are no numbers with leading ds in the range, so there are only the 0«0*»..(k-1)«9*» to count. If you want to include k*10^x in the range, the only change you need to make is when d=k, in which case there's an additional d to count, or when d=0, if you want to allow counting 0s (which I won't bother with).
Code:
(«d»0* .. «d»9*) (0«0*» .. (k-1)«9*»)
pdc(k,x,d), d<k = 10 ^ x + k * dc(x)
= 10 ^ x + k * x * 10^(x-1)
= (10 + k * x) * 10^(x-1)
(0«0*» .. (k-1)«9*»)
pdc(k,x,d), d>k = k * dc(x)
= k * x * 10^(x-1)
pdc(k,x,d), d=k = 1+k * x * 10^(x-1)
Final C function:
Code:
long count_digits_from_range(k,x,d) {
if (d < k) {
return (10 + k*x) * pow(10,x-1);
} else if (d==k) {
return 1 + k * x * pow(10,x-1);
} else {
return k * x * pow(10,x-1);
}
}
Time: 1.55 µs unoptimized, compared to 24s optimized (-O3) for the original, which is a speedup of over 1.5e7 times faster.
Addendum: 5e8 isn't the only fixed point. From the formula in pdc, we can get fixed points whenever d<k and (10+k*x)*10^(x-1) = k*10^x, or d>k (d≥k, if we don't include k*10^x) and k*10^x=k*x*10^(x-1), where 0 < k < 10. The former is equivalent to x=10(k-1)/k, which leads to the solutions (k,x) in [(1, 0), (2, 5), (5, 8)] when d<k:
Code:
# Python
>>> [(k,int(x)) for k,x in [(k, 10.0*(k-1.0)/k) for k in range(1,10)] if int(x)==x]
[(1, 0), (2, 5), (5, 8)]
The latter is much simpler to solve: 10=x when d>k, giving fixed points of 1e10, 2e10, ... 8e10. 9e10 is a fixed point if k*10^x isn't included in the count.