繁体   English   中英

Python:在列表中查找所有加性逆对并将其删除

[英]Python: Find all additive inverse pairs in a list and remove them

我有一些包含加性逆对的列表(添加两个对会给你零),对于每个列表,我试图删除这些对。

下面是一些例子,我试图从这里开始:

lst1 = [326, -326, 336, -336, 336]
lst2 = [-35, 35, 838, -838, 440]
lst3 = [544, -544, 544]
lst4 = [-25, 25, 32, -32, -32, 56, 79]

对此:

lst1 = [336]
lst2 = [440]
lst3 = [544]
lst4 = [-32, 56, 79]

您可以看到,在每个列表中,加法逆对被删除,留下没有加逆的值,或 1 个额外的非镜像值。

我希望我能分享我尝试过的东西,但我已经研究了几天,我想出的任何逻辑都与lst4的示例lst4

我似乎找到了一个解决方案,它产生了我所需要的东西,但是它是用 R 编写的,我没有经验,所以我无法理解逻辑。

关于如何克服这个问题的任何想法?

您可以使用defaultdict来跟踪加法对以及处理重复项:

from collections import defaultdict
from itertools import chain
def remove_additive_pairs(sequence: list) -> list:
    """ Remove additive pairs from given sequence. """
    result = defaultdict(list)
    
    for element in sequence:
        if -element in result:
            del result[-element]
        else:
            result[element].append(element)
            
    return list(chain.from_iterable(result.values()))

>>> lst5 = [-25, 25, 32, -25, 25, 32]
>>> for seq in [lst1, lst2, lst3, lst4, lst5]:
        print(remove_additive_pairs(seq))

[336]
[440]
[544]
[-32, 56, 79]
[32, 32]

当然,您可以在不导入itertools.chaincollections.defaultdict情况下执行相同操作:

def chain(args):
    for lst in args:
        for elem in lst:
            yield elem

            
def remove_additive_pairs(sequence: list) -> list:
    """ Remove additive pairs from given sequence. """
    result = dict()
    
    for element in sequence:
        if -element in result:
            del result[-element]
        else:
            result.setdefault(element, []).append(element)
            
    return list(chain(result.values()))

defaultdict是一个dict-like容器,可以为missing keys提供default值。 例如:

>>> d = defaultdict(list)
>>> d[5]
[]

因此,定义defaultdict(list)可确保当我们得到一个数字,其倒数不在键中时,我们可以将该数字作为key添加到字典中,并一步appendappendlist中的值中。

首先,计算每个值在列表中出现的次数。 然后遍历列表,要么填充新列表,要么丢弃该值并递减反向值的计数器。

from collections import Counter

def remove_inverses(lst):
  value_counts = Counter(lst)
  result = []
  for element in lst:
    if value_counts.get(-element):
      value_counts[-element] -= 1
    else:
      result.append(element)
  return result

您可以跟踪您在(默认)字典文档中看到的所有元素

字典是一种保存键值对并允许快速查找的数据结构。 在这种情况下,我们输入列表的元素将是我们的键,而值将是一个计数。 您可以像这样访问字典中的值: counter[key] = value

默认字典只是让它在key还不是字典的一部分时,它会自动为它生成一个默认值,而不是抛出KeyError 所以我们定义了counter = collections.defaultdict(int) int为尚未定义的键的值指定默认工厂函数。

每次看到未配对的数字时,都会增加该数字的计数器。 每次您在计数器中找到一个有一对的数字时,您就减少该对的计数器。 最后,计数器的键将是列表的元素,值将是找到的该元素的未配对实例的数量,因此我们根据需要将其转换为列表。 请注意,这不会保留原始列表中元素的顺序。

import collections

def filterInverse(lst):
    counter = collections.defaultdict(int)
    for num in lst:
        inverse = -num
        if counter[inverse] > 0:
            # Inverse was found previously in the counter
            # Decrement counter[inverse]
            counter[inverse] -= 1
            # And do nothing else with this number
        else: # Inverse wasn't found in the counter
            # This is an unpaired number so add it to the counter
            counter[num] += 1
    # Finally, go over the dict and return the keys whose values are nonzero
    # Repeat each item by its count
    # print(counter)
    rv = []
    for num, ct in counter.items():
        for _ in range(ct):
            rv.append(num)
    return rv

使用您问题中的输入以及我们在评论中讨论的内容运行此程序,

>>> filterInverse(lst1)
Out: [336]

>>> filterInverse(lst2)
Out: [440]

>>> filterInverse(lst3)
Out: [544]

>>> filterInverse(lst4)
Out: [-32, 56, 79]

>>> lst5 = [-1, 0, 1, -1, -1]
>>> filterInverse(lst5)
Out: [-1, -1, 0]

如果您坚决反对使用defaultdict ,您可以通过使用常规dict.get(key, default) 文档获得相同的行为:

counter = dict()

...
    ...
        if counter.get(inverse, 0) > 0:
            # Inverse was found previously in the counter
            # Decrement counter[inverse]
            counter[inverse] = counter.get(inverse, 0) - 1
            # And do nothing else with this number
        else: # Inverse wasn't found in the counter
            # This is an unpaired number so add it to the counter
            counter[num] = counter.get(num, 0) + 1
    ...
...

使用很棒的collections.Counter

from collections import Counter
from itertools import repeat

def remove_opposite_pairs(seq):
    dplus = Counter(seq)
    dminus = Counter(-x for x in seq)
    for x,n in (dplus - dminus).items():
        yield from repeat(x, n)

lsts = [[326, -326, 336, -336, 336],
        [-35, 35, 838, -838, 440],
        [544, -544, 544],
        [-25, 25, 32, -32, -32, 56, 79],
        [-1, 0, 1, -1, -1]]

for l in lsts:
    print(list(remove_opposite_pairs(l)))
# [336]
# [440]
# [544]
# [-32, 56, 79]
# [-1, -1]

注意 0 上的奇怪行为,因为 0 是它自己的对立面。 我可以解决它,但首先您需要编辑您的问题以准确指定如何处理 0。

这是一个可能的修复方法:

from collections import Counter
from itertools import repeat

def remove_opposite_pairs(seq):
    dplus = Counter(seq)
    dminus = Counter(-x for x in seq)
    for x,n in (dplus - dminus).items():
        yield from repeat(x, n)
    if dplus[0] % 2 == 1:
        yield 0

lsts = [[-1, 0, 1, -1, -1],
        [0, 0],
        [0, 0, 0]]

for l in lsts:
    print(list(remove_opposite_pairs(l)))
# [-1, -1, 0]
# []
# [0]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM