reversible "binary to number" predicate - prolog

What is the best way to convert binary bits (it might be a list of 0/1, for example) into numbers in a reversible way. I've written a native predicate in swi, but is there better solution ?
Best regards

Use CLP(FD) constraints, for example:
:- use_module(library(clpfd)).
binary_number(Bs0, N) :-
reverse(Bs0, Bs),
foldl(binary_number_, Bs, 0-0, _-N).
binary_number_(B, I0-N0, I-N) :-
B in 0..1,
N #= N0 + B*2^I0,
I #= I0 + 1.
Example queries:
?- binary_number([1,0,1], N).
N = 5.
?- binary_number(Bs, 5).
Bs = [1, 0, 1] .
?- binary_number(Bs, N).
Bs = [],
N = 0 ;
Bs = [N],
N in 0..1 ;
etc.

Here is the solution I was thinking of, or rather what I hoped exists.
:- use_module(library(clpfd)).
binary_number(Bs, N) :-
binary_number_min(Bs, 0,N, N).
binary_number_min([], N,N, _M).
binary_number_min([B|Bs], N0,N, M) :-
B in 0..1,
N1 #= B+2*N0,
M #>= N1,
binary_number_min(Bs, N1,N, M).
This solution also terminates for queries like:
?- Bs = [1|_], N #=< 5, binary_number(Bs, N).

The solution
This answer seeks to provide a predicate binary_number/2 that presents both logical-purity and the best termination properties. I've used when/2 in order to stop queries like canonical_binary_number(B, 10) from going into infinite looping after finding the first (unique) solution. There is a trade-off, of course, the program has redundant goals now.
canonical_binary_number([0], 0).
canonical_binary_number([1], 1).
canonical_binary_number([1|Bits], Number):-
when(ground(Number),
(Number > 1,
Pow is floor(log(Number) / log(2)),
Number1 is Number - 2 ^ Pow,
( Number1 > 1
-> Pow1 is floor(log(Number1) / log(2)) + 1
; Pow1 = 1
))),
length(Bits, Pow),
between(1, Pow, Pow1),
length(Bits1, Pow1),
append(Zeros, Bits1, Bits),
maplist(=(0), Zeros),
canonical_binary_number(Bits1, Number1),
Number is Number1 + 2 ^ Pow.
binary_number(Bits, Number):-
canonical_binary_number(Bits, Number).
binary_number([0|Bits], Number):-
binary_number(Bits, Number).
Purity and termination
I claim that this predicate presents logical-purity from construction. I hope I got it right from these answers: one, two and three.
Any goal with proper arguments terminates. If arguments need to be checked, the simplest way to achieve this is using the built-in length/2:
binary_number(Bits, Number):-
length(_, Number),
canonical_binary_number(Bits, Number).
?- binary_number(Bits, 2+3).
ERROR: length/2: Type error: `integer' expected, found `2+3'
Exception: (6) binary_number(_G1642009, 2+3) ? abort
% Execution Aborted
?- binary_number(Bits, -1).
ERROR: length/2: Domain error: `not_less_than_zero' expected, found `-1'
Exception: (6) binary_number(_G1642996, -1) ? creep
Example queries
?- binary_number([1,0,1|Tail], N).
Tail = [],
N = 5 ;
Tail = [0],
N = 10 ;
Tail = [1],
N = 11 ;
Tail = [0, 0],
N = 20 .
?- binary_number(Bits, 20).
Bits = [1, 0, 1, 0, 0] ;
Bits = [0, 1, 0, 1, 0, 0] ;
Bits = [0, 0, 1, 0, 1, 0, 0] ;
Bits = [0, 0, 0, 1, 0, 1, 0, 0] ;
Bits = [0, 0, 0, 0, 1, 0, 1, 0, 0] .
?- binary_number(Bits, N).
Bits = [0],
N = 0 ;
Bits = [1],
N = 1 ;
Bits = [1, 0],
N = 2 ;
Bits = [1, 1],
N = 3 ;
Bits = [1, 0, 0],
N = 4 ;
Bits = [1, 0, 1],
N = 5 .

playing with bits...
binary_number(Bs, N) :-
var(N) -> foldl(shift, Bs, 0, N) ; bitgen(N, Rs), reverse(Rs, Bs).
shift(B, C, R) :-
R is (C << 1) + B.
bitgen(N, [B|Bs]) :-
B is N /\ 1 , ( N > 1 -> M is N >> 1, bitgen(M, Bs) ; Bs = [] ).

Related

Prolog unification doesn't evaluate arithmetic expression

Suppose, I wanted to write a program in prolog, which accepts a number input X, and outputs all value pairs for which the sum is X.
some_pred(X,X1,X2) :-
X1 + X2 = X.
This does not work, because X1 + X2 is not evaluated arithmetically.
some_pred(X,X1,X2) :-
Xtemp is X1 + X2,
Xtemp = X.
The other option I have also doesn't work, because X1 and X2 are not instantiated.
How would someone solve this?
Yes, unification doesn't evaluate arithmetic expressions, and if it did that wouldn't help you because X1 and X2 are undefined so adding them together is meaningless.
You need either to write a search yourself such as a brute force nested loop:
sum_a_b(X, A, B) :-
between(1, X, A),
between(1, X, B),
X is A + B.
Or a more nuanced one where you encode something about arithmetic into it, start with 1+(X-1) and then (2+X-2), etc:
sum_a_b(X, A, B) :-
between(0, X, A),
B is X - A.
Or more generally, learn about clpfd (link1, link2) which can do arithmetic evaluating and solving for missing variables in equations, as well as searching through finite domains of possible values:
:- use_module(library(clpfd)).
sum_a_b(X, A, B) :-
[A, B] ins 1..X,
X #= A + B.
? sum_a_b(5, A, B), label([A, B]).
A = 1,
B = 4 ;
A = 2,
B = 3 ;
...
NB. I'm assuming positive integers, otherwise with negatives and decimals you'll get infinite pairs which sum to any given X.
Here's something very similar, using a list:
pos_ints_sum(Sum, L) :-
compare(C, Sum, 1),
pos_ints_sum_(C, L, Sum).
% 0 means the list has ended
pos_ints_sum_(<, [], 0).
% 1 means there is only 1 possible choice
pos_ints_sum_(=, [1], 1).
pos_ints_sum_(>, [I|T], Sum) :-
% Choose a number within the range
between(1, Sum, I),
% Loop with the remainder
Sum0 is Sum - I,
pos_ints_sum(Sum0, T).
Result in swi-prolog:
?- pos_ints_sum(5, L).
L = [1, 1, 1, 1, 1] ;
L = [1, 1, 1, 2] ;
L = [1, 1, 2, 1] ;
L = [1, 1, 3] ;
L = [1, 2, 1, 1] ;
L = [1, 2, 2] ;
L = [1, 3, 1] ;
L = [1, 4] ;
L = [2, 1, 1, 1] ;
L = [2, 1, 2] ;
L = [2, 2, 1] ;
L = [2, 3] ;
L = [3, 1, 1] ;
L = [3, 2] ;
L = [4, 1] ;
L = [5].
Note: X is a poor choice of variable name, when e.g. Sum can easily be used instead, which has far more meaning.

Prolog write n as sum of consecutive numbers

I'm studying prolog and I want to determine all decomposition of n (n given, positive), as sum of consecutive natural numbers but I don't know how to approach this.
Any ideas ?
The key here is between/3, which relates numbers and ranges. Prolog is not going to conjure up numbers from thin air, you have to give it some clues. In this case, you can assume a range of numbers between 1 and the n which you are given:
decomp2(N, X, Y) :-
between(1, N, X),
between(1, N, Y),
N =:= X + Y.
This will give you the sum of two numbers that yields N:
?- decomp2(5, X, Y).
X = 1,
Y = 4 ;
X = 2,
Y = 3 ;
X = 3,
Y = 2 ;
X = 4,
Y = 1 ;
Once you can get two, you can get a longer list by tearing one value off with decomp2/2 and getting the rest through induction. You just need to come up with a base case, such as, the singleton list of N:
decomp(N, [N]).
decomp(N, [X|L]) :- decomp2(N, X, Y), decomp(Y, L).
Be warned that this is going to produce a lot of repetition!
?- decomp(5, L).
L = [5] ;
L = [1, 4] ;
L = [1, 1, 3] ;
L = [1, 1, 1, 2] ;
L = [1, 1, 1, 1, 1] ;
L = [1, 1, 2, 1] ;
L = [1, 2, 2] ;
L = [1, 2, 1, 1] ;
L = [1, 3, 1] ;
L = [2, 3] ;
L = [2, 1, 2] ;
L = [2, 1, 1, 1] ;
L = [2, 2, 1] ;
L = [3, 2] ;
L = [3, 1, 1] ;
L = [4, 1] ;
You could probably clamp down on the repetition by introducing an ordering requirement, such as that X be greater than Y.
I have reached the solution and it looks something like this:
Remark: in isConsecutive i get rid of the "solution" when the list is the number itself
% equal with the given parameter N.
% generatePair(N - integer, X - integer, Y - integer)
% generatePair(i,o,o)
% generatePair(N) = { (X,Y), X<Y && X+Y=N
generatePair(N, X, Y) :-
my_between(1, N, Y),
my_between(1, N, X),
X < Y,
N =:= X + Y.
% This predicate decomposes the given number N into a list of integers
% such that their sum is equal to N.
% decomposeNumber(N - integer, L - list)
% decomposeNumber(i,o)
% decomposeNumber(N) = { [X|L]
decomposeNumber(N, [N]).
decomposeNumber(N, [X|L]) :- generatePair(N, X, Y), decomposeNumber(Y, L).
% This predicate checks it the that elements in the given list have
% consecutive value.
% isConsecutive(L - list)
% isConsecutive(i)
% isConsecutive([l1,l2,..,ln]) = { true, L=[l1,l2] && l1+1=l2
% { isConsecutive(l2..ln), l1+1=l2 && n>2
% { false, otherwise
isConsecutive([X,Y]):-X+1=:=Y.
isConsecutive([H1,H2|T]):-H2=:=H1+1, isConsecutive([H2|T]).
nAsSumOfConsecutives(N,L):-decomposeNumber(N,X), isConsecutive(X), L=X.
main(N,L):-findall(R,nAsSumOfConsecutives(N,R),L).

Find all natural divisors of a number (with Prolog)

I want to create a predicate divisors(X,[Y]) which is true if
X>1 and Y is the list of all divisors of X starting with X and going down to 1.
What my code right now looks like:
divisors(1,[1]).
divisors(X,[Y,Z|Ys]) :-
X>0,
Y is X,
Y>Z,
divides(X,[Z|Ys]).
divides(X,[Y,Z|Ys]) :-
Y>Z,
0 is X mod Y,
divides(X,[Z|Ys]).
divides(X,[1]).
But there are several problems with it:
prolog returns an error if asked for the list (e.g. ?-divisors(10,X).)
?- divisors(X,[Y]). Where [Y] is an incomplete list of divisors is true...
Edit by Guy Coder
This answer is by the OP and was posted in a comment below.
Moving here so others can see it.
divisors(X,R) :-
X > 1,
divisors(X,1,[],R).
divisors(X,D,R,R):-
D>X.
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 + 1,
divisors(N,D,R1,R).
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(N,D,R0,R0).
divides(N,D) :-
0 is N mod D.
Op also noted some errors in this version:
It doesn't terminate if I ask a wrong statement like (10,[1,2,3]).
It throws an error if I ask a statement like (X, [10,5,2,1]). (-> Arguments are not sufficiently initialized.)
While the answer by William is nice and probably faster here is answer closer to what you were writing.
divides(N,D) :-
0 is N mod D.
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(N,D,R0,R0) :-
\+ divides(N,D).
divisors(_,0,R,R).
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 - 1,
divisors(N,D,R1,R).
divisors(X,R) :-
X > 1,
divisors(X,X,[],R), !.
Example:
?- between(1,15,N), divisors(N,Rs).
N = 2,
Rs = [1, 2] ;
N = 3,
Rs = [1, 3] ;
N = 4,
Rs = [1, 2, 4] ;
N = 5,
Rs = [1, 5] ;
N = 6,
Rs = [1, 2, 3, 6] ;
N = 7,
Rs = [1, 7] ;
N = 8,
Rs = [1, 2, 4, 8] ;
N = 9,
Rs = [1, 3, 9] ;
N = 10,
Rs = [1, 2, 5, 10] ;
N = 11,
Rs = [1, 11] ;
N = 12,
Rs = [1, 2, 3, 4, 6, 12] ;
N = 13,
Rs = [1, 13] ;
N = 14,
Rs = [1, 2, 7, 14] ;
N = 15,
Rs = [1, 3, 5, 15].
Edit
OP modified their code, see update in question and had some errors.
This version resolves those errors.
divisors(X,R) :-
(
var(X)
->
false
;
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
).
divisors_2(_,[]).
divisors_2(X,[H|T]) :-
divides(X,H),
divisors_2(X,T).
divisors(X,D,R,R):-
D>X.
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 + 1,
divisors(N,D,R1,R).
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(_,_,R0,R0).
divides(N,D) :-
0 is N mod D.
The first error: It doesn't terminate if I ask a wrong statement like divisors(10,[1,2,3]).
is fixed by adding to divisors/2
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
and
divisors_2(_,[]).
divisors_2(X,[H|T]) :-
divides(X,H),
divisors_2(X,T).
which just processes the list of denominators instead of generating a list.
The second error: It throws an error if I ask a statement like divisors(X, [10,5,2,1]). (-> Arguments are not sufficiently initialized.)
is resolved by further adding to divisor/2
divisors(X,R) :-
(
var(X)
->
false
;
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
).
which checks if the first parameter X is a variable and if so just returns false. The other option would be to generate an infinite list of answers. While possible it wasn't requested.
In Prolog, it is quite common to use backtracking and propose multiple solutions to the same query. Instead of constructing a list of dividers, we thus can construct a predicate that unifies the second parameter with all divisors. For example:
divisor(N, D) :-
between(1, N, D),
0 is N mod D.
This then yields:
?- divisor(12, N).
N = 1 ;
N = 2 ;
N = 3 ;
N = 4 ;
N = 6 ;
N = 12.
The above algorithm is an O(n) algorithm: we scan for divisors linear with the value of the item for which we want to obtain the divisors. We can easily improve this to O(√n) by scanning up to √n, and each time yield both the divisor (of course in case it is a divisor), and the co-divisor, like:
emitco(D, _, D).
emitco(D, C, C) :-
dif(D, C).
divisor(N, R) :-
UB is floor(sqrt(N)),
between(1, UB, D),
0 is N mod D,
C is N / D,
emitco(D, C, R).
This still yield the correct answers, but the order is like a convergent alternating sequence:
?- divisor(12, N).
N = 1 ;
N = 12 ;
N = 2 ;
N = 6 ;
N = 3 ;
N = 4.
?- divisor(16, N).
N = 1 ;
N = 16 ;
N = 2 ;
N = 8 ;
N = 4 ;
false.
We can obtain a list of the divisors by using a findall/3 [swi-doc] or setof/3 [swi-doc]. The setof/3 will even sort the divisors, so we can implement divisors/2 in terms of divisor/2:
divisors(N, Ds) :-
setof(D, divisor(N, D), Ds).
For example:
?- divisors(2, N).
N = [1, 2].
?- divisors(3, N).
N = [1, 3].
?- divisors(5, N).
N = [1, 5].
?- divisors(12, N).
N = [1, 2, 3, 4, 6, 12].
?- divisors(15, N).
N = [1, 3, 5, 15].
We can use reverse/2 to reverse that result.

Create a list from zero down to a negative number

I want to make a program in which the user will give a negative number and it will return a list starting from zero till that number. Here is a desired output example
create(-5,L).
L = [0,-1,-2,-3,-4,-5]
could you help me in any way, please?
I would break it up into two auxiliary predicates. The auxiliary predicate is helpful for building the list in the direction you desire.
create(N, L) :-
N < 0,
create_neg(N, 0, L).
create(N, L) :-
N >= 0,
create_pos(N, 0, L).
create_neg(N, N, [N]).
create_neg(N, A, [A|T]) :-
A > N,
A1 is A - 1,
create_neg(N, A1, T).
create_pos(N, N, [N]).
create_pos(N, A, [A|T]) :-
A < N,
A1 is A + 1,
create_pos(N, A1, T).
This will put them in the right order as well:
| ?- create(-5, L).
L = [0,-1,-2,-3,-4,-5] ? a
no
| ?- create(5, L).
L = [0,1,2,3,4,5] ? a
no
| ?-
What you're after is not really a program, just an 'idiomatic' pattern:
?- findall(X, (between(0,5,T), X is -T), L).
L = [0, -1, -2, -3, -4, -5].
Note the parenthesis around the Goal. It's a compound one...
Another way:
?- numlist(-5,0,T), reverse(T,L).
...
Since you provided your code (which as mentioned in comments would be better to appear in your question), one problem I think is that with X>0 and X<0 clauses-cases you will have infinite recursion, maybe it would be better to use abs/1:
create(0,[0]).
create(X,[X|T]):- Y is abs(X), Y > 0,
(X>0 -> N is X-1 ; N is X+1),
create(N,T).
Though still one problem:
?- create(-5,L).
L = [-5, -4, -3, -2, -1, 0] ;
false.
?- create(5,L).
L = [5, 4, 3, 2, 1, 0] ;
false.
The list is built reversed so you could reverse it at the end like:
create_list(N,L):- create(N,L1), reverse(L1, L).
And now:
?- create_list(5,L).
L = [0, 1, 2, 3, 4, 5] ;
false.
?- create_list(-5,L).
L = [0, -1, -2, -3, -4, -5] ;
false.

Prolog - merge digits to number

I want to merge list of digits to number.
[1,2,3] -> 123
My predicate:
merge([X], X).
merge([H|T], X) :-
merge(T, X1),
X is X1 + H * 10.
But now I get:
[1,2,3] -> 33
Another way to do it would be to multiply what you've handled so far by ten, but you need an accumulator value.
merge(Digits, Result) :- merge(Digits, 0, Result).
merge([X|Xs], Prefix, Result) :-
Prefix1 is Prefix * 10 + X,
merge(Xs, Prefix1, Result).
merge([], Result, Result).
The math is off. You're rule says you have to multiply H by 10. But really H needs to be multiplied by a power of 10 equivalent to its position in the list. That would be * 100 for the 1, and * 10 for the 2. What you get now is: 10*1 + 10*2 + 3 which is 33. The problem is that your recursive clause doesn't know what numeric "place" the digit is in.
If you structure the code differently, and use an accumulator, you can simplify the problem. Also, by using CLP(FD) and applying some constraints on the digits, you can have a more general solution.
:- use_module(library(clpfd)).
digits_number(Digits, X) :-
digits_number(Digits, 0, X).
digits_number([], S, S).
digits_number([D|Ds], S, X) :-
D in 0..9,
S1 #= S*10 + D,
digits_number(Ds, S1, X).
?- digits_number([1,2,3], X).
X = 123
?- digits_number(L, 123).
L = [1, 2, 3] ;
L = [0, 1, 2, 3] ;
L = [0, 0, 1, 2, 3] ;
L = [0, 0, 0, 1, 2, 3] ;
L = [0, 0, 0, 0, 1, 2, 3]
...
?-

Resources