【问题标题】:Most pythonic way to select at random an element in a list with conditions在有条件的列表中随机选择一个元素的最pythonic方法
【发布时间】:2017-05-29 14:23:54
【问题描述】:

我有两个列表:

list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list2 = [1, 4, 5]

我想从list1 中选择一个元素,但它不应该属于list2

我有一个使用 while 循环的解决方案,但我想要一个更 Python 和优雅的内衬。

【问题讨论】:

标签: python python-3.x random conditional-statements


【解决方案1】:

如果您的元素是独一无二的,您可以使用设置差异。 (将 list1 转换为一个集合并从 list2 中删除元素)。然后随机抽取一个样本。

random.choice(list(set(list1).difference(list2)))

【讨论】:

    【解决方案2】:
    [item for item in list1 if not in list2]
    

    为了让它更快一点(因为在集合中查找比在列表中更快):

    list2_items = set(list2)
    [item for item in list1 if not in list2_items]
    

    或带有过滤功能(你将在 Python3 中得到一个生成器对象

    filter(lambda item: item not in list2, list1)
    

    将 list2 转换为 set 也会加快此处的过滤速度。

    如需了解更多信息,请阅读list comprehensions

    更新:我似乎错过了关于一个随机值的观点。好吧,您仍然可以使用列表推导式,但使用前面提到的 random.choice

    import random
    random.choice([item for item in list1 if not in list2_items])
    

    它会过滤选择,然后随机获得一个。 @zeehio 响应看起来像是更好的解决方案。

    【讨论】:

    • 这不涉及随机选择。
    • 是的,感谢您的指出。我错过了问题标签并误解了它。
    【解决方案3】:
    import random
    import itertools
    
    next(item for item in (random.choice(list1) for _ in itertools.count()) if item not in list2)
    

    相当于:

    while True:
        item = random.choice(list1)
        if item not in list2:
            break
    

    【讨论】:

    • 没有错,但是可能需要无限次迭代的东西感觉不对。
    • @chepner 没错,但set 方法可能是错误的,因为它们需要可散列的值并且丢弃重复项(因此可能会倾斜概率)。也许检查一些元素是否保留在list1 中会很好,但否则很难有一个“一个人统治所有”的方法。在这种情况下,它很少需要超过 2 次尝试,它是 O(1),而 set-approaches 是 O(n),具有相当高的常数因子。
    • 如果选择出现在list2 中的元素的概率很高,那么在调用random.choice 之前 过滤list1 可能是值得的。跨度>
    • 没错。对于包含可哈希值的长迭代,set 绝对是更好的选择。
    • 当不符合条件时要小心死循环
    【解决方案4】:

    您可能希望像这样使用sets:

    list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    list2 = [1, 4, 5]
    
    import random
    
    print(random.choice([x for x in set(list1).difference(list2)]))  # kudos @chepner
    

    这样我们从set(list1) - set(list2) 中随机抽取:list1 中的元素,而不是list2 中的元素。当列表 1 和 2 变大时,这种方法也可以很好地扩展。


    正如@MSeifert 所注意到的,将list1 转换为set 将删除list1 中可能存在的任何重复元素,从而改变概率。如果list1 在一般情况下可能包含重复元素,您可能希望这样做:

    print(random.choice([x for x in list1 if x not in list2]))
    

    【讨论】:

    • 使用set(list1).difference(list2),你不需要构造一个显式的第二个集合。 difference 将任何可迭代对象作为参数,而不仅仅是一个集合。
    • @chepner 那么你的意思可能是set(list1).difference(list2),对吧?
    • set2, list2, 随便 :) 谢谢。
    • 另外需要注意的是,这将丢弃list1 中的所有重复项(不仅是list2 中存在的那些),因此会扭曲概率。
    【解决方案5】:

    不导入随机库:

    print((list(set(list1) - set(list2))).pop())
    

    在 pop() 中你可以给出你想要选择的元素的索引,它会弹出那个元素 示例:用于选择索引 1 的列表(从新列表中),((list(set(list1) - set(list2))).pop(1))

    这里(list(set(list1) - set(list2)) 代码将创建一个新列表,其中仅包含第一个列表中不存在于第二个列表中的项目,

    【讨论】:

      猜你喜欢
      • 2012-04-20
      • 1970-01-01
      • 2016-02-15
      • 2018-03-06
      • 2023-01-11
      • 2021-12-30
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多