2-SAT'ı (boolean memnuniyeti) çözün


16

Genel SAT (boolean satisfiability) problemi NP-tamamlandı. Ancak her bir yan tümcenin yalnızca 2 değişkeni olduğu 2-SAT , P'dir . 2-SAT için bir çözücü yazın.

Giriş:

CNF'de aşağıdaki gibi kodlanmış 2-SAT örneği . İlk satır V, boole değişkenlerinin sayısı ve N, yan tümce sayısını içerir. Daha sonra, her biri bir cümlenin değişmezlerini temsil eden 2 sıfır olmayan tamsayıya sahip N çizgileri gelir. Pozitif tamsayılar verilen boole değişkenini ve negatif tamsayılar değişkenin negatifliğini temsil eder.

örnek 1

giriş

4 5
1 2
2 3
3 4
-1 -3
-2 -4

burada aşağıdaki formüle kodlayan (x 1 veya x 2 ) ve (x 2 veya x 3 ) ve (x 3 veya x 4 ) ve (değil x 1 ya da x 3 ) ve (değil x 2 ya da x 4 ) .

Tüm formülü doğru yapan 4 değişkenin tek ayarı x 1 = yanlış, x 2 = doğru, x 3 = doğru, x 4 = yanlış , bu nedenle programınız tek satır çıkmalıdır

çıktı

0 1 1 0

V değişkenlerinin gerçek değerlerini temsil eder ( x 1 ila x V sırasıyla ). Birden fazla çözüm varsa, her satırda bir tane olacak şekilde bunların herhangi bir boş olmayan alt kümesini çıktılayabilirsiniz. Çözüm yoksa çıktı almalısınız UNSOLVABLE.

ÖRNEK 2

giriş

2 4
1 2
-1 2
-2 1
-1 -2

çıktı

UNSOLVABLE

ÖRNEK 3

giriş

2 4
1 2
-1 2
2 -1
-1 -2

çıktı

0 1

Örnek 4

giriş

8 12
1 4
-2 5
3 7
2 -5
-8 -2
3 -1
4 -3
5 -4
-3 -7
6 7
1 7
-7 -1

çıktı

1 1 1 1 1 1 0 0
0 1 0 1 1 0 1 0
0 1 0 1 1 1 1 0

(veya bu 3 satırın herhangi bir boş olmayan alt kümesi)

Programınız makul bir süre içinde tüm N, V ​​<100 değerlerini işlemelidir. Programınızın büyük bir örneği işleyebildiğinden emin olmak için bu örneği deneyin . En küçük program kazanır.


2-SAT'ın P'de olduğunu belirtiyorsunuz, ancak çözümün polinom zamanında çalışması gerektiğinden değil ;-)
Timwi

@Timwi: Hayır, ama makul bir sürede V = 99 ile başa çıkmak zorunda ...
Keith Randall

Yanıtlar:


4

Haskell, 278 karakter

(∈)=elem
r v[][]=[(>>=(++" ").show.fromEnum.(∈v))]
r v[]c@(a:b:_)=r(a:v)c[]++r(-a:v)c[]++[const"UNSOLVABLE"]
r v(a:b:c)d|a∈v||b∈v=r v c d|(-a)∈v=i b|(-b)∈v=i a|1<3=r v c(a:b:d)where i w|(-w)∈v=[]|1<3=r(w:v)(c++d)[]
t(n:_:c)=(r[][]c!!0)[1..n]++"\n"
main=interact$t.map read.words

Kaba kuvvet değil. Polinom zamanında çalışır. Zor problemi (60 değişken, 99 cümle) hızlı bir şekilde çözer:

> time (runhaskell 1933-2Sat.hs < 1933-hard2sat.txt)
1 1 1 0 0 0 0 0 0 1 1 0 0 1 0 1 1 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 1 0 1 1 1 1 0 

real 0m0.593s
user 0m0.502s
sys  0m0.074s

Ve aslında, çoğu zaman kodu derlemek için harcanır!

Test durumlarda ve hızlı check Tam kaynak dosyası, test kullanılabilir .

Ungolf'd:

-- | A variable or its negation
-- Note that applying unary negation (-) to a term inverts it.
type Term = Int

-- | A set of terms taken to be true.
-- Should only contain  a variable or its negation, never both.
type TruthAssignment = [Term]

-- | Special value indicating that no consistent truth assignment is possible.
unsolvable :: TruthAssignment
unsolvable = [0]

-- | Clauses are a list of terms, taken in pairs.
-- Each pair is a disjunction (or), the list as a whole the conjuction (and)
-- of the pairs.
type Clauses = [Term]

-- | Test to see if a term is in an assignment
(∈) :: Term -> TruthAssignment -> Bool
a∈v = a `elem` v;

-- | Satisfy a set of clauses, from a starting assignment.
-- Returns a non-exhaustive list of possible assignments, followed by
-- unsolvable. If unsolvable is first, there is no possible assignment.
satisfy :: TruthAssignment -> Clauses -> [TruthAssignment]
satisfy v c@(a:b:_) = reduce (a:v) c ++ reduce (-a:v) c ++ [unsolvable]
  -- pick a term from the first clause, either it or its negation must be true;
  -- if neither produces a viable result, then the clauses are unsolvable
satisfy v [] = [v]
  -- if there are no clauses, then the starting assignment is a solution!

-- | Reduce a set of clauses, given a starting assignment, then solve that
reduce :: TruthAssignment -> Clauses -> [TruthAssignment]
reduce v c = reduce' v c []
  where
    reduce' v (a:b:c) d
        | a∈v || b∈v = reduce' v c d
            -- if the clause is already satisfied, then just drop it
        | (-a)∈v = imply b
        | (-b)∈v = imply a
            -- if either term is not true, the other term must be true
        | otherwise = reduce' v c (a:b:d)
            -- this clause is still undetermined, save it for later
        where 
          imply w
            | (-w)∈v = []  -- if w is also false, there is no possible solution
            | otherwise = reduce (w:v) (c++d)
                -- otherwise, set w true, and reduce again
    reduce' v [] d = satisfy v d
        -- once all caluses have been reduced, satisfy the remaining

-- | Format a solution. Terms not assigned are choosen to be false
format :: Int -> TruthAssignment -> String
format n v
    | v == unsolvable = "UNSOLVABLE"
    | otherwise = unwords . map (bit.(∈v)) $ [1..n]
  where
    bit False = "0"
    bit True = "1"

main = interact $ run . map read . words 
  where
    run (n:_:c) = (format n $ head $ satisfy [] c) ++ "\n"
        -- first number of input is number of variables
        -- second number of input is number of claues, ignored
        -- remaining numbers are the clauses, taken two at a time

Golf'd sürümünde satisfyve formatiçine alınmış reduceolsa da, geçmekten kaçınmak için n, reducebir değişken ( [1..n]) listesinden dize sonucuna bir işlev döndürür .


  • Düzenleme: (330 -> 323) sbir operatör yaptı, yeni satırın daha iyi işlenmesi
  • Düzenleme: (323 -> 313) tembel bir sonuç listesindeki ilk eleman, özel bir kısa devre operatörüne göre daha küçüktür; ana çözücü işlevi olarak yeniden adlandırıldı çünkü operatör olarak kullanmayı seviyorum !
  • Düzenleme: (313 -> 296) cümleleri liste listesi olarak değil, tek bir liste olarak tutar; bir seferde iki öğe işleyin
  • Düzenleme: (296 -> 291) iki karşılıklı özyinelemeli işlevi birleştirdi; satır içi daha ucuzdu, bu yüzden test şimdi yeniden adlandırıldı
  • Düzenleme: (291 -> 278) sonuç oluşturma içine satır içi çıktı biçimlendirme

4

J, 119 103

echo'UNSOLVABLE'"_`(#&c)@.(*@+/)(3 :'*./+./"1(*>:*}.i)=y{~"1 0<:|}.i')"1 c=:#:i.2^{.,i=:0&".;._2(1!:1)3
  • Tüm test senaryolarını geçer. Göze çarpan çalışma zamanı yok.
  • Kaba kuvvet. Aşağıdaki test senaryolarını geçer, oh, N = 20 veya 30. Emin değilim.
  • Tamamen beyin ölü test senaryosu ile test edilmiştir (Görsel kontrol ile)

Düzenleme: Elendi (n#2)ve böylece n=:yanı sıra bazı rütbe parens (teşekkürler, isawdrones) ortadan kaldırılması. Tacit-> açık ve ikili-> monadik, her biri birkaç karakter daha ortadan kaldırır. }.}.için }.,.

Düzenleme: Hata! Bu sadece büyük N için bir çözüm değil, aynı i. 2^99xzamanda aptallığa hakaret eklemek için -> "etki alanı hatası" dır.

İşte ungolfed orijinal sürümü ve kısa açıklama.

input=:0&".;._2(1!:1)3
n =:{.{.input
clauses=:}.input
cases=:(n#2)#:i.2^n
results =: clauses ([:*./[:+./"1*@>:@*@[=<:@|@[{"(0,1)])"(_,1) cases
echo ('UNSOLVABLE'"_)`(#&cases) @.(*@+/) results
  • input=:0&".;._2(1!:1)3 yeni satırlardaki girdiyi keser ve her satırdaki sayıları ayrıştırır (sonuçları girdi olarak toplar).
  • n atandı n, cümle matrisine atandı clauses(cümle sayımına gerek yok)
  • cases0,2 n -1, ikili basamağa dönüştürülür (tüm test senaryoları )
  • (Long tacit function)"(_,1)casestüm vakalara tümü ile uygulanır clauses.
  • <:@|@[{"(0,1)] cümlelerin işlenenlerinin bir matrisini alır (abs (op numarası) - 1 alarak ve bir dizi olan davadan kayıt silme)
  • *@>:@*@[ signum kötüye kullanımı ile "değil" bitleri (değil 0) bit yan tümcesi şeklinde dizisi alır.
  • = değil bitlerini işlenenlere uygular.
  • [:*./[:+./"1geçerlidir +.Elde edilen matris, ve sıraları boyunca (ve) *.bu sonucu boyunca (ya da).
  • Tüm bu sonuçlar, her vaka için ikili bir 'cevaplar' dizisi olarak sonuçlanır.
  • *@+/ sonuçlara uygulandığında sonuç varsa 0, yoksa 1 verir.
  • ('UNSOLVABLE'"_) `(#&cases) @.(*@+/) results 0 ise 'UNSOLVABLE' veren sabit işlev ve 1 ise vakaların her 'çözüm' öğesinin bir kopyasını çalıştırır.
  • echo sonucu sihirli yazdırır.

Sıralama argümanlarının etrafındaki ortakları kaldırabilirsiniz. "(_,1)için "_ 1. #:sol argüman olmadan çalışırdı.
isawdrones

@isawdrones: Bence geleneksel tepki yarı uzun bir cevap üreterek ruhumu ezmek olacak. "Çığlık at ve sıçra", Kzin'in dediği gibi. Yine de teşekkürler, bu 10 tuhaf karakterleri ortadan kaldırır ... Geri döndüğümde 100'ün altına düşebilirim.
Jesse Millikan

Güzel ve ayrıntılı açıklama için +1, çok büyüleyici bir okuma!
Timwi

Muhtemelen N = V = 99'u makul bir süre içinde işlemeyecektir. Yeni eklediğim büyük örneği deneyin.
Keith Randall

3

K - 89

J çözeltisi ile aynı yöntem.

n:**c:.:'0:`;`0::[#b:t@&&/+|/''(0<'c)=/:(t:+2_vs!_2^n)@\:-1+_abs c:1_ c;5:b;"UNSOLVABLE"]

Güzel, ücretsiz bir K uygulaması olduğunu bilmiyordum.
Jesse Millikan

Muhtemelen N = V = 99'u makul bir süre içinde işlemeyecektir. Yeni eklediğim büyük örneği deneyin.
Keith Randall

2

Yakut, 253

n,v=gets.split;d=[];v.to_i.times{d<<(gets.split.map &:to_i)};n=n.to_i;r=[1,!1]*n;r.permutation(n){|x|y=x[0,n];x=[0]+y;puts y.map{|z|z||0}.join ' 'or exit if d.inject(1){|t,w|t and(w[0]<0?!x[-w[0]]:x[w[0]])||(w[1]<0?!x[-w[1]]:x[w[1]])}};puts 'UNSOLVABLE'

Ama yavaş :(

Genişletildiğinde oldukça okunabilir:

n,v=gets.split
d=[]
v.to_i.times{d<<(gets.split.map &:to_i)} # read data
n=n.to_i
r=[1,!1]*n # create an array of n trues and n falses
r.permutation(n){|x| # for each permutation of length n
    y=x[0,n]
    x=[0]+y
    puts y.map{|z| z||0}.join ' ' or exit if d.inject(1){|t,w| # evaluate the data (magic!)
        t and (w[0]<0 ? !x[-w[0]] : x[w[0]]) || (w[1]<0 ? !x[-w[1]] : x[w[1]])
    }
}
puts 'UNSOLVABLE'

Muhtemelen N = V = 99'u makul bir süre içinde işlemeyecektir. Yeni eklediğim büyük örneği deneyin.
Keith Randall

1

OCaml + Piller, 438 436 karakter

Bir OCaml Pil gerektirir Üst düzey dahil:

module L=List
let(%)=L.mem
let rec r v d c n=match d,c with[],[]->[String.join" "[?L:if x%v
then"1"else"0"|x<-1--n?]]|[],(x,_)::_->r(x::v)c[]n@r(-x::v)c[]n@["UNSOLVABLE"]|(x,y)::c,d->let(!)w=if-w%v
then[]else r(w::v)(c@d)[]n in if x%v||y%v then r v c d n else if-x%v then!y else if-y%v then!x else r v c((x,y)::d)n
let(v,_)::l=L.of_enum(IO.lines_of stdin|>map(fun s->Scanf.sscanf s"%d %d"(fun x y->x,y)))in print_endline(L.hd(r[][]l v))

İtiraf etmeliyim ki, bu Haskell çözümünün doğrudan bir çevirisi. Benim savunmamda, bu algoritmanın doğrudan kodlanmasıdır Burada sunulan karşılıklı ile, [PDF] satisfy- eliminateTek işlevi içine yuvarlandı özyineleme. Pillerin kullanımı dışında, kodun belirsiz bir sürümü:

let rec satisfy v c d = match c, d with
| (x, y) :: c, d ->
    let imply w = if List.mem (-w) v then raise Exit else satisfy (w :: v) (c @ d) [] in
    if List.mem x v || List.mem y v then satisfy v c d else
    if List.mem (-x) v then imply y else
    if List.mem (-y) v then imply x else
    satisfy v c ((x, y) :: d)
| [], [] -> v
| [], (x, _) :: _ -> try satisfy (x :: v) d [] with Exit -> satisfy (-x :: v) d []

let rec iota i =
    if i = 0 then [] else
    iota (i - 1) @ [i]

let () = Scanf.scanf "%d %d\n" (fun k n ->
    let l = ref [] in
    for i = 1 to n do
        Scanf.scanf "%d %d\n" (fun x y -> l := (x, y) :: !l)
    done;
    print_endline (try let v = satisfy [] [] !l in
    String.concat " " (List.map (fun x -> if List.mem x v then "1" else "0") (iota k))
    with Exit -> "UNSOLVABLE") )

( iota kumarım affedersin).


OCaml sürümünü görmek güzel! Fonksiyonel programlar için güzel bir Rosetta Stone'un başlangıcını yapar. Şimdi Scala ve F # sürümlerini alabilirsek ... - Algoritma gelince - siz burada bahsetene kadar bu PDF'yi görmedim! Uygulamamı Wikipedia sayfasının "Sınırlı Geri Çekme" açıklamasına dayandırdım.
MtnViewMark
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.