[英]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.chain
或collections.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
添加到字典中,并一步append
其append
到list
中的值中。
首先,计算每个值在列表中出现的次数。 然后遍历列表,要么填充新列表,要么丢弃该值并递减反向值的计数器。
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.