你可以用递归解决这个问题。
一个重要的观察是,低于某个限制的“好”数字的计数,即具有至少一个 偶数 位的数字,等于:
- 数字本身(从 0 开始计算所有数字)
- 排除所有仅由奇数组成的数字
- 不包括零
仅由 10 次幂以下的奇数组成的数字的计数是:
- 5 + 5² + 5³ + ... + 5k,其中 k 是幂,等于 (5k+1 sup>-1)/4-1
仅由 10 次方到 10 次方两倍之间的奇数组成的数字计数(因此这些数字具有固定的位数,始终从 1 开始)是:
当然,如果最高有效位是 2(或另一个偶数),则奇数的计数为零。
因此,您可以使用这些成分构建解决方案:
def count_even(digit, num_zeroes):
# The arguments encode a number that only has one non-zero digit, the first:
# - digit: the most significant digit (1..9)
# - num_zeroes: the count of trailing zeroes
# Returns the count of numbers less than this encoded number that have at
# least one even digit
return (10**num_zeroes * digit # count all numbers
# exclude numbers with fewer digits and only odd digits:
- ((5**(num_zeroes+1)-1)//4-1)
# exclude numbers with same digit count and only odd digits,
# but with first digit less than given digit,
- 5**num_zeroes * (digit//2)
# ...and exclude 0
- 1
)
# The main algorithm
def get_nth_with_even_digit(n):
# Base cases
if n < 10:
return n * 2 # 2, 4, 6, ..., 18
if n < 20:
return 10 + n # 20 - 29
# Use recursion to build on previous results:
num_zeroes = 1
while True:
diff = count_even(3, num_zeroes) - count_even(1, num_zeroes)
num_zeroes += 1
power = 10**num_zeroes
limit = 1 + count_even(2, num_zeroes)
if n < limit:
return power//5 + get_nth_with_even_digit(n - diff)
if n < limit + power:
return n + power//5 + get_nth_with_even_digit(limit - diff) - limit
在测试代码时,我使用了类似您自己的蛮力实现,并将结果与上述函数的结果进行了比较:
# Testing code - will keep running until a difference is found with the brute force method
def brute():
for i in range(10000000000):
if any(d in "02468" for d in str(i)):
yield i
for n, expected in enumerate(brute()):
got = get_nth_with_even_digit(n)
assert got == expected, "{}: got {}, expected {}".format(n, got, expected)
print(n, expected)