/* Version using tabling on XSB */ % Thorsten Seelend wrote: /* Mister X thinks about two integers between 1 and 100 excluding: * [MISTERX: Two integers, X and Y between 2 and 99] */ two_integers( X, Y ) :- between( 2, 98, X ), between( X, 99, Y ). /* He tells Susan the Sum of them and Peter their Product. Their task is to * get the two original values without telling each other the numbers that * Mister X told them. * * After some time Peter says: "I can't say definitively which are the original * numbers." * [PETER1: There is more than one pair of factors giving Product] */ :- table peter1/1. peter1( Product ) :- \+ \+ ( ordered_factors( Product, X, _Y ), ordered_factors( Product, X1, _Y1 ), X1 =\= X ). /* Then Susan responds: "Neither can I, but I knew that you couldn't know it." * * [SUSAN1: The product of every pair of summands giving Sum has the property * PETER1] */ :- table susan1/1. susan1( Sum ) :- forall( ordered_summands(Sum, X, Y), (Z is X * Y, peter1(Z)) ). /* Peter: "Really? So now I know the original numbers". * * [PETER2: exactly one pair of factors giving Product gives a sum with the * property SUSAN1] */ :- table peter2/1. peter2( Product ) :- single_solution( (ordered_factors(Product, X, Y), Z is X+Y, susan1(Z)) ). /* Susan: "Now I know them too". * * [SUSAN2: exactly one pair of summands giving Sum has a product with the * property PETER2] */ :- table susan2/1. susan2( Sum ) :- single_solution( (ordered_summands(Sum, X, Y), Z is X * Y, peter2(Z)) ). /* Question: What are the two numbers that Mister X thought of? * [Unique solution] */ solve( X, Y ) :- single_solution( mister_x(X, Y) ). mister_x( X, Y ) :- two_integers( X, Y ), Sum is X + Y, Product is X * Y, peter1( Product ), susan1( Sum ), peter2( Product ), susan2( Sum ). % Support Predicates /* ordered_summands( +Sum, ?X, ?Y ) when X =< Y and Sum = X + Y. * NB: Since X =< Y it follows that X =< Sum/2. */ ordered_summands( Z, X, Y ) :- Half is Z//2, between( 2, Half, X ), Y is Z - X, between( X, 98, Y ). /* ordered_factors( +Product, ?X, ?Y ) when X =< Y and Product = X * Y * NB: Since X =< Y it follows that X =< Product^0.5. */ ordered_factors( Z, X, Y ) :- integer_sqrt( Z, SqrtZ ), between( 2, SqrtZ, X ), Y is Z // X, between( X, 99, Y ), Z =:= X * Y. /* integer_sqrt( +N, ?Sqrt ) when Sqrt^2 =< N and (Sqrt+1)^2 >= N. */ integer_sqrt( N, Sqrt ) :- Sqrt is floor(sqrt(N)). between( Lower, Upper, Index ) :- integer( Lower ), integer( Upper ), Lower =< Upper, ( integer( Index ) -> % Case 1: "test" Index >= Lower, Index =< Upper ; var( Index ) -> % Case 2: "generate". generate_between( Lower, Upper, Index ) ). generate_between( Lower, Upper, Index ) :- ( Lower =:= Upper -> Index = Lower ; Index = Lower ; Next is Lower + 1, Next =< Upper, generate_between( Next, Upper, Index ) ). single_solution( Goal ) :- findall( Goal, Goal, [Solution|Solutions] ), same_solution( Solutions, Solution ), Solution = Goal. same_solution( [], _Solution ). same_solution( [Solution0|Solutions], Solution ) :- Solution0 == Solution, same_solution( Solutions, Solution ). forall( Enumerator, Test ) :- \+ (call(Enumerator), \+ call(Test)).