在A和B之间交换元素以获得总和相等

时间:2018-06-19 14:11:02

标签: python algorithm

  

我们有两个长度相等的数组command:A。同样,对于每个Bi代表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 子句的原因,我将感到很高兴。

Source of the problem

3 个答案:

答案 0 :(得分:3)

如果存在这样的交换,则两个值之间的差必须是总和差的一半。交换两个值意味着两个列表的总和将以相同的量变化,一个上升,另一个下降。两次更改必须总计为交换之前的总和之差,并且两个总和的变化值相同(+d-d,或者 value < / em>增量,即两个交换值之间的差)。

首先,函数将d计算为总和之间的增量,即总和增量。请注意,sum_a可能比sum_b,此时结果sum_b - sum_a为负。这仅表示B中的某个值必须小于A中的目标值,然后进行交换将减少sum_a并增加sum_b以使其相等。如果总和的奇偶校验是 odd 而不是偶数,您将永远找不到一个值,该值的增量是总和的一半,因此函数此时将返回Falsed的最终值是 value 增量,即两个交换值之间的差值。请记住,值增量是总和的一半。

该算法对A中的所有值进行计数,然后测试B中的所有值。如果A中的 a 值与相差B,则只能在Bd之间交换两个值A中的> a 值。 A中要与B交换的值必须等于b_value - d。对于负数dsum_a > sum_b),它会使b_value变小;对于正数d,它要求b_value是更大的数字。

if测试旨在查看B - dA中是否有可用值,但它首先测试b_value - d是否仍在[0]范围内-m]:

  • 0 <= B[i] - d测试A中寻找的数字是否仍为正数。
  • B[i] - d <= m测试是否找到的数字仍不大于mB[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必须介于0m之间。您可以想象2 * dsum(B)sum(A),因此通过将B[i]B[i] - d交换,将数组A与增益{{ 1}}和数组d将丢失它,从而使差异增加B
  • 2 * d必须存在于A

尽管在代码中间重新定义了B[i] - d,但对理解并没有好处:)