繁体   English   中英

你如何在 Python 中获得两个变量的逻辑异或?

[英]How do you get the logical xor of two variables in Python?

你如何在 Python 中获得两个变量的逻辑异或?

例如,我有两个希望是字符串的变量。 我想测试其中只有一个包含 True 值(不是 None 或空字符串):

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")
if logical_xor(str1, str2):
    print "ok"
else:
    print "bad"

^运算符似乎是按位的,并没有在所有对象上定义:

>>> 1 ^ 1
0
>>> 2 ^ 1
3
>>> "abc" ^ ""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for ^: 'str' and 'str'

如果您已经将输入规范化为布尔值,则 != 是异或。

bool(a) != bool(b)

您始终可以使用 xor 的定义从其他逻辑运算中计算它:

(a and not b) or (not a and b)

但这对我来说有点太冗长了,乍一看并不是特别清楚。 另一种方法是:

bool(a) ^ bool(b)

两个布尔值的异或运算符是逻辑异或(与整数不同,它是按位的)。 这是有道理的,因为bool只是int的子类,但被实现为只有值01 当域被限制为01时,逻辑异或等效于按位异或。

所以logical_xor函数将实现如下:

def logical_xor(str1, str2):
    return bool(str1) ^ bool(str2)

归功于 Python-3000 邮件列表中的 Nick Coghlan

按位异或已经内置于 Python 中,位于operator模块中(与^运算符相同):

from operator import xor
xor(bool(a), bool(b))  # Note: converting to bools is essential

正如Zach解释的那样,您可以使用:

xor = bool(a) ^ bool(b)

就个人而言,我喜欢稍微不同的方言:

xor = bool(a) + bool(b) == 1

这种方言的灵感来自我在学校学到的逻辑图表语言,其中“OR”由包含≥1 (大于或等于1)的框表示,“XOR”由包含=1的框表示。

这具有正确实现异或多个操作数的优点。

  • “1 = a ^ b ^ c...”表示真实操作数的数量是奇数。 这个运算符是“奇偶校验”。
  • “1 = a + b + c...”表示恰好有一个操作数为真。 这是“独占或”,意思是“一个排除其他”。
  • 蟒逻辑orA or B :返回A如果bool(A)True ,否则返回B
  • 蟒逻辑andA and B :返回A如果bool(A)False ,否则返回B

为了保持这种思维方式,我的逻辑异或定义是:

def logical_xor(a, b):
    if bool(a) == bool(b):
        return False
    else:
        return a or b

这样它就可以返回abFalse

>>> logical_xor('this', 'that')
False
>>> logical_xor('', '')
False
>>> logical_xor('this', '')
'this'
>>> logical_xor('', 'that')
'that'

我已经测试了几种方法,而not a != (not b)似乎是最快的。

这里有一些测试

%timeit not a != (not b)
10000000 loops, best of 3: 78.5 ns per loop

%timeit bool(a) != bool(b)
1000000 loops, best of 3: 343 ns per loop

%timeit not a ^ (not b)
10000000 loops, best of 3: 131 ns per loop

编辑:上面的示例 1 和 3 缺少括号,因此结果不正确。 ShadowRanger 建议的新结果 + truth()函数。

%timeit  (not a) ^  (not b)   # 47 ns
%timeit  (not a) != (not b)   # 44.7 ns
%timeit truth(a) != truth(b)  # 116 ns
%timeit  bool(a) != bool(b)   # 190 ns

Python 有一个按位异或运算符,它是^

>>> True ^ False
True
>>> True ^ True
False
>>> False ^ True
True
>>> False ^ False
False

您可以通过在应用 xor ( ^ ) 之前将输入转换为布尔值来使用它:

bool(a) ^ bool(b)

(已编辑 - 感谢 Arel)

简单易懂:

sum(bool(a), bool(b)) == 1

如果您追求的是排他选择,即从n选择1选项,则可以将其扩展为多个参数:

sum(bool(x) for x in y) == 1

由于我没有看到使用可变参数的 xor 的简单变体,并且只对真值 True 或 False 进行操作,因此我将把它放在这里供任何人使用。 正如其他人所指出的那样,非常(不是说非常)直截了当。

def xor(*vars):
    result = False
    for v in vars:
        result = result ^ bool(v)
    return result

用法也很简单:

if xor(False, False, True, False):
    print "Hello World!"

由于这是广义的 n-ary 逻辑 XOR,当 True 操作数的数量为奇数时,它的真值将为 True(不仅当恰好有一个为 True 时,这只是 n-ary XOR 为 True 的一种情况)。

因此,如果您正在寻找一个 n 元谓词,该谓词仅在其操作数中的一个为 True 时为真,您可能需要使用:

def isOne(*vars):
    result = False
    for v in vars:
        if result and v:
            return False
        else:
            result = result or v
    return result

奖励线程:

阳极的想法......只是你尝试(可能是)pythonic表达式“不是”以获得逻辑“xor”的行为

真值表将是:

>>> True is not True
False
>>> True is not False
True
>>> False is not True
True
>>> False is not False
False
>>>

对于您的示例字符串:

>>> "abc" is not  ""
True
>>> 'abc' is not 'abc' 
False
>>> 'abc' is not '' 
True
>>> '' is not 'abc' 
True
>>> '' is not '' 
False
>>> 

然而; 正如他们上面指出的,这取决于你想要提取的关于任何一对字符串的实际行为,因为字符串不是 boleans ......甚至更多:如果你 «Dive Into Python» 你会发现 «The Peculiar Nature of“和”和“或”» http://www.diveintopython.net/power_of_introspection/and_or.html

对不起,我写的英语不是我天生的语言。

问候。

我知道这已经晚了,但我有一个想法,它可能是值得的,只是为了文档。 也许这会奏效: np.abs(xy)这个想法是

  1. 如果 x=True=1 和 y=False=0 那么结果将是 |1-0|=1=True
  2. 如果 x=False=0 和 y=False=0 那么结果将是 |0-0|=0=False
  3. 如果 x=True=1 和 y=True=1 那么结果将是 |1-1|=0=False
  4. 如果 x=False=0 和 y=True=1 那么结果将是 |0-1|=1=True

要在 Python 中获取两个或多个变量的逻辑异或:

  1. 将输入转换为布尔值
  2. 使用按位异或operator.xor符( ^operator.xor

例如,

bool(a) ^ bool(b)

当您将输入转换为布尔值时,按位异或变为逻辑异或。

请注意,接受的答案是错误的: !=与 Python 中的 xor 不同,因为operator chaining的微妙之处。

例如,使用!=时,以下三个值的异或是错误的:

True ^  False ^  False  # True, as expected of XOR
True != False != False  # False! Equivalent to `(True != False) and (False != False)`

(PS 我尝试编辑接受的答案以包含此警告,但我的更改被拒绝。)

您使用与 C 中相同的 XOR 运算符,即^

我不知道为什么,但最受支持的解决方案建议bool(A) != bool(B) ,而我会说 - 根据 C 的^运算符,最明显的解决方案是:

bool(A) ^ bool(B)

对于来自C或任何C派生语言的任何人来说,它更具可读性和立即理解......

在打代码时,可能

not A ^ (not B)

将是赢家。 with not as converter for boolean (one letter less than bool() 。在某些情况下,对于第一个表达式,可以省略括号。好吧,这取决于,在必须做的情况下not(A) ^ (not(B))bool()需要相同数量的字母......

有时我发现自己使用 1 和 0 而不是布尔 True 和 False 值。 在这种情况下,xor 可以定义为

z = (x + y) % 2

它具有以下真值表:

     x
   |0|1|
  -+-+-+
  0|0|1|
y -+-+-+
  1|1|0|
  -+-+-+

异或定义如下

def xor( a, b ):
    return (a or b) and not (a and b)

这里建议的一些实现在某些情况下会导致对操作数的重复评估,这可能会导致意想不到的副作用,因此必须避免。

也就是说,返回TrueFalsexor实现相当简单; 如果可能,返回其中一个操作数的方法要复杂得多,因为对于应选择哪个操作数没有达成共识,尤其是在有两个以上的操作数时。 例如, xor(None, -1, [], True)返回None[]还是False 我敢打赌,每个答案在某些人看来都是最直观的。

对于 True- 或 False-结果,有多达五种可能的选择:返回第一个操作数(如果它与值中的最终结果匹配,否则为布尔值),返回第一个匹配项(如果至少存在一个,则为布尔值),返回最后一个操作数(if ... else ...),返回最后一个匹配(if ... else ...),或者总是返回布尔值。 总共有 5 ** 2 = 25 种xor

def xor(*operands, falsechoice = -2, truechoice = -2):
  """A single-evaluation, multi-operand, full-choice xor implementation
  falsechoice, truechoice: 0 = always bool, +/-1 = first/last operand, +/-2 = first/last match"""
  if not operands:
    raise TypeError('at least one operand expected')
  choices = [falsechoice, truechoice]
  matches = {}
  result = False
  first = True
  value = choice = None
  # avoid using index or slice since operands may be an infinite iterator
  for operand in operands:
    # evaluate each operand once only so as to avoid unintended side effects
    value = bool(operand)
    # the actual xor operation
    result ^= value
    # choice for the current operand, which may or may not match end result
    choice = choices[value]
    # if choice is last match;
    # or last operand and the current operand, in case it is last, matches result;
    # or first operand and the current operand is indeed first;
    # or first match and there hasn't been a match so far
    if choice < -1 or (choice == -1 and value == result) or (choice == 1 and first) or (choice > 1 and value not in matches):
      # store the current operand
      matches[value] = operand
    # next operand will no longer be first
    first = False
  # if choice for result is last operand, but they mismatch
  if (choices[result] == -1) and (result != value):
    return result
  else:
    # return the stored matching operand, if existing, else result as bool
    return matches.get(result, result)

testcases = [
  (-1, None, True, {None: None}, [], 'a'),
  (None, -1, {None: None}, 'a', []),
  (None, -1, True, {None: None}, 'a', []),
  (-1, None, {None: None}, [], 'a')]
choices = {-2: 'last match', -1: 'last operand', 0: 'always bool', 1: 'first operand', 2: 'first match'}
for c in testcases:
  print(c)
  for f in sorted(choices.keys()):
    for t in sorted(choices.keys()):
      x = xor(*c, falsechoice = f, truechoice = t)
      print('f: %d (%s)\tt: %d (%s)\tx: %s' % (f, choices[f], t, choices[t], x))
  print()

这个怎么样?

(not b and a) or (not a and b)

如果b为假,将给出a
如果a为假,将给出b
否则会给出False

或者使用 Python 2.5+ 三元表达式:

(False if a else b) if b else a

许多人,包括我自己,都需要一个xor函数,它的行为类似于 n 输入 xor 电路,其中 n 是可变的。 (参见https://en.wikipedia.org/wiki/XOR_gate )。 下面的简单函数实现了这一点。

def xor(*args):
   """
   This function accepts an arbitrary number of input arguments, returning True
   if and only if bool() evaluates to True for an odd number of the input arguments.
   """

   return bool(sum(map(bool,args)) % 2)

示例 I/O 如下:

In [1]: xor(False, True)
Out[1]: True

In [2]: xor(True, True)
Out[2]: False

In [3]: xor(True, True, True)
Out[3]: True

Xor 在 Python 中是^ 它返回:

  • 整数的按位异或
  • bool 的逻辑异或
  • 集合的唯一并集
  • 实现__xor__类的用户定义结果。
  • 未定义类型的 TypeError,例如字符串或字典。

如果您打算在字符串上使用它们,将它们转换为bool会使您的操作明确(您也可以指set(str1) ^ set(str2) )。

这将获得两个(或更多)变量的逻辑异或异或

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")

any([str1, str2]) and not all([str1, str2])

这种设置的第一个问题是它很可能会遍历整个列表两次,并且至少会检查至少一个元素两次。 因此,它可能会提高代码理解能力,但不会提高速度(根据您的用例,速度可能会略有不同)。

这种设置的第二个问题是它检查排他性而不管变量的数量。 这最初可能被视为一个特征,但随着变量数量的增加(如果它们曾经这样做的话),第一个问题变得更加重要。

当您知道 XOR 的作用时,这很容易:

def logical_xor(a, b):
    return (a and not b) or (not a and b)

test_data = [
  [False, False],
  [False, True],
  [True, False],
  [True, True],
]

for a, b in test_data:
    print '%r xor %s = %r' % (a, b, logical_xor(a, b))

这就是我编写任何真值表的方式。 特别是对于异或,我们有:

| a | b  | xor   |             |
|---|----|-------|-------------|
| T | T  | F     |             |
| T | F  | T     | a and not b |
| F | T  | T     | not a and b |
| F | F  | F     |             |

只需查看答案列中的 T 值,并用逻辑或将所有真实情况串在一起。 所以,这个真值表可能在情况 2 或 3 中产生。因此,

xor = lambda a, b: (a and not b) or (not a and b)

XOR 在operator.xor实现。

鉴于 A 和 B 是布尔值。

A is not B

只是因为我没有在其他地方看到它,所以这也可以解决问题:

def logical_xor(a, b):
    return not b if a else bool(b)

我不确定它是否比公认的解决方案 bool(a).= bool(b)“更好”/更具可读性/更 pythonic。

这是 map-reduce 泛化的实现。 请注意,这等效于functools.reduce(lambda x, y: x,= y, map(bool, orands))

def xor(*orands):
    return bool(sum(bool(x) for x in orands)) % 2

如果您正在寻找单热检测器,这是一个概括。 这种概括可能符合排他或的英语用法(例如“花一美元你可以买果汁或咖啡或茶”),但它不符合典型的操作顺序。 例如xor_1hot(1,1,1) == 0,= 1 == xor_1hot(xor_1hot(1,1),1)

def xor_1hot(*orands):
    return sum(bool(x) for x in orands) == 1

您可以使用

# test
from itertools import product
n = 3
total_true = 0
for inputs in product((False, True), repeat=n):
    y = xor(*inputs)
    total_true += int(y)
    print(f"{''.join(str(int(b)) for b in inputs)}|{y}")
print('Total True:', total_true)

单热检测器输出:

000|错误
001|真
010|真
011|错误
100|真
101|错误
110|错误
111|错误
真实总数:3

Map-Reduce 输出:

000|错误
001|真
010|真
011|错误
100|真
101|错误
110|错误
111|真
真实总数:4

Python 处理逻辑操作的方式可能令人困惑,因此我的实现为用户提供了一个简单的 True/False 答案选项(默认情况下)。 可以通过将可选的第三个 arg 设置为 None 来获得实际的 Python 结果。

def xor(a, b, true=True, false=False): # set true to None to get actual Python result
    ab1 = a and not b
    ab2 = not a and b
    if bool(ab1) != bool(ab2):
        return (ab1 or ab2) if true is None else true
    else:
        return false

如何在Python中获得两个变量的逻辑异或?

例如,我有两个期望是字符串的变量。 我想测试其中只有一个包含True值(不是None或空字符串):

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")
if logical_xor(str1, str2):
    print "ok"
else:
    print "bad"

^运算符似乎是按位的,并且未在所有对象上都定义:

>>> 1 ^ 1
0
>>> 2 ^ 1
3
>>> "abc" ^ ""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for ^: 'str' and 'str'

我们可以使用以下方法轻松找到两个变量的异或:

def xor(a,b):
    return a !=b

例子:

xor(True,False) >>> 真

暂无
暂无

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

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