AlgoVerse

درخت عبارت و مسئله‌ی صدق‌پذیری

یک فرمول گزاره‌ای را به درخت عبارت تبدیل می‌کند و آن را برای هر مقداردهی متغیرها با پیمایش پس‌ترتیب ارزیابی می‌کند؛ فرمول صدق‌پذیر است اگر سطری درست شود. 2^n مقداردهی.

هر فرمول گزاره‌ای یک درخت عبارت دارد: برگ‌ها متغیرها هستند و گره‌های داخلی عملگرها (¬ یک فرزند، ∧ و ∨ دو فرزند). مقدار فرمول با پیمایش پس‌ترتیب به دست می‌آید: اول فرزندان، بعد عملگر.

ادامه‌ی توضیح ↓
ورودی:
formula="(a&~b)|(~a&c…"خروجی PDF

(a ∧ ¬b) ∨ (¬a ∧ c) ∨ ¬c؛ سه متغیر و ۸ سطر، ۷ سطر درست است (فقط وقتی هر سه درست‌اند نادرست می‌شود).

  1. class Node:
  2. def __init__(self, key):
  3. self.key = key
  4. self.left = None
  5. self.right = None
  6. def satisfiable(formula):
  7. root = parse(formula)
  8. if root is None:
  9. return None
  10. names = []
  11. collect(root, names)
  12. names.sort()
  13. n = len(names)
  14. rows = [names + ["value"]]
  15. models = 0
  16. for m in range(2 ** n):
  17. env = {}
  18. for i in range(n):
  19. env[names[i]] = (m >> (n - 1 - i)) % 2 == 1
  20. value = evaluate(root, env)
  21. row = []
  22. for name in names:
  23. row.append("T" if env[name] else "F")
  24. row.append("T" if value else "F")
  25. rows.append(row)
  26. if value:
  27. models = models + 1
  28. return models
  29. def evaluate(node, env):
  30. # Postorder: both children first, then the operator at this node.
  31. if node.key in env:
  32. v = env[node.key]
  33. return v
  34. right = evaluate(node.right, env)
  35. if node.key == "¬":
  36. v = not right
  37. return v
  38. left = evaluate(node.left, env)
  39. if node.key == "∧":
  40. v = left & right
  41. else:
  42. v = left | right
  43. return v
  44. def collect(node, names):
  45. if node is None:
  46. return
  47. if node.key.isalpha() and node.key not in names:
  48. names.append(node.key)
  49. collect(node.left, names)
  50. collect(node.right, names)
  51. def parse(text):
  52. # | is or, & is and, ~ is not; ~ binds tightest and | loosest.
  53. toks = []
  54. for c in text:
  55. if c != " ":
  56. toks.append(c)
  57. pos = [0]
  58. root = parse_or(toks, pos)
  59. if pos[0] != len(toks):
  60. return None
  61. return root
  62. def parse_or(toks, pos):
  63. left = parse_and(toks, pos)
  64. while left is not None and pos[0] < len(toks) and toks[pos[0]] == "|":
  65. pos[0] += 1
  66. left = join("∨", left, parse_and(toks, pos))
  67. return left
  68. def parse_and(toks, pos):
  69. left = parse_not(toks, pos)
  70. while left is not None and pos[0] < len(toks) and toks[pos[0]] == "&":
  71. pos[0] += 1
  72. left = join("∧", left, parse_not(toks, pos))
  73. return left
  74. def parse_not(toks, pos):
  75. if pos[0] >= len(toks):
  76. return None
  77. c = toks[pos[0]]
  78. pos[0] += 1
  79. if c == "~":
  80. return join("¬", None, parse_not(toks, pos))
  81. if c == "(":
  82. inner = parse_or(toks, pos)
  83. if inner is None or pos[0] >= len(toks) or toks[pos[0]] != ")":
  84. return None
  85. pos[0] += 1
  86. return inner
  87. if c.isalpha():
  88. return Node(c)
  89. return None
  90. def join(op, left, right):
  91. if right is None:
  92. return None
  93. node = Node(op)
  94. node.left = left
  95. node.right = right
  96. return node

ناوردا: مقدار هر گره فقط پس از مقدار فرزندانش حساب می‌شود (پس‌ترتیب).

شروع اجرا

∅

models—

جدول هنوز خالی است.

درخت فراخوانی‌های بازگشتی

نارنجی پر = فراخوانی در حال اجرا · حاشیه‌ی نارنجی = هنوز روی پشته · خاکستری = تمام شده

متغیرهای محلی

formula'(a&~b)|(~a&c)|~c'

نارنجی = در همین گام عوض شد

Call stack

  1. satisfiable()خط 8
گام ۱ از ۱٬۱۱۵
خط سیاه = row · خط نارنجی = گام کلیدیparsed: 0/1 · variable: 0/40 · negate: 0/24 · conj: 0/16 · disj: 0/16 · row: 0/8
    توضیح

    درخت عبارت

    هر فرمول گزاره‌ای یک درخت عبارت دارد: برگ‌ها متغیرها هستند و گره‌های داخلی عملگرها (¬ یک فرزند، ∧ و ∨ دو فرزند). مقدار فرمول با پیمایش پس‌ترتیب به دست می‌آید: اول فرزندان، بعد عملگر.

    صدق‌پذیری

    فرمول صدق‌پذیر است اگر مقداردهی‌ای از متغیرها آن را درست کند. روش مستقیم (کتاب هوروویتز) همه‌ی 2^n مقداردهی را امتحان می‌کند و هر بار درخت را ارزیابی می‌کند: همان جدول درستی.

    این مسئله (SAT) نخستین مسئله‌ای است که NP-کامل بودنش ثابت شد (Cook–Levin)؛ هیچ الگوریتم چندجمله‌ای برایش شناخته نشده است.

    ورودی

    حروف a تا d متغیرند؛ ~ نقیض، & «و»، | «یا» و پرانتز. ~ از همه قوی‌تر و | از همه ضعیف‌تر است.