pythonarraysalgorithm

Sum of absolute differences of a number in an array


I want to calculate the sum of absolute differences of a number at index i with all integers up to index i-1 in o(n). But I am not able to think of any approach better than o(n^2) .

For example:

[3, 5, 6, 7, 1]

array with absolute sum will be (for integer at index i sum will be at index i in another array):

[0, 2, 4, 7, 17]

Can anyone help me to reduce the complexity to o(n) (if not possible then at least better optimization in terms of time complexity)?

Here my Python code:

a = [3, 5, 6, 7, 1]
n = 5
absoluteSumArray = []
for i in range(n):
    Sum = 0
    for j in range(i):
        Sum += abs(int(a[i]) - int(a[j]))
    absoluteSumArray.append(Sum)

Solution

  • I can offer an O(n log n) solution for a start: Let fi be the i-th number of the result. We have:

    enter image description here

    When walking through the array from left to right and maintain a binary search tree of the elements a0 to ai-1, we can solve all parts of the formula in O(log n):

    We can replace the augmented search tree with some simpler data structures if we want to avoid the implementation cost:

    TBH I don't think this can be solved in O(n) in the general case. At the very least you would need to sort the numbers at some point. But maybe the numbers are bounded or you have some other restriction, so you might be able to implement the sum and count operations in O(1).

    An implementation:

    # 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]