【问题标题】:An Efficient way to calculate sum of absolute difference between two Arrays?计算两个数组之间绝对差之和的有效方法?
【发布时间】:2018-01-13 08:23:05
【问题描述】:

我有两个数组 A 和 B,可以包含任何正整数或负整数。 我想计算以

形式给出的绝对差之和

其中 p 和 q 分别是数组 P 和 Q 的大小。

让我们看一个例子 如果

  P = [2, 4] and Q = [4, -3, -4, 4].

    = |2 - 4| + | 2 - (-3) | + |2 - (-4) | + | 2 - 4 | + | 4 - 4 | + | 4 - (-3) | + 
       | 4 - (-4) | + | 4 - 4 | 

    = 2 + 5 + 6 + 2 + 0 + 7 + 8 + 0 
                   = 30

有什么有效的方法吗?

【问题讨论】:

  • 到目前为止你尝试过什么?你也有语言偏好吗?

标签: algorithm statistics


【解决方案1】:
[2, 4] [4, -3, -4, 4]

排序:[2, 4] [-4, -3, 4, 4]

qs are negative, p is positive:
2 + 4 + 2 + 3 = sum(4, 3) + 2*2

qs are larger than p:
4 - 2 + 4 - 2 = sum(4, 4) - 2*2

qs are negative, p is positive:
4 + 4 + 4 + 3 = sum(4, 3) + 2*4

qs are equal to p:
4 - 4 + 4 - 4 = 0


qs are smaller than p (not in our example):
5 - 3 + 5 - 2 = -sum(3, 2) + 2*5

qs and p are both negative (not in our example):
  p is larger:
  |-5 - (-7)| + |-5 - (-6)| = 7 - 5 + 6 - 5 = sum(7, 6) - 2*5
  q is larger:
  |-5 - (-3)| + |-5 - (-2)| = 5 - 3 + 5 - 2 = -sum(3, 2) + 2*5

p is negative, qs are positive (not in our example):
|7 - (-5)| + |6 - (-5)| = 7 + 5 + 6 + 5 = sum(7, 6) + 2*5

这里有一些情况,如果数组是升序的,所有这些情况都可以重用已经计算的和以及乘法。

排序并计算Q 的前缀和数组,同时记录qs 变为正数的索引(如果有的话)。对于每个p,在O(log n)O(1) 时间找到上述每个案例的开始和结束部分。使用前缀和和p 的倍数添加到总数中。复杂度:O(n log n) 时间,O(n) 空间。

【讨论】:

  • 为什么 p/q 的符号很重要?如果p>q 绝对差是p-q;否则为q-p
  • @rici 不确定-您能否针对您描述的使用前缀和和乘法而不观察符号的这两种情况提出一个公式?我可能错过了这种冗余。我想我的答案更多的是关于事物的原理,而不是确切的减少:)
  • 如果你将它们全部加起来,那么每个p 出现的次数正数大于某些q 的次数,负数的次数则不是。对于qs 也是如此,尽管您需要使用>= 而不是>。因此,您只需要知道每个元素在另一个向量的元素中的排名;您可以通过对两个向量 abd 进行合并来进行排序。
  • @rici 不错的还原!
【解决方案2】:

根据符号而不是(“无条件”)“添加abs()”来添加或减去(未修改的)差异可能会也可能不会更有效。
不过,我希望当代编译器,甚至是 JIT 能够检测到等价性。

! Sum of Absolute Differences between every pair of elements of two arrays;
INTEGER PROCEDURE SAD2(a, b);
    INTEGER ARRAY a, b;
BEGIN
    INTEGER sum, i, j;  ! by the book, declare j locally just like diff;
    sum := 0;
    FOR i := LOWERBOUND(a, 1) STEP 1 UNTIL UPPERBOUND(a, 1) DO
        FOR j := LOWERBOUND(b, 1) STEP 1 UNTIL UPPERBOUND(b, 1) DO BEGIN
            INTEGER diff;
            diff := a(i) - b(j);
            sum := if diff < 0 then sum - diff
                               else sum + diff;
        END;
    SAD2 := sum;
END SAD2;

关于二次算法,请参阅גלעד ברקן's answer
这很可能是 גלעד ברקן 意图的代码,而不是遵循 PEP8 到点:

''' Given sequences A and B, SAD2 computes the sum of absolute differences
 for every element b from B subtracted from every element a from A.

The usual SAD sums up absolute differences of pairs with like index, only.
'''
from bisect import bisect_right

class state(list):
    ''' Hold state for one sequence: sorted elements & processing state. '''
    def __init__(self, a):
        self.extend(sorted(a))
        self.total = 0
        ''' sum(self[:self.todo]) '''
        self.todo = 0
        ''' next index to do/#elements done '''
    def __str__(self):
        return list.__str__(self) + str(self.todo) + ', ' + str(self.total)

def SAD2(a, b):
    ''' return Sum of Absolute Differences of all pairs (a[x], b[y]). '''
    nPairs = len(a) * len(b)
    if nPairs < 2:
        return abs(a[0] - b[0]) if 0 < nPairs else None
    a = state(a)
    b = state(b)
    sad = 0
    while True:
        key = a[a.todo]
        identical = bisect_right(a, key, a.todo)
        local = 0
        # iterate 'til not lower
        # going to need i: no takewhile(lambda x: x < key, b[todo:])
        i = b.todo
        while i < len(b):
            val = b[i]
            if key <= val:
                break
            local += val
            i += 1
        # update SAD
        # account for elements in a[:a.todo] paired with b[b.todo:i]
        sad += local*a.todo - a.total*(i - b.todo)
        b.todo = i
        n_key = identical - a.todo
        local += b.total
        b.total = local
        # account for elements in a[a.todo:identical] paired with b[:i]
        sad += (key*i - local)*n_key
        if len(b) <= b.todo:
            rest = len(a) - identical
            if 0 < rest:
                sad += sum(a[identical:])*len(b) - b.total*rest
            return sad
        a.todo = identical
        a.total += key * n_key
        a, b = b, a

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    • 1970-01-01
    相关资源
    最近更新 更多