是否有一种简短的方法可以在不使用'if'和多个'and'的情况下检查值的唯一性?

Bun*_*vis 27 python duplicates

我正在编写一些代码,我需要比较一些值。关键是所有变量都不应该具有相同的值。例如:

a=1
b=2
c=3
if a != b and b != c and a != c:
    #do something 
Run Code Online (Sandbox Code Playgroud)

现在,很容易看到在具有更多变量的代码的情况下,该if语句变得很长且充满ands。有没有一种简短的方法来告诉Python,没有2个变量值应该相同。

Tae*_*ung 48

您可以尝试进行设置。

a, b, c = 1, 2, 3
if len({a,b,c}) == 3:
   # Do something
Run Code Online (Sandbox Code Playgroud)

如果将变量保存为列表,它将变得更加简单:

a = [1,2,3,4,4]
if len(set(a)) == len(a):
    # Do something
Run Code Online (Sandbox Code Playgroud)

是python集的官方文档。

正如问题中所给出的,这仅适用于可散列的对象,例如整数。对于非哈希对象,请参见@chepner的更一般的解决方案

这绝对是你应该去哈希的对象的方式,因为它需要O(n)的时间为对象的数目ñ。不可哈希对象的组合方法花费O(n ^ 2)时间。

  • @IsmaelELATIFI:我认为您将因子N丢弃了。您甚至无法在O(log(N))时间内将所有值放入树中。无论如何,在Python中排序会更容易,因为Python不附带TreeSet。 (6认同)

che*_*ner 25

假设不能使用散列,请使用itertools.combinationsall

from itertools import combinations

if all(x != y for x, y in combinations([a,b,c], 2)):
    # All values are unique
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,这在元素数量上是二次的,而可哈希解决方案(如果有)是线性的 (8认同)
  • 请给我降票以得到不必要的慢速解决方案(如@Ant所述,它是线性的,而不是线性的)。单位数大小的收藏集就可以了,但仅此而已。基于集合的解决方案更快,更短并且不需要库。从好的方面来说,这适用于不可散列的项目,并且可能消耗更少的内存(取决于“组合”的实现)。 (4认同)
  • 难道“全部”还短路吗? (2认同)

MSe*_*ert 21

这取决于您拥有的值的种类。

如果它们表现良好且可哈希化,那么您可以(如其他人已经指出的那样)简单地使用a set来查找您拥有多少个唯一值,如果这不等于总值的数量,那么您至少有两个值是等于。

def all_distinct(*values):
    return len(set(values)) == len(values)

all_distinct(1, 2, 3)  # True
all_distinct(1, 2, 2)  # False
Run Code Online (Sandbox Code Playgroud)

散列值和懒惰

如果您确实有很多值,并且想在找到一个匹配项后立即中止,则也可以延迟创建该集合。如果所有值都不同,则更为复杂,并且可能会更慢,但是如果发现重复项,则会短路:

def all_distinct(*values):
    seen = set()
    seen_add = seen.add
    last_count = 0
    for item in values:
        seen_add(item)
        new_count = len(seen)
        if new_count == last_count:
            return False
        last_count = new_count
    return True

all_distinct(1, 2, 3)  # True
all_distinct(1, 2, 2)  # False
Run Code Online (Sandbox Code Playgroud)

但是,如果值不可哈希,则将无法使用,因为set需要可哈希值。

不可散列的值

如果没有哈希值,则可以使用普通列表存储已处理的值,然后检查列表中是否已包含每个新项:

def all_distinct(*values):
    seen = []
    for item in values:
        if item in seen:
            return False
        seen.append(item)
    return True

all_distinct(1, 2, 3)  # True
all_distinct(1, 2, 2)  # False

all_distinct([1, 2], [2, 3], [3, 4])  # True
all_distinct([1, 2], [2, 3], [1, 2])  # False
Run Code Online (Sandbox Code Playgroud)

这会比较慢,因为检查值是否在列表中需要将其与列表中的每个项目进行比较。

(第三方)库解决方案

如果您不介意其他依赖关系,则也可以使用我的一个库(在PyPi和conda-forge上可用)来完成此任务iteration_utilities.all_distinct。此函数可以处理可哈希值和不可哈希值(以及它们的混合):

from iteration_utilities import all_distinct

all_distinct([1, 2, 3])  # True
all_distinct([1, 2, 2])  # False

all_distinct([[1, 2], [2, 3], [3, 4]])  # True
all_distinct([[1, 2], [2, 3], [1, 2]])  # False
Run Code Online (Sandbox Code Playgroud)

普通的留言

请注意,上述所有方法均基于这样的事实,即相等意味着“不等于”(几乎)所有内置类型都是这种情况,但不一定如此!

但是,我想指出的是,cheapers答案不需要值的可哈希性,并且不通过显式检查来依赖“平等意味着不等于” !=。它还短路,因此其行为类似于您的原始and方法。

性能

为了大致了解性能,我使用了我的另一个库(simple_benchmark

我使用了不同的可哈希输入(左)和不可哈希输入(右)。对于可哈希输入,设置方法效果最好,而对于不可哈希输入,列表方法效果更好。combinations在两种情况下,基于方法的速度似乎最慢:

在此处输入图片说明

为了方便起见,我还测试了性能,为方便起见,我考虑了前两个元素相等的情况(否则设置与前面的情况相同):

在此处输入图片说明

from iteration_utilities import all_distinct
from itertools import combinations
from simple_benchmark import BenchmarkBuilder

# First benchmark
b1 = BenchmarkBuilder()

@b1.add_function()
def all_distinct_set(values):
    return len(set(values)) == len(values)

@b1.add_function()
def all_distinct_set_sc(values):
    seen = set()
    seen_add = seen.add
    last_count = 0
    for item in values:
        seen_add(item)
        new_count = len(seen)
        if new_count == last_count:
            return False
        last_count = new_count
    return True

@b1.add_function()
def all_distinct_list(values):
    seen = []
    for item in values:
        if item in seen:
            return False
        seen.append(item)
    return True

b1.add_function(alias='all_distinct_iu')(all_distinct)

@b1.add_function()
def all_distinct_combinations(values):
    return all(x != y for x, y in combinations(values, 2))

@b1.add_arguments('number of hashable inputs')
def argument_provider():
    for exp in range(1, 12):
        size = 2**exp
        yield size, range(size)

r1 = b1.run()
r1.plot()

# Second benchmark

b2 = BenchmarkBuilder()
b2.add_function(alias='all_distinct_iu')(all_distinct)
b2.add_functions([all_distinct_combinations, all_distinct_list])

@b2.add_arguments('number of unhashable inputs')
def argument_provider():
    for exp in range(1, 12):
        size = 2**exp
        yield size, [[i] for i in range(size)]

r2 = b2.run()
r2.plot()

# Third benchmark
b3 = BenchmarkBuilder()
b3.add_function(alias='all_distinct_iu')(all_distinct)
b3.add_functions([all_distinct_set, all_distinct_set_sc, all_distinct_combinations, all_distinct_list])

@b3.add_arguments('number of hashable inputs')
def argument_provider():
    for exp in range(1, 12):
        size = 2**exp
        yield size, [0, *range(size)]

r3 = b3.run()
r3.plot()

# Fourth benchmark
b4 = BenchmarkBuilder()
b4.add_function(alias='all_distinct_iu')(all_distinct)
b4.add_functions([all_distinct_combinations, all_distinct_list])

@b4.add_arguments('number of hashable inputs')
def argument_provider():
    for exp in range(1, 12):
        size = 2**exp
        yield size, [[0], *[[i] for i in range(size)]]

r4 = b4.run()
r4.plot()
Run Code Online (Sandbox Code Playgroud)

  • @Bergi是的,麻烦。但是,检查in并执行add会调用两次哈希和(内部)find-slot-for-hash操作。根据要添加的值和集合的状态,仅将其“添加”并检查长度可能会明显更快。也许我遗漏了一些明显的东西,但是至少在我的基准测试中,`set.add` +`len(set)`方法比`in set`和`set.add`要快。 (2认同)

归档时间:

查看次数:

3325 次

最近记录:

6 年,6 月 前