Python 数组中数字的绝对差之和

Python 数组中数字的绝对差之和,python,arrays,algorithm,Python,Arrays,Algorithm,我想计算指数I上的一个数字的绝对差之和,所有整数都是指数I-1,在o(n)中。但是我想不出比o(n^2)更好的方法 例如: [3,5,6,7,1] 具有绝对和的数组将为(对于索引i处的整数,和将位于另一个数组中的索引i): 谁能帮我把复杂度降低到o(n)(如果不可能,那么至少在时间复杂度方面进行更好的优化) 下面是我的python代码: a=[3,5,6,7,1] n=5 absoluteSumArray=[] for i in range(0,n): Sum=0 for j in r

我想计算指数I上的一个数字的绝对差之和,所有整数都是指数I-1,在o(n)中。但是我想不出比o(n^2)更好的方法

例如:

[3,5,6,7,1]
具有绝对和的数组将为(对于索引i处的整数,和将位于另一个数组中的索引i):

谁能帮我把复杂度降低到o(n)(如果不可能,那么至少在时间复杂度方面进行更好的优化)

下面是我的python代码:

a=[3,5,6,7,1]
n=5
absoluteSumArray=[]
for i in range(0,n):
  Sum=0
  for j in range(0,i):
     Sum+=abs(int(a[i])-int(a[j]))
  absoluteSumArray.append(Sum)
首先,我可以提供一个O(n logn)解决方案:让fi作为结果的第I个数字。我们有:

从左到右遍历数组并维护元素a0到ai-1的二元搜索树时,我们可以求解O(logn)中公式的所有部分:

  • 保留子树大小以计算大于/小于给定值的元素
  • 保留累积子树和,以回答大于/小于给定元素的和查询
如果我们想避免实施成本,我们可以用一些更简单的数据结构替换增强的搜索树:

  • 事先对数组进行排序。按排序顺序为每个数字指定其排名
  • 保持0/1的值以计算小于给定值的元素数
  • 保留数组值的另一个二叉索引树,以计算小于给定值的元素和
在一般情况下,我认为这不能用O(n)来解决。至少你需要在某个时候对数字进行排序。但是,可能这些数字是有界的,或者您有其他一些限制,因此您可能能够在O(1)中实现求和和和计数操作

实施:

# binary-indexed tree, allows point updates and prefix sum queries
class Fenwick:
  def __init__(self, n):
    self.tree = [0]*(n+1)
    self.n = n
  def update_point(self, i, val):  # O(log n)
    i += 1
    while i <= self.n:
      self.tree[i] += val
      i += i & -i
  def read_prefix(self, i):        # O(log n)
    i += 1
    sum = 0
    while i > 0:
      sum += self.tree[i]
      i -= i & -i
    return sum

def solve(a):
  rank = { v : i for i, v in enumerate(sorted(a)) }
  res = []
  counts, sums = Fenwick(len(a)), Fenwick(len(a))
  total_sum = 0
  for i, x in enumerate(a):
    r = rank[x]
    num_smaller = counts.read_prefix(r)
    sum_smaller = sums.read_prefix(r)
    res.append(total_sum - 2*sum_smaller + x * (2*num_smaller - i))
    counts.update_point(r, 1)
    sums.update_point(r, x)
    total_sum += x
  return res

print(solve([3,5,6,7,1]))  # [0, 2, 4, 7, 17]
print(solve([2,0,1]))      # [0, 2, 2]
二进制索引树,允许点更新和前缀和查询 芬威克级: 定义初始化(self,n): self.tree=[0]*(n+1) self.n=n def更新点(自身、i、val):#O(日志n) i+=1 而我0: sum+=self.tree[i] i-=i&-i 回报金额 def解决方案(a): 秩={v:i表示枚举中的i,v(排序(a))} res=[] 计数,总和=芬威克(len(a)),芬威克(len(a)) 总和=0 对于枚举(a)中的i,x: r=秩[x] num_较小=计数。读取前缀(r) sum\u较小=sums.read\u前缀(r) res.append(total_sum-2*sum_较小+x*(2*num_较小-i)) 计数。更新_点(r,1) 求和。更新_点(r,x) 总和+等于x 返回res 打印(求解([3,5,6,7,1])#[0,2,4,7,17] 打印(求解([2,0,1])#[0,2,2] 这里是线性决策树模型中的一个比较下限。这排除了“nice”
o(n logn)
时间算法的可能性(两个现在删除的答案都在这个类中)

这个问题从计算的问题上有一个很小的简化

f(x1, ..., xn) = sum_i sum_j |xi - xj|.

函数
f
x1,…,xn
是完全可微的,当且仅当
x1,…,xn
是两两不同的。
f
是完全可微的集合,因此有
n连接的组件,其中决策树的每个叶最多可以处理一个。

[sum(abs(a[i]-a[j])表示范围(i)中的j)表示范围(n)中的i)]
产生
[0,2,4,7,17]
@cdhagmann很酷的故事,但OP已经有了一个O(n^2)解决方案“我想计算索引I处的数字与索引I-1处的所有整数的绝对差之和”。但这不是您的代码所做的。它是计算索引I处的数字与索引0到I-1处的所有整数的绝对差之和。
O(n)
看起来不太可能。即使是(计算这个问题的输出之和)几年前才得到一个O(nlog(n))解。你太擅长算法了:P…答案不错…而且你说服我的唯一正确答案是O(n)是不可能的。但我会稍等一下(以防万一)当我实现这个的时候。谢谢。@JoranBeasley:我看不到任何证明它有效的证据(似乎缺少一个因素),也没有代码可以证明它有效try@JoranBeasley什么?它是O(n对数n),OPs版本是欧米茄(n^2)。显然,对于小列表,常数因子要高得多。尝试使用大小为10000的列表,您会非常清楚地注意到大小为10k的列表的差异。我看到此算法的timeit为0.065(1次迭代),而他的旧算法@15.115(也只有1次)…这是一个非常棒的加速(如果您的列表很大)是时候去学习线性决策树的功能了,这样我才能理解这个界限。
f(x1, ..., xn) = sum_i sum_j |xi - xj|.