【问题标题】:Python list comprehension with multiple lists and an if else conditon具有多个列表和 if else 条件的 Python 列表理解
【发布时间】:2017-03-20 12:29:48
【问题描述】:

我想遍历两个同样长的集合,并确定每个集合中的元素是否也在一个数组中。

这是一个我已经解决的 Hackerrank 问题。但是,我正在使用 Hackerrank 来进一步了解 Python。我一直在学习列表理解,虽然我确实相信我如何尝试使用它被认为是糟糕的生产代码,但我仍然想根据自己的知识探索语言语法的可能性。

这是设置它的代码:

n, m = map(int, input().split())
arr = list(map(int, input().split()))
A = set(map(int, input().split()))
B = set(map(int, input().split()))

任务是为 A 和 arr 中的每个元素输出一个值为 +1 的整数,为 B 和 arr 中的每个元素输出 -1。

示例输入:

3 2
1 5 3
3 1
5 7

样本输出:

1

这样就达到了要求的结果:

print(sum([1 for a in A if a in arr]) + sum([-1 for b in B if b in arr]))

但是,这更接近我想要实现的目标:

sum([1 if a in arr else -1 if b in arr for a, b in zip(A, B)])

编辑(实际上更接近):

print(sum(1 if a in arr -1 if b in arr for a, b in zip(A, B)))

正如您所见,两者都是单行代码,因此不是要尝试减少代码,而是要了解列表理解和 Python 代码的可能性。如果这是不可能的,甚至是不好的做法,我也很感兴趣。

这是 Hackerrank 链接: https://www.hackerrank.com/challenges/no-idea

【问题讨论】:

  • 如果要立即将列表转换为集合,为什么要完全使用列表理解?您可以改用集合推导。
  • 顺便说一句,我认为你不需要列表推导,如果我记得很清楚 sum() 接受任何可迭代的,因此你可以删除 [] 并将其传递给生成器。
  • @spectras 我不知道。我将在没有列表理解的情况下尝试它。我也刚刚注意到else if 不正确,因为它应该是两个if 语句,因此即使第一个条件不正确,也会检查第二个条件
  • 我正在重新阅读,我认为试图同时做两个总和有点误导,因为你是从不同的容器中总结不同的东西。不仅它不起作用(不以奇怪的方式扭曲数据),而且从逻辑的角度来看它也没有意义。

标签: python list conditional list-comprehension


【解决方案1】:

如何转换数组来设置和取交集

s = set(arr)
print(len(A.intersection(s)) - len(B.intersection(s)))

编辑: 此解决方案不适用于arr中的重复值

【讨论】:

  • 我也在考虑这个问题,但是如果arr 可以有重复值(应该计算多次),我认为它不能正常工作。
  • @Blckknght> 实际上,问题陈述并没有说它们应该被计算多次。 “对于数组中的每个整数,如果 i ∊ A,你的幸福度就加 1”。 所以无论它在 A 中出现多少次,它都只是 1。
  • @RaviKumar:但您可以使用arr = [1, 1]A = set([1])B = set([0]) 进行测试。输出应该2,但是这段代码会生成1
  • @MartijnPieters OP 提供的解决方案遍历AB 的每个元素,并检查arr 中是否存在元素。因此,对于您的示例,答案应该是 1。
  • @RaviKumar:我知道 OP 发布了什么,但这并不意味着他们的答案是正确
【解决方案2】:

首先,放弃列表理解;使用生成器表达式将值直接输入sum()

sum(1 for a in A if a in arr)

如果A是一个集合,使用set.intersection() method提取公共值,然后取结果的长度

len(A.intersection(arr)))

这比尝试为每个值测试 arr 更快。这确实首先生成一个新的set() 对象,但是您之前创建了一个列表,所以没有太大区别。

在这一点上,简单得多只需减去第二个长度:

len(A.intersection(arr)) - len(B.intersection(arr))

您可以通过循环arr 并针对AB 测试arr 中的每个值来完全避免创建集合;这也更快,因为集合成员测试是 O(1) 常数时间:

sum(1 if v in A else -1 if v in B else 0 for v in arr)

如果值a 不存在,您的方法,即为集合A 中的每个值测试a in arr,需要对arr 进行完整列表扫描;这使得针对列表的成员资格测试成为 O(N) 线性时间问题,并且您对 A 中的每个值都这样做 并且B 中的每个值都这样做,所以你最终会得到 O( (A+B) * N) == O(KN) 时间。针对集合测试 arr 中的每个值只需 O(N * 1) == O(N) 时间。

此外,如果arr 中的值不是唯一的,您的方法实际上会导致错误的答案;您只会计算快乐或不快乐的数字一次,而问题要求每次出现时都要计算。

【讨论】:

  • 由于只是加减一个单位值,这更有意义,而且集合论的使用更简洁。
【解决方案3】:

您的解决方案很棒,但有一个问题。您对两组数字使用set 数据结构,对数组使用list。当在列表顶部应用 in 运算符时,您正在执行 O(n) 搜索,而在一组中,相同的操作是 O(logn)(在 python 中,平均情况是 O(1)!)。所以你的总时间复杂度是 O(2 * m * n) = O(m*n)。您可以以相反的方式搜索,例如:

For each element in array, if element in A then +1, if element in B then -1.

总复杂度为 O(n * 2 * logm) = O(n*logm)

更多关于python时间复杂度here

【讨论】:

  • 我是在您发帖前不久才发现这一点的。这是一个很好的观点。
【解决方案4】:

这是我提交的答案,因为它使用了我最初想到的列表理解。这只是必要的,因为数组可能有多个相同元素,因此计数需要反映这一点。

n, m = map(int, input().split())
arr = list(input().split())
A, B = set(input().split()), set(input().split())
print(sum([(e in A) - (e in B) for e in arr]))

【讨论】:

    【解决方案5】:

    解决问题的最有效方法可能是将AB 的项目放入字典(作为键),值是1-1,具体取决于它们来自哪个集合从。这将让您浏览列表 arr 并轻松获取要添加到总和的值:

    ab_dict = {a: 1 for a in A}
    ab_dict.update((b, -1) for b in B)
    
    result = sum(ab_dict.get(x, 0) for x in arr)
    

    结果的计算与在生成器表达式 (sum(1 if x in A else -1 if x in b else 0 for x in arr)) 中使用几个条件运算符具有相同的计算复杂性,但应该更快一些常数因子,因为对于每个项目只有一个 dict 查找而不是两个集合成员资格测试。

    【讨论】:

      猜你喜欢
      • 2020-08-27
      • 2020-01-07
      • 2023-03-14
      • 2017-11-14
      • 1970-01-01
      • 2012-05-03
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多