Python构建动态增长的真值表

23

我的问题很简单:如何用优雅的方式在Python中构建一个动态生长的真值表?

n=3时:

for p in False, True:
    for q in False, True:
        for r in False, True:
            print '|{0} | {1} | {2} |'.format(int(p),int(q), int(r))

当n=4时

for p in False, True:
    for q in False, True:
        for r in False, True:
            for s in False, True:
                print '|{0} | {1} | {2} | {3}'.format(int(p),int(q), int(r), int(s))
我希望有一个函数,它接受一个参数n并构建表格,不需要打印表格,只返回代表表格的数据结构即可。
8个回答

53
使用 itertools.product() 函数:
table = list(itertools.product([False, True], repeat=n))

n=3 的结果:

[(False, False, False),
 (False, False, True),
 (False, True, False),
 (False, True, True),
 (True, False, False),
 (True, False, True),
 (True, True, False),
 (True, True, True)]

1
这确实是正确的方法。感谢大家的回答。我会按照这里描述的方式去做,但下面的实现值得一看并投票支持! - evildead

6

当然,列表推导式更符合Python的风格。

def truthtable (n):
  if n < 1:
    return [[]]
  subtable = truthtable(n-1)
  return [ row + [v] for row in subtable for v in [0,1] ]

结果,为了清晰起见缩进:

truthtable(1)
[ [0],
  [1] ]

truthtable(3)
[ [0, 0, 0],
  [0, 0, 1],
  [0, 1, 0],
  [0, 1, 1],
  [1, 0, 0],
  [1, 0, 1],
  [1, 1, 0],
  [1, 1, 1] ]

作为一个带有 yield 的生成器函数:
def truthtable (n): 
  if n < 1:
    yield []
    return
  subtable = truthtable(n-1)
  for row in subtable:
    for v in [0,1]:
      yield row + [v]

只需将返回值从数组理解式更改为生成器表达式,即可使返回类型等效于使用 yield 版本的生成器函数:

def truthtable (n):
  if n < 1:
    return [[]]
  subtable = truthtable(n-1)
  return ( row + [v] for row in subtable for v in [0,1] )

使用yield会是什么样子?我在Python方面还比较新。 - evildead

5

itertools确实是大家都指出的最佳选择。但如果你真的想了解这个所需算法的细节,你应该查阅递归下降分析器。以下是它在您的情况下的工作方式:

def tablize(n, truths=[]):
    if not n:
        print truths
    else:
        for i in [True, False]:
            tablize(n-1, truths+[i])

测试通过,工作正常

希望这有所帮助


你能给我一个使用yield的版本吗?这对于解决其他问题非常有帮助 :) 我非常喜欢在Scala中使用yield ;) - evildead

2
返回一个代表表格的数据结构是可以的。在这种情况下,只需要使用range(2 ** n)。范围内的每个数字表示真值表中的一行。数字k的二进制表示中的第i位仅在第k行中的第i个变量为真时为1。如果您想要一个实际的表格,可以使用:
[ [ ((row >> bit_index) & 1) == 1 for bit_index in range(n)] 
  for bit_index in range(2 ** n) ]

2
请查看itertools模块。
In [7]: [i for i in itertools.product([0,1], repeat=3)]
Out[7]: 
[(0, 0, 0),
 (0, 0, 1),
 (0, 1, 0),
 (0, 1, 1),
 (1, 0, 0),
 (1, 0, 1),
 (1, 1, 0),
 (1, 1, 1)]

2

简单的数学方法:

a = lambda x: [x//4%2,x//2%2,x%2]

for i in range(8):
    print(a(i))

[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[1, 0, 0]
[1, 0, 1]
[1, 1, 0]
[1, 1, 1]

编辑:

一个更通用的格式是:

def truth_table(n):
    for i in range(2**n):
        line = [i//2**j%2 for j in reversed(range(n))]
        print(line)

这将仅以以下方式打印结果:
>>> truth_table(3)
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[1, 0, 0]
[1, 0, 1]
[1, 1, 0]
[1, 1, 1]

这不是一般化的,它只适用于 n = 3。 - Hans
1
请试一下,这是您需要的。 - Maviles

2

在场的各位,谁喜欢原始的一行代码呢?

>>> truthtable = lambda n: [[(v>>i)&1 for i in range(n-1,-1,-1)] for v in range(1<<n)] if n>0 else [[]]

经过100%测试并且工作正常。
(因为我在手机上使用互联网,无法复制/粘贴结果或以上代码)


1
提示:如果您想要类似于yield的功能,只需将所有括号(除了else后面的括号)替换为圆括号,lambda函数将返回一个双重生成器对象。 - Tcll

0
这是一个使用函数式编程实现的例子,不使用循环,并且不导入任何外部库:
get_bits = lambda n: [*map(lambda x:[*map(int,bin(x)[2:].zfill(n))],range(2**n))]

然后使用所需参数调用get_bits

>>> get_bits(3)
[[0, 0, 0],
 [0, 0, 1],
 [0, 1, 0],
 [0, 1, 1],
 [1, 0, 0],
 [1, 0, 1],
 [1, 1, 0],
 [1, 1, 1]]

请查看我的 GitHub 存储库,以查看我从布尔表达式字符串生成真值表的完整项目。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接