Python 快速访问成对运算的和
给定一个数字向量Python 快速访问成对运算的和,python,algorithm,data-structures,Python,Algorithm,Data Structures,给定一个数字向量v,我可以通过使用累积和来访问该向量各部分的和,即,代替O(n) v=[1,2,3,4,5] 定义和v(i,j): 回报总额(v[i:j]) 我可以做O(1) 现在,我需要一个类似的东西,但是对于成对而不是求和: def pairwise(i,j): ret = 0 for p in range(i,j): for q in range(p+1,j): ret += f(v(p),v(q)) return ret
v
,我可以通过使用累积和来访问该向量各部分的和,即,代替O(n)
v=[1,2,3,4,5]
定义和v(i,j):
回报总额(v[i:j])
我可以做O(1)
现在,我需要一个类似的东西,但是对于成对
而不是求和
:
def pairwise(i,j):
ret = 0
for p in range(i,j):
for q in range(p+1,j):
ret += f(v(p),v(q))
return ret
其中,f
最好是相对任意的东西(例如,*
或^
或…)。然而,只为产品或XOR工作的东西也会很好
PS1。我正在寻找一个加速方面,而不是通用的,如
PS2。问题在于算法,而不是实现,因此与语言无关。我给它添加了python
,这仅仅是因为我的示例是用python编写的
PS3。显然,可以两两预先计算
的所有值,因此解决方案在时间和空间上都应该是o(n^2)
(最好是线性的)。对于二进制操作,例如或xor
,可以使用o(n)
算法。
让我们考虑这个例子的XOR,但这可以很容易地修改为//和。
这里需要注意的最重要的一点是,两个数字的位x
上的二进制运算符的结果不会影响位y
的结果。(您可以通过尝试类似于010^011=001
的方法很容易看出这一点。因此,我们首先计算所有数字中最左边的位对最终和的贡献,然后是下一个最低有效位,依此类推。下面是一个简单的算法/伪代码:
构造一个简单的表dp
,其中dp[i][j]=范围[i,n]内的数字计数,并设置第j位
在大多数情况下,对于整数,原则上位数,您总是可以在Θ(n²)空间中预计算每个可能的输出,然后回答Θ(1)中的查询通过在预计算表中查找它。其他一切都是一种权衡,取决于预计算时间、空间和实际计算时间的成本;有趣的问题是o(n²)可以做什么空间,即次二次。这通常取决于应用程序和二进制运算的属性f
在f
为*
的特定情况下,我们可以仅使用Θ(n)空间进行Θ(1)查找:我们将利用p
中的对的和等于所有对的和,减去p=q
中的对之和,除以2来说明p>q
中的对
#输入数据
v=[1,2,3,4,5]
n=len(v)
#预计算
部分和=[0]*(n+1)
部分和平方=[0]*(n+1)
对于枚举(v)中的i,x:
部分和[i+1]=部分和[i]+x
部分和平方[i+1]=部分和平方[i]+x*x
#查询响应
def成对(i,j):
s=部分和[j]-部分和[i]
s2=部分和平方[j]-部分和平方[i]
返回(s*s-s2)/2
更一般地说,只要f
是可交换的,并且分布在累加器操作上(在本例中为+
),该方法就可以工作。我在这里编写的示例没有使用itertools,因此它更容易翻译为其他语言,因为这个问题是与语言无关的。我认为你不能比O(N^2)做得更好在这里,因为有N^2个值需要考虑。可以进行的任何优化都与运算符的属性有关(例如:如果运算是XOR,则进行位操作等)@AbhinavMathur:谢谢,你会为XOR做什么?(f=^
)numpy是一个选项吗?@PranavHosangadi:我在寻找一个算法,而不是它的实现;如果numpy
实现了一些东西,我可以处理它。然而,问题是加速方面的O
,而不是在C
中重写。numpy使用SIMD操作来获得它所实现的加速,这在香草中是不可能的因此,您的算法仍然是O(无论什么)
,但由于并行处理的数据更多,因此作业运行速度更快。
def pairwise(i,j):
ret = 0
for p in range(i,j):
for q in range(p+1,j):
ret += f(v(p),v(q))
return ret
l = [5,3,1,7,8]
n = len(l)
ans = 0
max_binary_length = max(log2(i) for i in l)+1 #maximum number of bits we need to check
for j in range(max_binary_length):
# we check the jth bits of all numbers here
for i in range(0,n):
# we need sum((l[i]^l[j]) for j in range (i+1,n))
current = l[i]
if jth bit of current == 0:
# since 0^1 = 1, we need count of numbers with jth bit 1
count = dp[i+1][j]
else:
# we need count of numbers with jth bit 0
count = (n-i)-dp[i+1][j]
# the indexing could be slightly off, you can check that once
ans += count * (2^j)
# since we're checking the jth bit, it will have a value of 2^j when set
print(ans)