给定一个整数数组,找到数组中所有有序元素对的数量,其总和位于给定范围[a,b]
这是针对相同
的O(n ^ 2)解决方案'''
counts all pairs in array such that the
sum of pair lies in the range a and b
'''
def countpairs(array, a, b):
num_of_pairs = 0
for i in range(len(array)):
for j in range(i+1,len(array)):
total = array[i] + array[j]
if total >= a and total <= b:
num_of_pairs += 1
return num_of_pairs
我知道我的解决方案不是最佳的 这样做有什么更好的算法。
答案 0 :(得分:14)
时间复杂度当然是输出敏感的,但这仍然优于现有的算法:
O(nlogn) + O(k)
其中k是满足条件的对的数量。
注意:如果您只需计算对的数量,您可以在O(nlogn)
中执行此操作。修改上述算法,以便搜索[b - x](或下一个较小的元素)。通过这种方式,您可以计算“匹配”的数量。每个元素在O(logn)
中只有第一个和最后一个匹配的索引。然后,这只是一个总结得到最终计数的问题。这样,最初的O(nlogn)
排序步骤占主导地位。
答案 1 :(得分:11)
首先对数组进行排序,然后按两个索引计算对。两个索引方法类似于2-sum problem中的方法,这避免了N
次的二进制搜索。该算法的耗时是Sort Complexity + O(N)
,通常,sort是O(NInN),因此该方法是O(NInN)。该算法的思想是,对于索引i
,找到下限和上限,使a <= arr[i]+arr[low] <= arr[i]+arr[high] <= b
和i
增加时,我们应该做的是减少{{1}和low
来保持条件。为避免两次计算同一对,我们保留high
,同时保留low > i
。以下计数方法的复杂性是O(N),因为在low <= high
中,我们可以做的是while loop
或++i
或--low
,并且最多--high
此类行动。
N
答案 2 :(得分:6)
计算工作对的问题可以在排序时间+ O(N)中完成。这比Ani给出的解决方案更快,即排序时间+ O(N log N)。这个想法是这样的。首先你排序。然后,您运行几乎相同的单通道算法两次。然后,您可以使用两个单通算法的结果来计算答案。
我们第一次运行单遍算法时,我们将创建一个新数组,列出可以与该索引合作的最小索引,以得到大于a的总和。例如:
a = 6
array = [-20, 1, 3, 4, 8, 11]
output = [6, 4, 2, 2, 1, 1]
因此,数组索引1处的数字是1(基于0的索引)。它可以配对以获得超过6的最小数字是8,它在索引4处。因此输出[1] = 4. -20不能与任何东西配对,因此输出[0] = 6(从边界)。另一个例子:输出[4] = 1,因为8(索引4)可以与1(索引1)或之后的任何数字配对,总和超过6。
你现在需要做的就是让自己相信这是O(N)。它是。代码是:
i, j = 0, 5
while i - j <= 0:
if array[i] + array[j] >= a:
output[j] = i
j -= 1
else:
output[i] = j + 1
i += 1
想想从边缘开始并向内工作的两个指针。它是O(N)。你现在做同样的事情,只是条件b&lt; = a:
while i-j <= 0:
if array[i] + array[j] <= b:
output2[i] = j
i += 1
else:
output2[j] = i-1
j-=1
在我们的示例中,此代码为您提供(数组和b以供参考):
b = 9
array = [-20, 1, 3, 4, 8, 11]
output2 = [5, 4, 3, 3, 1, 0]
但是现在,output和output2包含了我们需要的所有信息,因为它们包含配对的有效索引范围。 output是它可以配对的最小索引,output2是它可以配对的最大索引。差值+ 1是该位置的配对数。因此对于第一个位置(对应于-20),有5 - 6 + 1 = 0对。对于1,有4-4 + 1对,数字在索引4为8.另一个微妙,这个算法计算自我配对,所以如果你不想要它,你必须减去。例如。 3似乎包含3-2 + 1 = 2对,一个在索引2,一个在索引3.当然,3本身在索引2,所以其中一个是自配对,另一个是与4配对。只要输出和输出2的索引范围包含您正在查看的索引本身,您只需要减去一个。在代码中,您可以写:
answer = [o2 - o + 1 - (o <= i <= o2) for i, (o, o2) in enumerate(zip(output, output2))]
哪个收益率:
answer = [0, 1, 1, 1, 1, 0]
总和为4,对应于(1,8),(3,4),(4,3),(8,1)
无论如何,正如你所看到的,这是sort + O(N),这是最佳的。
编辑:要求完整实施。提供。供参考,完整代码:
def count_ranged_pairs(x, a, b):
x.sort()
output = [0] * len(x)
output2 = [0] * len(x)
i, j = 0, len(x)-1
while i - j <= 0:
if x[i] + x[j] >= a:
output[j] = i
j -= 1
else:
output[i] = j + 1
i += 1
i, j = 0, len(x) - 1
while i-j <= 0:
if x[i] + x[j] <= b:
output2[i] = j
i += 1
else:
output2[j] = i-1
j -=1
answer = [o2 - o + 1 - (o <= i <= o2) for i, (o, o2) in enumerate(zip(output, output2))]
return sum(answer)/2
答案 3 :(得分:5)
from itertools import ifilter, combinations
def countpairs2(array, a, b):
pairInRange = lambda x: sum(x) >= a and sum(x) <= b
filtered = ifilter(pairInRange, combinations(array, 2))
return sum([2 for x in filtered])
我认为Itertools库非常方便。我还注意到你计算了两次对,例如你把(1,3)和(3,1)算作两种不同的组合。如果您不想这样,只需将最后一行中的2更改为1即可。
注意:最后一个可以更改为return len(list(filtered)) * 2
。这可以更快,但代价是使用更多RAM。
答案 4 :(得分:3)
由于对数据有一些限制,我们可以在线性时间内解决问题(对不起Java,我对Python不太熟悉):
public class Program {
public static void main(String[] args) {
test(new int[]{-2, -1, 0, 1, 3, -3}, -1, 2);
test(new int[]{100,200,300}, 300, 300);
test(new int[]{100}, 1, 1000);
test(new int[]{-1, 0, 0, 0, 1, 1, 1000}, -1, 2);
}
public static int countPairs(int[] input, int a, int b) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int el : input) {
max = Math.max(max, el);
min = Math.min(min, el);
}
int d = max - min + 1; // "Diameter" of the array
// Build naive hash-map of input: Map all elements to range [0; d]
int[] lookup = new int[d];
for (int el : input) {
lookup[el - min]++;
}
// a and b also needs to be adjusted
int a1 = a - min;
int b1 = b - min;
int[] counts = lookup; // Just rename
// i-th element contain count of lookup elements in range [0; i]
for (int i = 1; i < counts.length; ++i) {
counts[i] += counts[i - 1];
}
int res = 0;
for (int el : input) {
int lo = a1 - el; // el2 >= lo
int hi = b1 - el; // el2 <= hi
lo = Math.max(lo, 0);
hi = Math.min(hi, d - 1);
if (lo <= hi) {
res += counts[hi];
if (lo > 0) {
res -= counts[lo - 1];
}
}
// Exclude pair with same element
if (a <= 2*el && 2*el <= b) {
--res;
}
}
// Calculated pairs are ordered, divide by 2
return res / 2;
}
public static int naive(int[] ar, int a, int b) {
int res = 0;
for (int i = 0; i < ar.length; ++i) {
for (int j = i + 1; j < ar.length; ++j) {
int sum = ar[i] + ar[j];
if (a <= sum && sum <= b) {
++res;
}
}
}
return res;
}
private static void test(int[] input, int a, int b) {
int naiveSol = naive(input, a, b);
int optimizedSol = countPairs(input, a, b);
if (naiveSol != optimizedSol) {
System.out.println("Problem!!!");
}
}
}
对于数组的每个元素,我们知道该对的第二个元素可以放置的范围。该算法的核心是给出范围内元素的计数[a; b]在O(1)时间内。
产生的复杂度为O(max(N,D)),其中D是数组的max和min元素之间的差异。如果该值与N的顺序相同 - 复杂度为O(N)。
注意:
if (a <= 2*el && 2*el <= b)
是必需的,因为算法总是计算对(a [i],a [i])另一种线性算法是基数排序+线性对计数。
修改即可。如果D远小于N并且不允许修改输入数组,则此算法可以非常好。对于这种情况的替代选项将略微修改,计数排序与计数数组(附加的O(D)内存),但不将排序的元素填充回输入数组。可以使用对数计数来使用计数数组而不是完全排序的数组。
答案 5 :(得分:2)
我有一个解决方案(实际上是2个解决方案;-))。用python编写:
def find_count(input_list, min, max):
count = 0
range_diff = max - min
for i in range(len(input_list)):
if input_list[i]*2 >= min and input_list[i]*2 <= max:
count += 1
for j in range(i+1, len(input_list)):
input_sum = input_list[i] + input_list[j]
if input_sum >= min and input_sum <= max:
count += 2
这将使nCr(n次组合)次数达到最大值,并为您提供所需的计数。这比排序列表然后找到范围内的对更好。如果组合失败的元素数量更多以及所有数字都是正整数,我们可以通过添加检查元素的条件来更好地改善结果,
这样的事情:
# list_maximum is the maximum number of the list (i.e) max(input_list), if already known
def find_count(input_list, min, max, list_maximum):
count = 0
range_diff = max - min
for i in range(len(input_list)):
if input_list[i] > max or input_list[i] + list_maximum < min:
continue
if input_list[i]*2 >= min and input_list[i]*2 <= max:
count += 1
for j in range(i+1, len(input_list)):
input_sum = input_list[i] + input_list[j]
if input_sum >= min and input_sum <= max:
count += 2
我也很乐意学习比这更好的解决方案:-)如果我遇到一个,我会更新这个答案。
答案 6 :(得分:1)
我相信这是一个简单的数学问题,可以用numpy
来解决,没有循环而我们没有排序。我不完全确定,但我认为在更糟糕的情况下复杂度为O(N ^ 2)(希望能够通过numpy中的时间复杂性更熟悉的人对此进行一些确认)。
无论如何,这是我的解决方案:
import numpy as np
def count_pairs(input_array, min, max):
A = np.array(input_array)
A_ones = np.ones((len(A),len(A)))
A_matrix = A*A_ones
result = np.transpose(A_matrix) + A_matrix
result = np.triu(result,0)
np.fill_diagonal(result,0)
count = ((result > min) & (result < max)).sum()
return count
现在让我们来看看 - 首先我创建一个矩阵,其中的列代表我们的数字:
A = np.array(input_array)
A_ones = np.ones((len(A),len(A)))
A_matrix = A*A_ones
假设我们的输入数组看起来像[1,1,2,2,3,-1]
,因此,此时此值应为A_matrix
。
[[ 1. 1. 2. 2. 3. -1.]
[ 1. 1. 2. 2. 3. -1.]
[ 1. 1. 2. 2. 3. -1.]
[ 1. 1. 2. 2. 3. -1.]
[ 1. 1. 2. 2. 3. -1.]
[ 1. 1. 2. 2. 3. -1.]]
如果我将其添加到自身的转置中......
result = np.transpose(A_matrix) + A_matrix
...我应该得到一个表示对的总和的所有组合的矩阵:
[[ 2. 2. 3. 3. 4. 0.]
[ 2. 2. 3. 3. 4. 0.]
[ 3. 3. 4. 4. 5. 1.]
[ 3. 3. 4. 4. 5. 1.]
[ 4. 4. 5. 5. 6. 2.]
[ 0. 0. 1. 1. 2. -2.]]
当然,该矩阵在对角线上镜像,因为对(1,2)和(2,1)产生相同的结果。我们不想考虑这些重复的条目。我们也不想考虑项目的总和,所以让我们清理我们的数组:
result = np.triu(result,0)
np.fill_diagonal(result,0)
我们的结果现在看起来像:
[[ 0. 2. 3. 3. 4. 0.]
[ 0. 0. 3. 3. 4. 0.]
[ 0. 0. 0. 4. 5. 1.]
[ 0. 0. 0. 0. 5. 1.]
[ 0. 0. 0. 0. 0. 2.]
[ 0. 0. 0. 0. 0. 0.]]
剩下的就是计算通过我们标准的项目。
count = ((result > min) & (result < max)).sum()
如果0
位于可接受的域中,则此方法无法正常工作,但我确信操纵上面的结果矩阵将这些0转换为某些字段会很简单其他毫无意义的数字......
答案 7 :(得分:1)
我们可以简单地检查是否使用关系运算符 数组元素i和j的总和在指定的范围内。
def get_numOfPairs(array, start, stop):
num_of_pairs = 0
array_length = len(array)
for i in range(array_length):
for j in range(i+1, array_length):
if sum([array[i], array[j]]) in range(start, stop):
num_of_pairs += 1
return num_of_pairs
答案 8 :(得分:0)
n = int(input())
ar = list(map(int, input().rstrip().split()))[:n]
count=0
uniq=[]
for i in range(n):
if ar[i] not in uniq:
uniq.append(ar[i])
for j in uniq:
if ((ar.count(j))%2==0):
count=count+((ar.count(j))/2)
if ((ar.count(j))%2!=0) & (((ar.count(j))-1)%2==0):
count=count+((ar.count(j)-1)/2)
print(int(count))