Given two arrays `a` and `b`, find all pairs `(i, j)`such that `i <= j` and `a[i] – b[j] = a[j] – b[i]`

huangapple go评论89阅读模式
英文:

Given two arrays `a` and `b`, find all pairs `(i, j)`such that `i <= j` and `a[i] - b[j] = a[j] - b[i]`

问题

给定两个数组 ab,找到所有满足条件 i &lt;= ja[i] - b[j] = a[j] - b[i] 的配对 (i, j)ab 的长度始终相同。

显然的解决方案是双重循环:

def simple(a, b):
    count = 0
    for i in range(len(a)):
        for j in range(len(b)):
            if (a[i] - b[j] == a[j] - b[i] and i &lt;= j):
                count += 1
    return count
a = [2, -2, 5, 3]
b = [1, 5, -1, 1]
print('Pairs:', simple(a, b)) # 这会打印出 6,配对为 [[0, 0], [0, 1], [1, 1], [2, 2], [2, 3], [3, 3]]

但这个解决方案的时间复杂度是 O(n^2)。

下面是我提供的 O(n) 解决方案:

我注意到 a[i] - b[j] = a[j] - b[i] 可以写成 a[i] + b[i] = a[j] + b[j]

所以我决定创建两个列表 list_ilist_j,它们的长度与 ab 相同。

list_i 中,我存储了所有 a[i] + b[i] 的结果。

list_j 中,我存储了所有 a[j] + b[j] 的结果。

然后,我只需要比较上面提到的两个列表中每个索引的值,确保它们相同并且 i &lt;= j。以下是代码,但它是不正确的。

def complex(a, b):
    n = len(a)
    count = 0
    list_i = [0] * n
    list_j = [0] * n

    for i in range(n):
        list_i[i] = a[i] + b[i]

    for j in range(n):
        list_j[j] = a[j] + b[j]

    i, j = 0, 0
    while (i &lt;= j and i &lt; n and j &lt; n):
        if (list_i[i] == list_j[j]):
            count += 1
            j += 1
        else:
            i = j
    return count

在这个过程中,我注意到 ij 相同的索引将始终为真。

所以我不需要检查它们。我只需要将 a 的长度添加到最终的计数中。

我还注意到,我只需要检查相邻的值,例如 i = i + 1。这意味着我只需要一个总和列表,并在循环中检查 sum_list[i] == sum_list[i + 1]

因此,我的最终代码如下:

def complex(a, b):
    n = len(a)
    count = 0
    list_sum = [0] * n

    for i in range(n):
        list_sum[i] = a[i] + b[i]

    for i in range(n - 1):
        if list_sum[i] == list_sum[i + 1]:
            count += 1
    return count + len(a)
a = [2, -2, 5, 3]
b = [1, 5, -1, 1]
print('Pairs:', complex(a, b)) # 这会打印出 6,配对为 [[0, 0], [0, 1], [1, 1], [2, 2], [2, 3], [3, 3]]

这提供了正确的解决方案,并且时间复杂度为 O(n),但是否正确?

另外,是否可以使用集合或字典来找到更好的解决方案?

英文:

NOTE: Yes, this is for a coding challenge, which I already finished, but I want to see if my answer was correct, and if there are more elegant solutions

I got this question on a coding assignment, and I failed to find the optimal O(n) solution

Given two arrays a and b, find all pairs (i, j) such that i &lt;= j and a[i] - b[j] = a[j] - b[i]. a and b will be the same length at all times.

Give an O(n) solution

The obvious solution is the double for loop:

def simple(a, b):
    count = 0
    for i in range(len(a)):
        for j in range(len(b)):
            if (a[i] - b[j] == a[j] - b[i] and i &lt;= j):
                count += 1
    return count
a = [2, -2, 5, 3]
b = [1, 5, -1, 1]
print(&#39;Pairs:&#39;, simple(a, b)) # This prints 6, the pairs being [[0, 0], [0, 1], [1, 1], [2, 2], [2, 3], [3, 3]]

But this solution is O(n^2)

Here is my O(n) solution:

I noticed that a[i] - b[j] = a[j] - b[i] can be written as a[i] + b[i] = a[j] + b[j]

So i decided to create two lists list_i and list_j, both the same length as a and b.

In list_i I stored all the results of a[i] + b[i]

In list_j I stored all the results of a[j] + b[j]

Then all I had to do was compare the values at each index for the 2 lists mentioned above and make sure they were the same and that i &lt;= j. The code is below, but it's incorrect.

def complex(a, b):
    n = len(a)
    count = 0
    list_i = [0] * n
    list_j = [0] * n

    for i in range(n):
        list_i[i] = a[i] + b[i]

    for j in range(n):
        list_j[j] = a[j] + b[j]

    i, j = 0, 0
    while (i &lt;= j and i &lt; n and j &lt; n):
        if (list_i[i] == list_j[j]):
            count += 1
            j += 1
        else:
            i = j
    return count

While doing that, I noticed that the index where i and j are the same, will always be true.

So I don't have to check them. I can simply add the line of a to the final count.

I also noticed that I only needed to check adjacent values such that i = i + 1.

This meant that I only had to have one list of sums and in a for loop check to see if sum_list[i] == sum_list[i + 1]

So my final code is as follows:

def complex(a, b):
    n = len(a)
    count = 0
    list_sum = [0] * n

    for i in range(n):
        list_sum[i] = a[i] + b[i]

    for i in range(n - 1):
        if list_sum[i] == list_sum[i + 1]:
            count += 1
    return count + len(a)
a = [2, -2, 5, 3]
b = [1, 5, -1, 1]
print(&#39;Pairs:&#39;, complex(a, b)) # This prints 6, the pairs being [[0, 0], [0, 1], [1, 1], [2, 2], [2, 3], [3, 3]]

This gives the correct solution and is O(n), but is it correct?

Also is there a better solution using sets or dicts?

答案1

得分: 4

如果您只对成对数量感兴趣,而不关心具体的成对元素,可以使用字典在O(n)时间内解决这个问题。

首先,让c成为ab的逐元素求和(即c = [ai + bi for ai, bi in zip(a, b)])。注意到约束条件a[i] + b[i] == a[j] + b[j]意味着我们要找到i, j使得c[i] == c[j]

对于c的每个唯一v,如果有n个索引使得c[i] == v,那么就会有n * (n + 1) // 2对索引使得c[i] == c[j]i <= j。因此,我们只需计算每个唯一值的c的索引数量,并应用以下公式:

from collections import Counter

def count_pairs(a, b):
    c = [ai + bi for ai, bi in zip(a, b)]
    ctr = Counter(c)
    return sum(n * (n + 1) // 2 for n in ctr.values())

这在摊销时间O(n)内运行:构建c需要O(n)时间,构建ctr需要摊销时间O(n)(它是一个仅存储每个唯一值计数的dict),计算总和需要O(n)时间(计算仅在c的每个唯一值中运行一次,最多有n个这样的唯一值)。

英文:

If you're only interested in the number of pairs, not the pairs themselves, this can be solved in O(n) with a dictionary.

First, let c be the elementwise sum of a and b (i.e. c = [ai + bi for ai, bi in zip(a, b)]). Observe that the constraint a[i] + b[i] == a[j] + b[j] implies that we're looking for i, j such that c[i] == c[j].

For each unique value v of c, if there are n indices such that c[i] == v, then there will be n * (n + 1) // 2 pairs of indices such that c[i] == c[j] and i &lt;= j. Therefore, all we need to do is count the number of indices per unique value of c and apply this formula:

from collections import Counter

def count_pairs(a, b):
    c = [ai + bi for ai, bi in zip(a, b)]
    ctr = Counter(c)
    return sum(n * (n + 1) // 2 for n in ctr.values())

This runs in O(n) amortized time: constructing c takes O(n) time, constructing ctr takes O(n) amortized time (it is a dict which stores only the count of each unique value), and computing the sum takes O(n) time (the calculation runs once per unique value in c, and there are at most n such unique values).

答案2

得分: 2

一个简单的O(N)解决方案可以通过使用哈希映射来实现,这允许O(1)的查找。

英文:

An easy O(N) solution can be found by using a hashmap, which allows O(1) lookup.

答案3

得分: 0

我还注意到只需要检查相邻的值。

如果list_sum列表已排序,则是正确的,但在一般情况下不是如此(例如,如果list_sum[10, 20, 10],来自于a = [1, 2, 3]b = [9, 18, 7])。

因此,要修复您的代码,在第3个for循环之前执行list_sum.sort()。不过这会使其变慢,因为排序需要O(N * log(N))的时间。

此外,您可能需要更多地计算相同值的较长运行。如果list_sum[10, 10, 10],您的complex函数返回5,但它应该返回6才对。

英文:

> I also noticed that I only needed to check adjacent values

This is true if the list_sum list is sorted, but it's not true in the general case (e.g. if list_sum is [10, 20, 10], coming from e.g. a = [1, 2, 3], b = [9, 18, 7]).

So to fix your code, do a list_sum.sort() before your 3rd for loop. This will make it slower though, because the sorting takes O(N * log(N)) time.

Also you may have to count longer runs of the same value more. Your complex function returns 5 if list_sum is [10, 10, 10], but shouldn't it return 6 instead?

答案4

得分: 0

"给出一个O(n)的解决方案"

嗯...考虑一下当ab都是包含n个0的数组时的情况,例如:

n = 2

a = [0, 0]
b = [0, 0]
解决方案 = [[0, 0], [0, 1], [1, 1]]
解决方案长度: 3

n = 3

a = [0, 0, 0]
b = [0, 0, 0]
解决方案 = [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]
解决方案长度: 6

n = 4

a = [0, 0, 0, 0]
b = [0, 0, 0, 0]
解决方案 = [[0, 0], [0, 1], [0, 2], [0, 3], [1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]
解决方案长度: 10

一般情况下,当ab是包含n个0的数组时,解决方案的长度是三角数;f(n) = n * (n + 1) * 1/2 - 这是二次增长,看起来不可能在O(n^2)时间内找到更快的方法。

我们是否遗漏了一些问题的细节?

  • 例如,返回配对数而不是配对本身?
  • ab是否已排序?
  • ab中的项目是否保证是唯一的?
英文:

"Give an O(n) solution"

Hmmmm... consider cases where a and b are both arrays containing n 0s each, e.g.:

n = 2

a = [ 0, 0 ]
b = [ 0, 0 ]
solution = [ [ 0, 0 ], [ 0, 1 ], [ 1, 1 ] ]
solution length: 3

n = 3

a = [ 0, 0, 0 ]
b = [ 0, 0, 0 ]
solution = [ [ 0, 0 ], [ 0, 1 ], [ 0, 2 ], [ 1, 1 ], [ 1, 2 ], [ 2, 2 ] ]
solution length: 6

n = 4

a = [ 0, 0, 0, 0 ]
b = [ 0, 0, 0, 0 ]
solution = [ [ 0, 0 ], [ 0, 1 ], [ 0, 2 ], [ 0, 3 ], [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 2 ], [ 2, 3 ], [ 3, 3 ] ]
solution length: 10

In general when a and b are arrays of n 0s, the length of the solution is the triangular numbers; f(n) = n * (n + 1) * 1/2 - this is quadratic growth, and doesn't look to me like it will ever be captured in less than O(n^2) time.

Are we missing some details from the question?

  • e.g. return the number of pairs, not the pairs themselves?
  • are a and b sorted?
  • are items in a and b guaranteed to be unique?

huangapple
  • 本文由 发表于 2023年7月28日 02:55:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76782676.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定