我们有两个长度相等的数组
command:
和A
。同样,对于每个B
:i
代表0 <= a_i, b_i <= m
。我们要检查1<=m<= 1000000
的某个项和A
的某个项之间的单个交换是否会使数组的总和相等。
请考虑以下解决方案:
B
如果您能解释最后def fast_solution(A, B, m):
n = len(A)
sum_a = sum(A)
sum_b = sum(B)
d = sum_b - sum_a
if d % 2 == 1:
return False
d //= 2
count = counting(A, m) # returns a mapping <integer, #occurrences in A>
for i in range(n):
if 0 <= B[i] - d and B[i] - d <= m and count[B[i] - d] > 0:
return True
return False
子句的原因,我将感到很高兴。
答案 0 :(得分:3)
如果存在这样的交换,则两个值之间的差必须是总和差的一半。交换两个值意味着两个列表的总和将以相同的量变化,一个上升,另一个下降。两次更改必须总计为交换之前的总和之差,并且两个总和的变化值相同(+d
和-d
,或者 value < / em>增量,即两个交换值之间的差)。
首先,函数将d
计算为总和之间的增量,即总和增量。请注意,sum_a
可能比sum_b
大,此时结果sum_b - sum_a
为负。这仅表示B
中的某个值必须小于A
中的目标值,然后进行交换将减少sum_a
并增加sum_b
以使其相等。如果总和的奇偶校验是 odd 而不是偶数,您将永远找不到一个值,该值的增量是总和的一半,因此函数此时将返回False
。 d
的最终值是 value 增量,即两个交换值之间的差值。请记住,值增量是总和的一半。
该算法对A
中的所有值进行计数,然后测试B
中的所有值。如果A
中的 a 值与相差B
,则只能在B
和d
之间交换两个值A
中的> a 值。 A
中要与B
交换的值必须等于b_value - d
。对于负数d
(sum_a > sum_b
),它会使b_value
变小;对于正数d
,它要求b_value
是更大的数字。
if
测试旨在查看B - d
中A
中是否有可用值,但它首先测试b_value - d
是否仍在[0]范围内-m]:
0 <= B[i] - d
测试A中寻找的数字是否仍为正数。B[i] - d <= m
测试是否找到的数字仍不大于m
; B[i]
接近而d
为负。count
包含A
中数字的计数;如果count[B[i] - d] > 0
为真,则A中至少有一个这样的数字。这是一个可以交换的数字。之所以需要进行范围测试,是因为counted
列表仅保存从0到m(含)的数字的计数,而不包含负数或大于m
的数字的计数。
可以通过使用集合而不是计数功能来改进该功能。无需知道一个数字在A
中出现了多少次,而只是存在 。这将使边界检查变得过时,因为超出界限的数字根本不会出现在A
的一组值中。
一旦我们拥有一组A值,我们就可以使用disjoint来测试该b值集合中的set.isdisjoint()
是否为collections.Counter()
object:
def faster_solution(A, B, m=None): # m is ignored in this version
delta = sum(A) - sum(B)
if delta % 2 == 1:
return False
delta //= 2
return not set(A).isdisjoint(b - delta for b in B)
如果A
中的某个值等于B
中的 a 值减去差值,则返回True。 Python只会在b - delta for b in B
循环上循环,直到找到匹配项为止(此时集合不脱节,并且not
求反,结果为True),或者循环已经耗尽,因此没有这样的循环在A
中找到值,并且发现集合不相交。
所示的counter()
函数还有另一个问题:它需要的存储空间远远超过了所需的存储空间,并且与{{3}}相比,它的速度很慢,而{{3}}具有实现优化的循环以进行计数。 Counter()
使用字典(哈希图)仅存储大于0的计数。
上面设置的解决方案胜过“快速解决方案”:
>>> import timeit, random
>>> m = 1000000
>>> testdata = [random.randrange(m + 1) for _ in range(1000)]
>>> testinputs = (testdata[:], testdata[:])
>>> random.shuffle(testinputs[0]) # The order of A differs from B
>>> testinputs[1][-1] -= testinputs[1][-1] // 2 # now the two sums differ by an even amount, guaranteed to be in range
>>> assert testinputs[1][-1] > 0 # make sure the original random value was not 0 or 1.
>>> # note: It's the *last value in B* that makes it possible to swap;
... # this is one of two worst-case scenarios (the other is even-delta-no-swap).
...
>>> assert fast_solution(*testinputs, m) # the original finds a solution
>>> assert faster_solution(*testinputs, m) # my version finds a solution
>>> timeit.timeit("f(*ab, m)", "from __main__ import fast_solution as f, testinputs as ab, m", number=1000)
2.3270092820748687
>>> timeit.timeit("f(*ab, m)", "from __main__ import faster_solution as f, testinputs as ab, m", number=1000)
0.13949943508487195
不使用计数器,而使用Python的set功能使输入长度为1000的速度快17倍!
故事的寓意:使用您选择的语言中可用的最佳工具,并认真思考解决问题的实际需要。 Python的内置类型和操作通常可以使您避免在Python字节码中运行关键循环,从而大大减少了算法的恒定时间因素。
答案 1 :(得分:3)
来自https://www.geeksforgeeks.org/find-a-pair-swapping-which-makes-sum-of-two-arrays-same:
我们正在寻找两个值a和b,例如:
sumA - a + b = sumB - b + a
2b - 2a = sumB - sumA
b - a = (sumB - sumA) / 2
(sumB - sumA) / 2
是目标差异d
count[B[i] - d] > 0
-这只是意味着数组A
中必须存在这样的值才能满足条件
答案 2 :(得分:1)
此for循环最后搜索B
数组的每个元素。要成为索引i
上的交换元素,它必须满足两个条件:
B[i] - d
必须介于0
和m
之间。您可以想象2 * d
比sum(B)
大sum(A)
,因此通过将B[i]
与B[i] - d
交换,将数组A
与增益{{ 1}}和数组d
将丢失它,从而使差异增加B
2 * d
必须存在于A 尽管在代码中间重新定义了B[i] - d
,但对理解并没有好处:)