Math O(logn)时间复杂度中前n个正整数的二进制表示的级联

Math O(logn)时间复杂度中前n个正整数的二进制表示的级联,math,bit-manipulation,Math,Bit Manipulation,我在一次编码比赛中遇到了这个问题。给定一个数n,将前n个正整数的二进制表示形式串联起来,并返回所形成的结果数的十进制值。因为答案可以是大的,所以返回的答案可以是模10^9+7。 N可以大到10^9。 例:n=4。形成的数字=11011100(1=1,10=2,11=3100=4)。十进制值11011100=220 我找到了这个问题的堆栈溢出答案,但问题是它只包含一个O(n)解。 链接:- 由于n可以达到10^9,我们需要找到比O(n)更好的解决方案。您只需注意一个简单的模式。以n=4为例,让我们

我在一次编码比赛中遇到了这个问题。给定一个数n,将前n个正整数的二进制表示形式串联起来,并返回所形成的结果数的十进制值。因为答案可以是大的,所以返回的答案可以是模10^9+7。 N可以大到10^9。 例:n=4。形成的数字=11011100(1=1,10=2,11=3100=4)。十进制值11011100=220

我找到了这个问题的堆栈溢出答案,但问题是它只包含一个O(n)解。 链接:-


由于n可以达到10^9,我们需要找到比O(n)更好的解决方案。

您只需注意一个简单的模式。以
n=4
为例,让我们从
n=1
开始逐步构建解决方案

1 -> 1                         #1
2 -> 2^2(1) + 2                #6
3 -> 2^2[2^2(1)+2] + 3         #27
4 -> 2^3{2^2[2^2(1)+2]+3} + 4  #220
如果展开
n=4
的每个项的系数,将得到如下系数:

1 -> (2^3)*(2^2)*(2^2)
2 -> (2^3)*(2^2)
3 -> (2^3)
4 -> (2^0)
N
为所需数字的字符串表示中的总位数,
D(x)
x
中的位数。然后,这些系数可以写成

1 -> 2^(N-D(1))
2 -> 2^(N-D(1)-D(2))
3 -> 2^(N-D(1)-D(2)-D(3))
... and so on
由于对于某些给定的
t
,范围
(2^t,2^(t+1)-1)
之间的所有
x
D(x)
的值都是相同的,因此您可以将问题分解为这些范围,并使用数学(而不是迭代)对每个范围进行求解。由于此类范围的数量将为
log2(给定N)
,因此应在给定的时间限制内工作。
例如,各种范围变为:

1. 1 (D(x) = 1)
2. 2-3 (D(x) = 2)
3. 4-7 (D(x) = 3)
4. 8-15 (D(x) = 4)

下面是一些Python代码,它提供了一个快速的解决方案;它使用了与in的帖子相同的想法。它要求Python>=3.8,但它没有使用Python中特别奇特的东西,并且可以很容易地翻译成另一种语言。如果目标语言中还没有模块求幂和模块求逆的算法,则需要编写它们

首先,出于测试目的,让我们定义缓慢而明显的版本:

结果减少的模数, M=10**9+7 def slow_binary_concat(n): """ 连接1到n(包括)的二进制表示形式。 将生成的二进制字符串重新解释为整数。 """ 连接=“”.join(范围(n+1)中k的格式(k,“b”) 返回int(串联,2)%M 检查我们是否得到了预期的结果:

慢速二进制压缩(4) 220 >>>慢二元concat(10) 462911642 现在我们将编写一个更快的版本。首先,我们将范围
[1,n)
拆分为子区间,以便在每个子区间内,所有数字的二进制长度相同。例如,范围
[1,10)
将拆分为四个子区间:
[1,2)
[2,4)
[4,8)
[8,10)
。这里有一个函数可以进行拆分:

def按位长度拆分(n):
"""
将[1,n]中的数字按位长度拆分。
生成三元组(a、b、2**k)。每个三元组表示一个子区间
[1,n]的[a,b],其中aa时:
b=2*a
产量(a,最小值(n,b),b)
a=b
示例输出:

>>列表(按位长度拆分(10))
[(1, 2, 2), (2, 4, 4), (4, 8, 8), (8, 10, 16)]
现在,对于每个子区间,该子区间中所有数字的串联值由一个相当简单的数学和表示,它可以以精确的形式计算。下面是一个函数,用于计算该和模
M

def子区间浓度(a、b、l):
"""
[a,b]中的值的串联,所有这些值都具有相同的位长度k。
l是2**k。
等价地,在(a,b))模M的范围内,i的和(i*l**(b-1-i))。
"""
n=b-a
inv=功率(l-1,-1,M)
q=(功率(l,n,M)-1)*inv
返回(a*q+(q-n)*库存)%M
我不会在这里讨论总和的计算:对于这个网站来说,这有点离题,如果没有一个好的方法来呈现公式,很难表达。如果你想了解细节,这是一个主题,或者是一页相当简单的代数

最后,我们想把所有的时间间隔放在一起。这里有一个函数可以做到这一点

def fast\u binary\u concat(n):
"""
慢速二进制concat的快速版本。
"""
acc=0
对于按位长度(n+1)拆分的a、b、l:
acc=(acc*pow(l,b-a,M)+子区间(a,b,l))%M
返回acc
与慢速版本的比较表明,我们得到了相同的结果:

>>fast\u binary\u concat(4)
220
>>>快速二进制concat(10)
462911642
但是快速版本可以很容易地评估更大的输入,而使用慢速版本是不可行的:

快速二进制压缩(10**9) 827129560 >>>快速二进制通信(10**18) 945204784
是什么让你认为是O(logn)解决方案存在?@Arafat Siddiqui我错过了
答案模10^9+7
要求这回答了你的问题吗?这个总和的计算速度能比每个项的总和快吗?这些项形成算术-几何序列,总和可以在对数时间内计算出来。@abhinav mathur否决票并不意味着答案是错误的。我没有添加代码,读者仍然需要付出努力:这太棒了!我增加了一个C++解决方案,我把数学解释得更清楚了。