درخت عبارت و مسئلهی صدقپذیری
یک فرمول گزارهای را به درخت عبارت تبدیل میکند و آن را برای هر مقداردهی متغیرها با پیمایش پسترتیب ارزیابی میکند؛ فرمول صدقپذیر است اگر سطری درست شود. 2^n مقداردهی.
هر فرمول گزارهای یک درخت عبارت دارد: برگها متغیرها هستند و گرههای داخلی عملگرها (¬ یک فرزند، ∧ و ∨ دو فرزند). مقدار فرمول با پیمایش پسترتیب به دست میآید: اول فرزندان، بعد عملگر.
- زمان:
O(2^n · size) - حافظه:
O(size) - هوروویتز–ساهنی، فصل ۵
(a ∧ ¬b) ∨ (¬a ∧ c) ∨ ¬c؛ سه متغیر و ۸ سطر، ۷ سطر درست است (فقط وقتی هر سه درستاند نادرست میشود).
class Node:def __init__(self, key):self.key = keyself.left = Noneself.right = Nonedef satisfiable(formula):root = parse(formula)if root is None:return Nonenames = []collect(root, names)names.sort()n = len(names)rows = [names + ["value"]]models = 0for m in range(2 ** n):env = {}for i in range(n):env[names[i]] = (m >> (n - 1 - i)) % 2 == 1value = evaluate(root, env)row = []for name in names:row.append("T" if env[name] else "F")row.append("T" if value else "F")rows.append(row)if value:models = models + 1return modelsdef evaluate(node, env):# Postorder: both children first, then the operator at this node.if node.key in env:v = env[node.key]return vright = evaluate(node.right, env)if node.key == "¬":v = not rightreturn vleft = evaluate(node.left, env)if node.key == "∧":v = left & rightelse:v = left | rightreturn vdef collect(node, names):if node is None:returnif node.key.isalpha() and node.key not in names:names.append(node.key)collect(node.left, names)collect(node.right, names)def parse(text):# | is or, & is and, ~ is not; ~ binds tightest and | loosest.toks = []for c in text:if c != " ":toks.append(c)pos = [0]root = parse_or(toks, pos)if pos[0] != len(toks):return Nonereturn rootdef parse_or(toks, pos):left = parse_and(toks, pos)while left is not None and pos[0] < len(toks) and toks[pos[0]] == "|":pos[0] += 1left = join("∨", left, parse_and(toks, pos))return leftdef parse_and(toks, pos):left = parse_not(toks, pos)while left is not None and pos[0] < len(toks) and toks[pos[0]] == "&":pos[0] += 1left = join("∧", left, parse_not(toks, pos))return leftdef parse_not(toks, pos):if pos[0] >= len(toks):return Nonec = toks[pos[0]]pos[0] += 1if c == "~":return join("¬", None, parse_not(toks, pos))if c == "(":inner = parse_or(toks, pos)if inner is None or pos[0] >= len(toks) or toks[pos[0]] != ")":return Nonepos[0] += 1return innerif c.isalpha():return Node(c)return Nonedef join(op, left, right):if right is None:return Nonenode = Node(op)node.left = leftnode.right = rightreturn node
شروع اجرا
∅
جدول هنوز خالی است.
درخت فراخوانیهای بازگشتی
نارنجی پر = فراخوانی در حال اجرا · حاشیهی نارنجی = هنوز روی پشته · خاکستری = تمام شده
متغیرهای محلی
نارنجی = در همین گام عوض شد
Call stack
- satisfiable()خط 8
توضیح
درخت عبارت
هر فرمول گزارهای یک درخت عبارت دارد: برگها متغیرها هستند و گرههای داخلی عملگرها (¬ یک فرزند، ∧ و ∨ دو فرزند). مقدار فرمول با پیمایش پسترتیب به دست میآید: اول فرزندان، بعد عملگر.
صدقپذیری
فرمول صدقپذیر است اگر مقداردهیای از متغیرها آن را درست کند. روش مستقیم (کتاب هوروویتز) همهی 2^n مقداردهی را امتحان میکند و هر بار درخت را ارزیابی میکند: همان جدول درستی.
این مسئله (SAT) نخستین مسئلهای است که NP-کامل بودنش ثابت شد (Cook–Levin)؛ هیچ الگوریتم چندجملهای برایش شناخته نشده است.
ورودی
حروف a تا d متغیرند؛ ~ نقیض، & «و»، | «یا» و پرانتز. ~ از همه قویتر و | از همه ضعیفتر است.