2012-06-07 50 views
7

में मौखिक अंकगणित का तेज़ कार्यान्वयन मैंने पहले से ही प्रोलॉग में एक सामान्यीकृत verbal arithmetic सॉल्वर बनाया है लेकिन यह बहुत धीमा है। सरल अभिव्यक्ति एस ई एन डी + एम ओ आर ई = एम ओ एन ई वाई चलाने के लिए बस 8 मिनट लगते हैं। क्या कोई इसे तेजी से चलाने में मेरी सहायता कर सकता है?प्रोलॉग

/* verbalArithmetic(List,Word1,Word2,Word3) where List is the list of all 
possible letters in the words. The SEND+MORE = MONEY expression would then 
be represented as 
    verbalArithmetic([S,E,N,D,M,O,R,Y],[S,E,N,D],[M,O,R,E],[M,O,N,E,Y]). */ 

validDigit(X) :- member(X,[0,1,2,3,4,5,6,7,8,9]). 
validStart(X) :- member(X,[1,2,3,4,5,6,7,8,9]). 
assign([H|[]]) :- validDigit(H).   
assign([H|Tail]) :- validDigit(H), assign(Tail), fd_all_different([H|Tail]). 

findTail(List,H,T) :- append(H,[T],List). 

convert([T],T) :- validDigit(T). 
convert(List,Num) :- findTail(List,H,T), convert(H,HDigit), Num is (HDigit*10+T). 

verbalArithmetic(WordList,[H1|Tail1],[H2|Tail2],Word3) :- 
    validStart(H1), validStart(H2), assign(WordList), 
    convert([H1|Tail1],Num1),convert([H2|Tail2],Num2), convert(Word3,Num3), 
    Sum is Num1+Num2, Num3 = Sum. 

उत्तर

3

नोट: यह उत्तर कोशिश करने की आवश्यकता वाले संयोजनों की संख्या को कम करने के लिए एल्गोरिदम पर चर्चा करता है। मैं प्रोलॉग नहीं जानता, इसलिए मैं कोई कोड स्निपेट नहीं दे सकता।

एक ब्रूट फोर्स समाधान को गति देने की चाल शॉर्टकट है। यदि आप अमान्य हैं कि संयोजनों की एक श्रृंखला की पहचान कर सकते हैं तो आप संयोजनों की संख्या को काफी कम कर सकते हैं।

हाथ में उदाहरण लें। जब कोई मानव इसे हल करता है, तो उसने तुरंत नोटिस किया कि पैसा के पास 5 अंक हैं जबकि SEND और अधिक में केवल 4 है, इसलिए एम में पैसा होना चाहिए 1. संयोजन का 9 0% चला गया!

जब एक कंप्यूटर के लिए एक एल्गोरिथ्म का निर्माण, हम शॉर्टकट है कि सभी संभव इनपुट के लिए सबसे पहले लागू उपयोग करने के लिए प्रयास करें। यदि वे आवश्यक प्रदर्शन देने में असफल होते हैं तो हम शॉर्टकट को देखना शुरू करते हैं जो केवल इनपुट के विशिष्ट संयोजनों पर लागू होता है। तो हम अब के लिए एम = 1 शॉर्टकट छोड़ दें।

इसके बजाय, मैं अंतिम अंक पर ध्यान केंद्रित करूंगा। हम जानते हैं कि (डी + ई) मॉड 10 = वाई यह कोशिश करने के लिए संयोजनों की संख्या में हमारी 90% कमी है।

उस चरण को केवल एक मिनट के भीतर निष्कासन लाया जाना चाहिए।

यदि हम पर्याप्त नहीं हैं तो हम क्या कर सकते हैं? अगला चरण: अंतिम अंक के लिए दूसरे को देखें! हम जानते हैं कि (एन + आर + डी + ई से ले जाता है) मॉड 10 = ई

चूंकि हम अंतिम अंक के सभी वैध संयोजनों के माध्यम से परीक्षण कर रहे हैं, प्रत्येक परीक्षण के लिए हम जान लेंगे कि कैरी 0 या 1 है एक जटिलता (कोड के लिए) जो जांच के लिए संयोजनों की संख्या को कम कर देता है यह है कि हम डुप्लिकेट का सामना करेंगे (एक पत्र उस नंबर पर मैप हो जाता है जो पहले से ही किसी अन्य पत्र को सौंपा गया है)। जब हम डुप्लिकेट का सामना करते हैं, तो हम चेन को आगे बढ़ाने के बिना अगले संयोजन में अग्रिम कर सकते हैं।

आपके असाइनमेंट के साथ शुभकामनाएँ!

+1

बहुत अच्छा तर्क, +1! दृश्यों के पीछे सीएलपी (एफडी) संस्करण आपके लिए बिल्कुल सही है। उदाहरण के लिए, जब मैं पूछता हूं: '? - पहेली ([एस, ई, एन, डी] + [एम, ओ, आर, ई] = [एम, ओ, एन, ई, वाई])। ', तो मुझे मिलता है परिवर्तनीय बाइंडिंग के रूप में: 'एम = 1, ओ = 0, एस = 9', इसलिए पहेली को वर्णित सीएलपी (एफडी) बाधाओं को पोस्ट करके पहले से ही कंक्रीट पूर्णांक के लिए 3 चर आसानी से तय किए जाते हैं। शेष चर के डोमेन भी कम हो जाते हैं, जैसा कि हम अवशिष्ट लक्ष्यों से देखते हैं: '5 में एन, 4 में 4 ई, 7 में 2..8, वाई 2..8' में। एक अंतिम खोज चरण सभी सीएलपी (एफडी) चर के लिए ठोस पूर्णांक बाइंडिंग के रूप में अद्वितीय समाधान पाता है। – mat

6

SWI-Prolog में, finite domain constraints का उपयोग कर उदाहरण के लिए, पर विचार करें:

:- use_module(library(clpfd)). 

puzzle([S,E,N,D] + [M,O,R,E] = [M,O,N,E,Y]) :- 
     Vars = [S,E,N,D,M,O,R,Y], 
     Vars ins 0..9, 
     all_different(Vars), 
        S*1000 + E*100 + N*10 + D + 
        M*1000 + O*100 + R*10 + E #= 
     M*10000 + O*1000 + N*100 + E*10 + Y, 
     M #\= 0, S #\= 0. 

उदाहरण क्वेरी:

?- time((puzzle(As+Bs=Cs), label(As))). 
% 5,803 inferences, 0.002 CPU in 0.002 seconds (98% CPU, 3553582 Lips) 
As = [9, 5, 6, 7], 
Bs = [1, 0, 8, 5], 
Cs = [1, 0, 6, 5, 2] ; 
% 1,411 inferences, 0.001 CPU in 0.001 seconds (97% CPU, 2093472 Lips) 
false. 
4

खराब प्रदर्शन यहाँ की जाँच से पहले सभी संभव पत्र कार्य के गठन की वजह से है, यदि कोई हो संभव है ।

मेरी सलाह "जल्दी विफल, अक्सर असफल" है। यही है, असाइनमेंट चरणों में जितनी जल्दी संभव हो सके विफलता के लिए कई चेक दबाएं, इस प्रकार खोज पेड़ को काट लें।

क्लास लिंडबैक कुछ अच्छे सुझाव देता है। एक सामान्यीकरण के रूप में, जब दो संख्याओं को जोड़ते हैं तो प्रत्येक स्थान पर वाहक सबसे अधिक होता है। तो बाएं से दाएं अक्षरों के लिए अलग-अलग अंकों के असाइनमेंट को सही जगहों पर एक अभी तक अनिश्चितकालीन वाहक की संभावना के लिए भत्ता के साथ चेक किया जा सकता है। (निश्चित रूप से अंतिम "इकाइयों" जगह में, कोई वाह नहीं है।)

यह चटाई के रूप में पता चलता है (और आप पहले से ही fd_all_different/1) के साथ broached किया है जो, इस तरह के एक सुविधा है, एक बहुत के बारे में सोचना, यही वजह है कि बाधा तर्क है।


जोड़ा गया: यहाँ बाधा तर्क के बिना एक Prolog समाधान है, सिर्फ एक सहायक विधेय न आना/3 का उपयोग कर:

omit(H,[H|T],T). 
omit(X,[H|T],[H|Y]) :- omit(X,T,Y). 

जो दोनों एक सूची से किसी आइटम का चयन करता है और छोटा सूची का उत्पादन उस वस्तु के बिना।

यहाँ तो के लिए कोड है sendMoreMoney/3 उस राशि का मूल्यांकन बाएं से दाएं द्वारा खोज:

sendMoreMoney([S,E,N,D],[M,O,R,E],[M,O,N,E,Y]) :- 
    M = 1, 
    omit(S,[2,3,4,5,6,7,8,9],PoolO), 
    (CarryS = 0 ; CarryS = 1), 
    %% CarryS + S + M =  M*10 + O 
    O is (CarryS + S + M) - (M*10), 
    omit(O,[0|PoolO],PoolE), 
    omit(E,PoolE,PoolN), 
    (CarryE = 0 ; CarryE = 1), 
    %% CarryE + E + O = CarryS*10 + N 
    N is (CarryE + E + O) - (CarryS*10), 
    omit(N,PoolN,PoolR), 
    (CarryN = 0 ; CarryN = 1), 
    %% CarryN + N + R = CarryE*10 + E 
    R is (CarryE*10 + E) - (CarryN + N), 
    omit(R,PoolR,PoolD), 
    omit(D,PoolD,PoolY), 
    %%   D + E = CarryN*10 + Y 
    Y is (D + E) - (CarryN*10), 
    omit(Y,PoolY,_). 

हम चाहते हैं कि एम को देख कर एक त्वरित शुरुआत कर लेते हैं से अशून्य कैरी होना चाहिए बाएं सबसे अधिक अंक, इसलिए 1, और वह एस कुछ अन्य nonzero अंक होना चाहिए। टिप्पणियां उन चरणों को दिखाती हैं जहां अतिरिक्त अक्षर निर्धारित किए गए विकल्पों के आधार पर निर्धारित मूल्य निर्धारित किए जा सकते हैं।


जोड़ा गया (2): यहाँ दो summands है, जो "स्थानों" का एक ही लंबाई/संख्या है की जरूरत नहीं है के लिए एक "सामान्य" cryptarithm solver है। लंबाई/2 के लिए कोड एक बहुत ही आम निर्मित विधेय के रूप में छोड़ दिया जाता है, और विल नेस से सुझाव लेने, SWI-Prolog उपयोगकर्ताओं की सुविधा के लिए चयन/3न आना/3ने ले ली है करने के लिए कहता है।

मैंने इसका परीक्षण अजी के साथ किया है! और एसडीआई-प्रोलॉग उन अल्फामेटिक्स उदाहरणों का उपयोग करते हुए from Cryptarithms.com जिसमें दो सारांश शामिल हैं, जिनमें से प्रत्येक का एक अद्वितीय समाधान है। मैंने उचित बैकट्रैकिंग का परीक्षण करने के लिए एक दर्जन समाधान, I + AM = BEN के साथ एक उदाहरण भी बनाया।

solveCryptarithm([H1|T1],[H2|T2],Sum) :- 
    operandAlign([H1|T1],[H2|T2],Sum,AddTop,AddPad,Carry,TSum,Pool), 
    solveCryptarithmAux(H1,H2,AddTop,AddPad,Carry,TSum,Pool). 

operandAlign(Add1,Add2,Sum,AddTop,AddPad,Carry,TSum,Pool) :- 
    operandSwapPad(Add1,Add2,Length,AddTop,AddPad), 
    length(Sum,Size), 
    ( Size = Length 
    -> (Carry = 0, Sum = TSum , Pool = [1|Peel]) 
    ; (Size is Length+1, Carry = 1, Sum = [Carry|TSum], Pool = Peel) 
    ), 
    Peel = [2,3,4,5,6,7,8,9,0]. 

operandSwapPad(List1,List2,Length,Longer,Padded) :- 
    length(List1,Length1), 
    length(List2,Length2), 
    ( Length1 >= Length2 
    -> (Length = Length1, Longer = List1, Shorter = List2, Pad is Length1 - Length2) 
    ; (Length = Length2, Longer = List2, Shorter = List1, Pad is Length2 - Length1) 
    ), 
    zeroPad(Shorter,Pad,Padded). 

zeroPad(L,0,L). 
zeroPad(L,K,P) :- 
    K > 0, 
    M is K-1, 
    zeroPad([0|L],M,P). 

solveCryptarithmAux(_,_,[],[],0,[],_). 
solveCryptarithmAux(NZ1,NZ2,[H1|T1],[H2|T2],CarryOut,[H3|T3],Pool) :- 
    (CarryIn = 0 ; CarryIn = 1), /* anticipatory carry */ 
    ( var(H1) 
    -> select(H1,Pool,P_ol) 
    ; Pool = P_ol 
    ), 
    ( var(H2) 
    -> select(H2,P_ol,P__l) 
    ; P_ol = P__l 
    ), 
    ( var(H3) 
    -> (H3 is H1 + H2 + CarryIn - 10*CarryOut, select(H3,P__l,P___)) 
    ; (H3 is H1 + H2 + CarryIn - 10*CarryOut, P__l = P___) 
    ), 
    NZ1 \== 0, 
    NZ2 \== 0, 
    solveCryptarithmAux(NZ1,NZ2,T1,T2,CarryIn,T3,P___). 

मैं इस दिखाता है लगता है कि के फायदे बाएँ-से-सही खोज/मूल्यांकन, एक "सामान्यीकृत" solver में प्राप्त किया जा सकता पहले की तुलना में दो का लगभग एक पहलू से अनुमान की संख्या में वृद्धि " अनुरूप "कोड।

+0

आपका 'ओमिट/3' एसडब्ल्यूआई-प्रोलॉग ['चयन/3'] है (http://www.swi-prolog.org/pldoc/doc_for?object=select/3)। 'डेल/3',' डिलीट/3' आदि के रूप में जाना जाता है। इसका उपयोग सीमित डोमेन (या "पूल") के प्रत्यक्ष हेरफेर के लिए अनुमति देता है। मेरे उत्तर से 'selectM/3' predicate, आसान और बहुत कम कोडिंग के लिए' चयन/3' के एकाधिक आमंत्रण पैक करता है। इसके अलावा, आपका कोड बहुत सारे मानवीय तर्कों को नियुक्त करता है। –

+0

@WillNess: यह सच है कि एसडब्ल्यूआई-प्रोलॉग में अंतर्निहित (समकक्ष) अंतर्निहित है। मैं बाएं से दाएं मूल्यांकन के लाभ को स्पष्ट करने की कोशिश कर रहा था, जो आपके दाएं से बाएं संस्करण के लिए धन्यवाद, हम तुलना कर सकते हैं। – hardmath

+0

इसलिए मैंने आपके संस्करण की कोशिश की और इसमें 533 (676) संदर्भ/0.00 सेकंड, बनाम 27,653 (38,601) सम्मेलन/0.02 सेकेंड लिया गया जो मेरा संस्करण लेता है। :) यह आपके कोड में जाने वाले मानव तर्क की मात्रा पर विचार करने में आश्चर्य की बात नहीं है, जो तुलना में औपचारिक रूप से औपचारिक रूप से कठिन है (मूल क्यू के बारे में क्या है)। WP लेख उदा। किसी भी कोड के बिना पूर्ण समाधान पर आता है, जो मानव तर्क को थोड़ा और आगे ले जाता है। –

2

आप

convert([A,B,C,D]) => convert([A,B,C])*10 + D 
=> (convert([A,B])*10+C)*10+D => ... 
=> ((A*10+B)*10+C)*10+D 

तो, आप एक सरल रेखीय प्रत्यावर्तन के साथ इस व्यक्त कर सकते हैं।

इससे भी महत्वपूर्ण बात, जब आप अपने डोमेन 0..9 से एक संभव अंकों लेने, तो आप उस अंकों अब और बाद के विकल्पों के लिए उपयोग नहीं करना चाहिए:

selectM([A|As],S,Z):- select(A,S,S1),selectM(As,S1,Z). 
selectM([],Z,Z). 

select/3 SWI Prolog में उपलब्ध है। इस उपकरण के साथ, आप अपने इस प्रकार संकुचन डोमेन से धीरे-धीरे अपने अंक का चयन कर सकते हैं:

money_puzzle([[S,E,N,D],[M,O,R,E],[M,O,N,E,Y]]):- 
    Dom = [0,1,2,3,4,5,6,7,8,9], 
    selectM([D,E], Dom,Dom1), add(D,E,0, Y,C1), % D+E=Y 
    selectM([Y,N,R],Dom1,Dom2), add(N,R,C1,E,C2), % N+R=E 
    select( O,  Dom2,Dom3), add(E,O,C2,N,C3), % E+O=N 
    selectM([S,M], Dom3,_),  add(S,M,C3,O,M), % S+M=MO 
    S \== 0, M \== 0. 

हम एक कैरी के साथ दो अंक जोड़ सकते हैं, नई कैरी के साथ एक है, जिसके परिणामस्वरूप अंकों उत्पादन जोड़ने (जैसे कि, 4+8 (0) = 2 (1) अर्थात12):

add(A,B,C1,D,C2):- N is A+B+C1, D is N mod 10, C2 is N // 10 . 
इस प्रकार लागू किया

, money_puzzle/1 रन तत्क्षण, धीरे-धीरे प्रकृति के लिए धन्यवाद, जिसमें अंक उठाया और अभी परीक्षण कर रहे हैं:

?- time(money_puzzle(X)). 
% 27,653 inferences, 0.02 CPU in 0.02 seconds (100% CPU, 1380662 Lips) 
X = [[9, 5, 6, 7], [1, 0, 8, 5], [1, 0, 6, 5, 2]] ; 
No 
?- time((money_puzzle(X),fail)). 
% 38,601 inferences, 0.02 CPU in 0.02 seconds (100% CPU, 1927275 Lips) 

चुनौती अब हो जाता है यह सामान्य बनाने के लिए ।

+0

एस (एक्स) और चुनौती स्वीकार कर ली गई! – CapelliC

2

यहां मेरा लेना है। मैं , , और mapfoldl/5 का उपयोग करें:

:- meta_predicate mapfoldl(4,?,?,?,?). 
mapfoldl(P_4,Xs,Zs, S0,S) :- 
    list_mapfoldl_(Xs,Zs, S0,S, P_4). 

:- meta_predicate list_mapfoldl_(?,?,?,?,4). 
list_mapfoldl_([],[], S,S, _). 
list_mapfoldl_([X|Xs],[Y|Ys], S0,S, P_4) :- 
    call(P_4,X,Y,S0,S1), 
    list_mapfoldl_(Xs,Ys, S1,S, P_4). 

के बेहतर इस्तेमाल के लिए mapfoldl/5 रख दिया और कुछ मौखिक गणित करते हैं!

:- use_module(library(clpfd)). 
:- use_module(library(lambda)). 

digits_number(Ds,Z) :- 
    Ds = [D0|_], 
    Ds ins 0..9, 
    D0 #\= 0,   % most-significant digit must not equal 0 
    reverse(Ds,Rs), 
    length(Ds,N), 
    numlist(1,N,Es), % exponents (+1) 
    maplist(\E1^V^(V is 10**(E1-1)),Es,Ps), 
    scalar_product(Ps,Rs,#=,Z). 

list([]) --> []. 
list([E|Es]) --> [E], list(Es). 

cryptarithexpr_value([V|Vs],X) --> 
    { digits_number([V|Vs],X) }, 
    list([V|Vs]). 
cryptarithexpr_value(T0,T) --> 
    { functor(T0,F,A) }, 
    { dif(F-A,'.'-2) }, 
    { T0 =.. [F|Args0] }, 
    mapfoldl(cryptarithexpr_value,Args0,Args), 
    { T =.. [F|Args] }. 

crypt_arith_(Expr,Zs) :- 
    phrase(cryptarithexpr_value(Expr,Goal),Zs0), 
    ( member(Z,Zs0), \+var(Z) 
    -> throw(error(uninstantiation_error(Expr),crypt_arith_/2)) 
    ; true 
    ), 
    sort(Zs0,Zs), 
    all_different(Zs), 
    call(Goal). 

त्वरित और गंदे हैक डंप करने के लिए सब समाधान पाया:

solve_n_dump(Opts,Eq) :- 
    ( crypt_arith_(Eq,Zs), 
     labeling(Opts,Zs), 
     format('Eq = (~q), Zs = ~q.~n',[Eq,Zs]), 
     false 
    ; true 
    ). 

solve_n_dump(Eq) :- solve_n_dump([],Eq). 

की कोशिश करते हैं!

 
?- solve_n_dump([S,E,N,D]+[M,O,R,E] #= [M,O,N,E,Y]). 
Eq = ([9,5,6,7]+[1,0,8,5]#=[1,0,6,5,2]), Zs = [9,5,6,7,1,0,8,2]. 
true. 

?- solve_n_dump([C,R,O,S,S]+[R,O,A,D,S] #= [D,A,N,G,E,R]). 
Eq = ([9,6,2,3,3]+[6,2,5,1,3]#=[1,5,8,7,4,6]), Zs = [9,6,2,3,5,1,8,7,4]. 
true. 

?- solve_n_dump([F,O,R,T,Y]+[T,E,N]+[T,E,N] #= [S,I,X,T,Y]). 
Eq = ([2,9,7,8,6]+[8,5,0]+[8,5,0]#=[3,1,4,8,6]), Zs = [2,9,7,8,6,5,0,3,1,4]. 
true. 

?- solve_n_dump([E,A,U]*[E,A,U] #= [O,C,E,A,N]). 
Eq = ([2,0,3]*[2,0,3]#=[4,1,2,0,9]), Zs = [2,0,3,4,1,9]. 
true. 

?- solve_n_dump([N,U,M,B,E,R] #= 3*[P,R,I,M,E]). 
% same as:  [N,U,M,B,E,R] #= [P,R,I,M,E]+[P,R,I,M,E]+[P,R,I,M,E] 
Eq = (3*[5,4,3,2,8]#=[1,6,2,9,8,4]), Zs = [5,4,3,2,8,1,6,9]. 
true. 

?- solve_n_dump(3*[C,O,F,F,E,E] #= [T,H,E,O,R,E,M]). 
Eq = (3*[8,3,1,1,9,9]#=[2,4,9,3,5,9,7]), Zs = [8,3,1,9,2,4,5,7]. 
true. 

चलो कुछ और करते हैं और कोशिश कुछ अलग labeling options:

 
?- time(solve_n_dump([],[D,O,N,A,L,D]+[G,E,R,A,L,D] #= [R,O,B,E,R,T])). 
Eq = ([5,2,6,4,8,5]+[1,9,7,4,8,5]#=[7,2,3,9,7,0]), Zs = [5,2,6,4,8,1,9,7,3,0]. 
% 35,696,801 inferences, 3.929 CPU in 3.928 seconds (100% CPU, 9085480 Lips) 
true. 

?- time(solve_n_dump([ff],[D,O,N,A,L,D]+[G,E,R,A,L,D] #= [R,O,B,E,R,T])). 
Eq = ([5,2,6,4,8,5]+[1,9,7,4,8,5]#=[7,2,3,9,7,0]), Zs = [5,2,6,4,8,1,9,7,3,0]. 
% 2,902,871 inferences, 0.340 CPU in 0.340 seconds (100% CPU, 8533271 Lips) 
true. 
1

शैली, सामान्यीकृत (लेकिन length(A) <= length(B) कल्पना करते हुए) solver नेस होगा:

money_puzzle([A,B,C]) :- 
    maplist(reverse, [A,B,C], [X,Y,Z]), 
    numlist(0, 9, Dom), 
    swc(0, Dom, X,Y,Z), 
    A \= [0|_], B \= [0|_]. 

swc(C, D0, [X|Xs], [Y|Ys], [Z|Zs]) :- 
    peek(D0, X, D1), 
    peek(D1, Y, D2), 
    peek(D2, Z, D3), 
    S is X+Y+C, 
    (S > 9 -> Z is S - 10, C1 = 1 ; Z = S, C1 = 0), 
    swc(C1, D3, Xs, Ys, Zs). 
swc(C, D0, [], [Y|Ys], [Z|Zs]) :- 
    peek(D0, Y, D1), 
    peek(D1, Z, D2), 
    S is Y+C, 
    (S > 9 -> Z is S - 10, C1 = 1 ; Z = S, C1 = 0), 
    swc(C1, D2, [], Ys, Zs). 
swc(0, _, [], [], []). 
swc(1, _, [], [], [1]). 

peek(D, V, R) :- var(V) -> select(V, D, R) ; R = D. 

प्रदर्शन:

?- time(money_puzzle([S,E,N,D],[M,O,R,E],[M,O,N,E,Y])). 
% 38,710 inferences, 0.016 CPU in 0.016 seconds (100% CPU, 2356481 Lips) 
S = 9, 
E = 5, 
N = 6, 
D = 7, 
M = 1, 
O = 0, 
R = 8, 
Y = 2 ; 
% 15,287 inferences, 0.009 CPU in 0.009 seconds (99% CPU, 1685686 Lips) 
false. 

?- time(money_puzzle([D,O,N,A,L,D],[G,E,R,A,L,D],[R,O,B,E,R,T])). 
% 14,526 inferences, 0.008 CPU in 0.008 seconds (99% CPU, 1870213 Lips) 
D = 5, 
O = 2, 
N = 6, 
A = 4, 
L = 8, 
G = 1, 
E = 9, 
R = 7, 
B = 3, 
T = 0 ; 
% 13,788 inferences, 0.009 CPU in 0.009 seconds (99% CPU, 1486159 Lips) 
false.