3-SAT 솔버 Python
이 프롬프트를 기반으로 3-SAT 솔버를 작성했습니다 .
Alice는 최근에 하드웨어 설계 회사에서 일하기 시작했으며 작업의 일부로 조립 된 집적 회로의 결함을 식별해야합니다. 이러한 결함을 식별하기위한 접근 방식은 만족도 인스턴스를 해결하는 것으로 귀결됩니다. 그녀는이 작업을 수행하는 프로그램을 작성하는 데 귀하의 도움이 필요합니다.
입력 입력
의 첫 번째 줄에는 따라야 할 테스트 케이스의 수를 나타내는 5 개 이하의 단일 정수가 포함됩니다. 각 테스트 케이스의 첫 번째 행에는 두 개의 정수 n과 m이 포함됩니다. 여기서 1 ≤ n ≤ 20은 변수 수를 나타내고 1 ≤ m ≤ 100은 절의 수를 나타냅니다. 그런 다음 m 줄이 각 절에 해당합니다. 각 절은 1 ≤ i ≤ n에 대한 Xi 또는 ~ Xi 형식의 리터럴 분리입니다. 여기서 ~ Xi는 리터럴 Xi의 부정을 나타냅니다. "or"연산자는 'v'문자로 표시되며 단일 공백이있는 리터럴과 구분됩니다.출력
각 테스트 케이스에 대해 만족할만한 할당이 있으면 만족할 수 있음을 한 줄에 표시합니다. 그렇지 않으면 만족스럽지 않습니다.샘플 입력
2 3 3 X1 v X2 ~X1 ~X2 v X3 3 5 X1 v X2 v X3 X1 v ~X2 X2 v ~X3 X3 v ~X1 ~X1 v ~X2 v ~X3샘플 출력
satisfiable unsatisfiable
이 코드는 기본적으로 mySets전체 문을 참으로 만들 수있는 리터럴의 가능한 조합을 나타내는 집합 목록을 유지 합니다. 새 절을 구문 분석 할 때마다 해당 세트에 이미 부정이 존재하는지 확인합니다. 존재하는 경우 세트가 포함되지 않습니다.
이것은 작동하지만 약간 느리게 실행됩니다.
import sys
cases = int(sys.stdin.readline())
def GetReverse(literal):
if literal[0] == '~':
return literal[1:]
else:
return '~' + literal
for i in range(cases):
vars, clauses = map(int, sys.stdin.readline().split())
mySets = []
firstClause = sys.stdin.readline().strip().split(" v ")
for c in firstClause:
this = set()
this.add(c)
mySets.append(this)
for i in range(clauses-1):
tempSets = []
currentClause = sys.stdin.readline().strip().split(" v ")
for s in mySets:
for literal in currentClause:
if not s.__contains__(GetReverse(literal)):
newset = s.copy()
newset.add(literal)
tempSets.append(newset)
mySets = tempSets
if mySets:
print("satisfiable")
else:
print("unsatisfiable")
나는 들여 쓰기 된 for-loops 때문에 문제가 여기에 있다고 생각합니다. 3-SAT는 기하 급수적이어야하지만 약간의 속도를 높이고 싶습니다 (아마도 루프를 제거하여?)
for i in range(clauses-1):
tempSets = []
currentClause = sys.stdin.readline().strip().split(" v ")
for s in mySets:
for literal in currentClause:
if not s.__contains__(GetReverse(literal)):
newset = s.copy()
newset.add(literal)
tempSets.append(newset)
mySets = tempSets
답변
전략적으로 배치하는 print 문을 사용하여 코드를 계측하면 반복되는 계산이 진행되고 있음을 알 수 있습니다. 두 번째 테스트 케이스에서 절을 처리 할 때 X2 v ~X3세트 {'X1', 'X2'}가 mySets두 번 추가됩니다 . 절을 처리 할 때 X3 v ~X1집합 {'X3', 'X1', 'X2'}이 mySets세 번 추가됩니다 .
큰 경우 에는 중복을 제거하기 위해 목록 대신 mySetsa 로 변경 하는 속도를 높일 수 있습니다 set(). 그런 다음 내부 세트는 frozensets.
mySets모든 절을 충족하는 가능한 솔루션 집합이므로 이름을 candidates.
candidates하나의 빈 집합을 포함하도록 초기화 하면 첫 번째 절을 별도로 처리 할 필요가 없습니다.
candidates비어 있으면 언제든지 멈출 수 있다고 생각합니다 .
또한 코드를 함수로 분할하십시오.
def is_satisfiable(n_vars, clauses):
candidates = {frozenset()}
for clause in clauses:
temp = set()
for s in candidates:
for literal in clause:
if GetReverse(literal) not in s:
temp.add(s | {literal})
candidates = temp
if len(candidates) == 0:
return False
return True
def load_case(f):
n_vars, n_clauses = f.readline().split()
clauses = [f.readline().strip().split(' v ') for _ in range(int(n_clauses))]
return int(n_vars), clauses
def main(f=sys.stdin):
num_cases = int(f.readline())
for i in range(num_cases):
n_vars, clauses = load_case(f)
result = is_satisfiable(n_vars, clauses)
print(f"{'satisfiable' if result else 'unsatisfiable'}")
다음과 같이 부름 :
import io
data = """
2
3 3
X1 v X2
~X1
~X2 v X3
3 5
X1 v X2 v X3
X1 v ~X2
X2 v ~X3
X3 v ~X1
~X1 v ~X2 v ~X3
""".strip()
main(io.StringIO(data))
또는
import sys
main(sys.stdin)
기본적으로 알고리즘에 대해 아무것도 변경하지 않는 제안 된 구현이 있습니다.
- 적절한 들여 쓰기가 있음
- 약간의 유형 암시를 사용합니다.
- 세트 리터럴 및 생성기를 사용합니다.
- 사용
_"사용하지 않는"변수 parse_clause()절 코드가 반복되기 때문에 추가- 사용
StringIO, 이러한 목적을 위해, 효과적으로 멀리 조롱하는stdin당신이 보여준 예 입력을 사용 - PEP8 호환 이름 사용 (밑줄 포함)
from io import StringIO
from typing import List
stdin = StringIO('''2
3 3
X1 v X2
~X1
~X2 v X3
3 5
X1 v X2 v X3
X1 v ~X2
X2 v ~X3
X3 v ~X1
~X1 v ~X2 v ~X3
'''
)
def get_reverse(literal: str) -> str:
if literal[0] == '~':
return literal[1:]
return '~' + literal
def parse_clause() -> List[str]:
return stdin.readline().strip().split(' v ')
n_cases = int(stdin.readline())
for _ in range(n_cases):
n_vars, n_clauses = (int(s) for s in stdin.readline().split())
my_sets = [{c} for c in parse_clause()]
for _ in range(n_clauses - 1):
temp_sets = []
current_clause = parse_clause()
for s in my_sets:
for literal in current_clause:
if get_reverse(literal) not in s:
new_set = s.copy()
new_set.add(literal)
temp_sets.append(new_set)
my_sets = temp_sets
if my_sets:
print('satisfiable')
else:
print('unsatisfiable')