这是一些提供快速解决方案的 Python 代码;它使用与Abhinav Mathur 的帖子相同的想法。它需要 Python >= 3.8,但它没有使用 Python 中的任何特别花哨的东西,并且可以很容易地翻译成另一种语言。如果目标语言中不可用,则需要编写模幂运算和模逆算法。
首先,出于测试目的,让我们定义缓慢而明显的版本:
# Modulus that results are reduced by,
M = 10 ** 9 + 7
def slow_binary_concat(n):
"""
Concatenate binary representations of 1 through n (inclusive).
Reinterpret the resulting binary string as an integer.
"""
concatenation = "".join(format(k, "b") for k in range(n + 1))
return int(concatenation, 2) % M
检查我们是否得到了预期的结果:
>>> slow_binary_concat(4)
220
>>> slow_binary_concat(10)
462911642
现在我们将编写一个更快的版本。首先,我们将[1, n) 范围分割成子区间,这样在每个子区间内,所有数字的二进制长度都相同。例如,[1, 10) 范围将分为四个子区间:[1, 2)、[2, 4)、[4, 8) 和 [8, 10)。这是一个执行拆分的函数:
def split_by_bit_length(n):
"""
Split the numbers in [1, n) by bit-length.
Produces triples (a, b, 2**k). Each triple represents a subinterval
[a, b) of [1, n), with a < b, all of whose elements has bit-length k.
"""
a = 1
while n > a:
b = 2 * a
yield (a, min(n, b), b)
a = b
示例输出:
>>> list(split_by_bit_length(10))
[(1, 2, 2), (2, 4, 4), (4, 8, 8), (8, 10, 16)]
现在对于每个子区间,该子区间中所有数字的串联值由一个相当简单的数学总和表示,可以以精确的形式计算。这是一个计算和模M的函数:
def subinterval_concat(a, b, l):
"""
Concatenation of values in [a, b), all of which have the same bit-length k.
l is 2**k.
Equivalently, sum(i * l**(b - 1 - i)) for i in range(a, b)) modulo M.
"""
n = b - a
inv = pow(l - 1, -1, M)
q = (pow(l, n, M) - 1) * inv
return (a * q + (q - n) * inv) % M
我不会在这里对总和进行评估:对于本网站来说,这有点离题,如果没有一种呈现公式的好方法,就很难表达。如果您想了解详细信息,那是https://math.stackexchange.com 的主题,或者是相当简单的代数页面。
最后,我们想把所有的区间放在一起。这是执行此操作的函数。
def fast_binary_concat(n):
"""
Fast version of slow_binary_concat.
"""
acc = 0
for a, b, l in split_by_bit_length(n + 1):
acc = (acc * pow(l, b - a, M) + subinterval_concat(a, b, l)) % M
return acc
与慢版本的比较表明我们得到了相同的结果:
>>> fast_binary_concat(4)
220
>>> fast_binary_concat(10)
462911642
但是对于更大的输入,可以很容易地评估快速版本,而使用慢速版本是不可行的:
>>> fast_binary_concat(10**9)
827129560
>>> fast_binary_concat(10**18)
945204784