2-SAT (बूलियन संतुष्टि) को हल करें


16

सामान्य सैट (बूलियन संतोषजनक) समस्या एनपी-पूर्ण है। लेकिन 2-SAT , जहां प्रत्येक खंड में केवल 2 चर हैं, पी में है । 2-SAT के लिए एक सॉल्वर लिखें।

इनपुट:

2-SAT उदाहरण, CNF में इनकोडिंग निम्नानुसार है। पहली पंक्ति में V, बूलियन चर की संख्या और N, खंड की संख्या शामिल है। तब एन लाइनें अनुसरण करती हैं, प्रत्येक 2 नॉनजो पूर्णांक के साथ एक खंड के शाब्दिक का प्रतिनिधित्व करती हैं। सकारात्मक पूर्णांक दिए गए बूलियन चर का प्रतिनिधित्व करते हैं और नकारात्मक पूर्णांक चर की नकारात्मकता का प्रतिनिधित्व करते हैं।

उदाहरण 1

इनपुट

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

जो सूत्र encodes (एक्स 1 या एक्स 2 ) और (एक्स 2 या एक्स 3 ) और (एक्स 3 या एक्स 4 ) और (नहीं एक्स 1 या एक्स नहीं 3 ) और (नहीं एक्स 2 या एक्स नहीं 4 )

4 चर की एकमात्र सेटिंग जो पूरे सूत्र को सत्य बनाती है x 1 = गलत, x 2 = true, x 3 = true, x 4 = false है , इसलिए आपके प्रोग्राम को सिंगल लाइन आउटपुट करना चाहिए

उत्पादन

0 1 1 0

V चरों के सत्य मानों का प्रतिनिधित्व करना ( x 1 से x V के क्रम में )। यदि कई समाधान हैं, तो आप उनमें से किसी भी गैर-रिक्त सबसेट, प्रति पंक्ति एक आउटपुट कर सकते हैं। यदि कोई समाधान नहीं है, तो आपको आउटपुट करना होगा UNSOLVABLE

उदाहरण 2

इनपुट

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

उत्पादन

UNSOLVABLE

उदाहरण 3

इनपुट

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

उत्पादन

0 1

उदाहरण 4

इनपुट

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

उत्पादन

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

(या उन 3 पंक्तियों के किसी भी गैर-रिक्त उपसमुच्चय)

आपके कार्यक्रम को उचित समय में सभी N, V ​​<100 को संभालना चाहिए। प्रयास करें इस उदाहरण सुनिश्चित करें कि आपके कार्यक्रम एक बड़ा उदाहरण संभाल कर सकते हैं बनाने के लिए। सबसे छोटा कार्यक्रम जीत जाता है।


आप उल्लेख करते हैं कि 2-SAT पी में है, लेकिन ऐसा नहीं है कि यह एक आवश्यकता है कि समाधान बहुपद समय में चलना चाहिए ;-)
टिमवी

@ टिमी: नहीं, लेकिन इसे उचित समय में वी = ९९ को संभालना होगा ...
कीथ रान्डेल

जवाबों:


4

हास्केल, 278 वर्ण

(∈)=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

पाशविक बल नहीं। बहुपद समय में चलता है। कठिन समस्या को हल करता है (60 चर, 99 खंड) जल्दी से:

> 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

और वास्तव में, उस समय का अधिकांश भाग कोड संकलित करने में खर्च होता है!

पूर्ण स्रोत फ़ाइल, परीक्षण मामलों और त्वरित जाँच परीक्षणों के साथ उपलब्ध है

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 संस्करण में, satisfyऔर formatमें ला दिया जाएगा reduce, हालांकि बचने के निधन के क्रम में n, reduceचर (की एक सूची से एक फ़ंक्शन [1..n]) स्ट्रिंग परिणाम के लिए।


  • संपादित करें: (३३० -> ३२३) ने sएक ऑपरेटर बना दिया , नई लाइन की बेहतर हैंडलिंग
  • संपादित करें: (323 -> 313) परिणामों की एक आलसी सूची से पहला तत्व एक कस्टम शॉर्ट-सर्किट ऑपरेटर से छोटा है; मुख्य सॉल्वर फ़ंक्शन का नाम बदला गया क्योंकि मैं ऑपरेटर के रूप में उपयोग करना पसंद करता हूं !
  • संपादित करें: (313 -> 296) एक सूची के रूप में खंडों को रखें, सूचियों की सूची नहीं; एक बार में दो तत्वों को संसाधित करें
  • संपादित करें: (296 -> 291) दो परस्पर पुनरावर्ती कार्यों को मिला दिया; यह इनलाइन सस्ता था इसलिए अब नाम बदला गया
  • संपादित करें: (291 -> 278) परिणाम जनरेशन में आउटपुट स्वरूपण का निर्माण

4

जे, ११ ९ १०३

echo'UNSOLVABLE'"_`(#&c)@.(*@+/)(3 :'*./+./"1(*>:*}.i)=y{~"1 0<:|}.i')"1 c=:#:i.2^{.,i=:0&".;._2(1!:1)3

संपादित करें: हटा दिया गया है (n#2)और इस प्रकार n=:, साथ ही साथ कुछ रैंक पार्न्स (धन्यवाद, आइसड्रॉन) को समाप्त कर रहा है। टैसीट-> स्पष्ट और डाइएडिक-> मोनैडिक, प्रत्येक और कुछ वर्णों को समाप्त करना। }.}.को }.,

संपादित करें: वूप्स। न केवल बड़े एन के लिए यह एक गैर-समाधान है, लेकिन i. 2^99x- मूर्खता के लिए अपमान को जोड़ने के लिए "डोमेन त्रुटि"।

यहाँ अनगढ़ा मूल संस्करण और संक्षिप्त विवरण है।

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 प्रत्येक लाइन पर newlines और पर्स नंबर पर इनपुट काटता है (इनपुट में परिणाम जमा करता है)।
  • n को सौंपा गया है n, खंड मैट्रिक्स को सौंपा गया है clauses(खंड गणना की आवश्यकता नहीं है)
  • cases0..2 n -1 बाइनरी अंकों में परिवर्तित हो गया है (सभी परीक्षण मामले)
  • (Long tacit function)"(_,1)casesसभी के साथ प्रत्येक मामले में लागू होता है clauses
  • <:@|@[{"(0,1)] क्लॉज़ के ऑपरेंड का एक मैट्रिक्स प्राप्त करता है (एब्स (सेशन नंबर) ले कर - 1 और केस से डीरेफरिंग, जो एक एरे है)
  • *@>:@*@[ संकेत के दुरुपयोग के माध्यम से 'नहीं' बिट्स (नहीं के लिए 0) के खंड के आकार का सरणी हो जाता है।
  • = नहीं बिट्स ऑपरेंड पर लागू होता है।
  • [:*./[:+./"1+.परिणामी मैट्रिक्स की पंक्तियों में (और) लागू होता है , और *.(या) उस परिणाम के पार।
  • उन सभी परिणामों के प्रत्येक मामले के लिए 'जवाब' के एक बाइनरी सरणी के रूप में समाप्त होता है।
  • *@+/ परिणामों पर लागू होने पर 0 मिलता है यदि परिणाम हैं और 1 यदि कोई नहीं है।
  • ('UNSOLVABLE'"_) `(#&cases) @.(*@+/) results यदि 0 पर 'UNSOLVABLE' और 'प्रत्येक' समाधान 'तत्वों की एक प्रति है तो 1 को देने के लिए निरंतर फ़ंक्शन चलता है।
  • echo जादू-परिणाम को छापता है।

आप रैंक के तर्कों के इर्दगिर्द के परगनों को हटा सकते हैं। "(_,1)को "_ 1#:बाएं तर्क के बिना काम करेगा।
इवड्रोनस

@isawdrones: मुझे लगता है कि पारंपरिक उत्तर मेरी आत्मा को कुचलने के लिए होगा, जब तक कि उत्तर आधा न हो जाए। "चीखें और उछलें", जैसा कि कजिन कहेगा। धन्यवाद, हालांकि, यह 10-विषम वर्णों को समाप्त करता है ... जब मैं इसे वापस प्राप्त करता हूं तो मुझे 100 से कम हो सकता है।
जेसी मिलिकन 18

अच्छा और विस्तृत विवरण के लिए +1, बहुत ही आकर्षक पठन!
टिमवी

संभवत: उचित समय में N = V = 99 को हैंडल नहीं किया जाएगा। मैं अभी जोड़ा गया बड़ा उदाहरण की कोशिश करो।
कीथ रान्डेल

3

के - 89

जे समाधान के रूप में एक ही विधि।

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

अच्छा, मुझे नहीं पता था कि एक मुफ्त के कार्यान्वयन था।
जेसी मिलिकन 23

संभवत: उचित समय में N = V = 99 को हैंडल नहीं किया जाएगा। मैं अभी जोड़ा गया बड़ा उदाहरण की कोशिश करो।
कीथ रान्डेल

2

रूबी, 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'

लेकिन यह धीमा है :(

एक बार विस्तार से पढ़ने योग्य:

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'

संभवत: उचित समय में N = V = 99 को हैंडल नहीं किया जाएगा। मैं अभी जोड़ा गया बड़ा उदाहरण की कोशिश करो।
कीथ रान्डेल

1

OCaml + बैटरियों, 438 436 वर्ण

एक OCaml बैटरी की आवश्यकता होती है जिसमें शीर्ष स्तर शामिल होता है:

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))

मुझे स्वीकार करना चाहिए, यह हास्केल समाधान का सीधा अनुवाद है। मेरे बचाव में, कि बदले में यहाँ प्रस्तुत एल्गोरिदम का एक सीधा कोडिंग है [PDF], एक ही फ़ंक्शन में परस्पर satisfy- eliminateपुनरावृत्ति लुढ़का हुआ है। कोड का एक अप्रयुक्त संस्करण, बैटरी का उपयोग शून्य करता है:

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 kमुझे उम्मीद है कि आप माफ कर देंगे)।


OCaml संस्करण को देखकर अच्छा लगा! यह कार्यात्मक कार्यक्रमों के लिए एक अच्छा रोसेटा स्टोन की शुरुआत करता है। अब अगर हम स्काला और एफ # संस्करण प्राप्त कर सकते हैं ... - जैसा कि एल्गोरिथ्म के लिए - मैंने उस पीडीएफ को तब तक नहीं देखा था जब तक कि आपने यहां उल्लेख नहीं किया था! मैंने "सीमित बैकग्राउंडिंग" विकिपीडिया पृष्ठ के वर्णन के अपने कार्यान्वयन को आधारित किया।
माउंटनवार्कर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.