Calculate Area Tangent Circumferences - Prolog - list

I have the following function that calculates an area.
It receives three parameters, the first is an n representing the number of cases, the 2nd representing the radio of circumferences, and the 3rd is l giving me back the result.
The problem I have is that when the 1st input is greater than 1 it does not work.
This my code:
as(1, [X], A) :-
A is (sqrt(3.0) * (X*X)) - (3.14 * (X*X))/2.
as(N, [H|_T], A) :-
A is (sqrt(3.0) * (H*H)) - (3.14 * (H*H))/2,
N1 is N-1,
as(N1-1, T, A).
An example of how it should work is:
?- as(4, [1,1,1,1], R).
R = 0.162050807568877130000 ;
R = 0.162050807568877130000 ;
R = 0.162050807568877130000 ;
R = 0.162050807568877130000.
If you could help me, I would be grateful ...

Is there a reason this version isn't sufficient to your needs?
as([H|_], A):-
A is (sqrt(3.0) * (H*H)) - (3.14 * (H*H))/2.
as([_|T], A) :- as(T, A).
Or maybe this?
area(H, Area) :-
Area is (sqrt(3.0) * (H*H)) - (3.14 * (H*H))/2.
as(List, Area) :- member(Ratio, List), area(Ratio, Area).
I don't understand why you need to worry about N at all.
Matching both N and [X] leads to redundancy. You shouldn't have to repeat your formula.
You have a lot of singleton errors: _T in the head, and then T in the body which will not work.
You are passing N1-1 to the recursive call, which will not cause it to be evaluated—but you already evaluated N-1 in the previous expression, so just pass N1 here. Again, I don't see the point of this at all.
I think it's a good idea to use succ(N1, N) instead of adding or subtracting one, because it works in both directions (not relevant here, of course).
It feels a bit icky to be combining the list traversal with the calculation to me. I would in general break things down so that the calculation is separate from the data structure insofar as that can be done. This is a universal maxim of programming.

Since you want to compute the area for every measurement anyway, would it not be opportune to get a list of areas corresponding to the list of radio measurements? The structure of your predicate as/3 seems to indicate that you were thinking along those lines. And you could easily achieve that by using maplist/3:
:- use_module(library(apply)). % needed for maplist
% a single measurement and the corresponding area
area(X, A) :-
A is (sqrt(3.0) * (X*X)) - (3.14 * (X*X))/2.
areas(Xs,As) :-
maplist(area,Xs,As). % area/2 mapped to Xs results in As
Querying this predicate yields the desired results but in a list:
?- areas([1,1,5,3],As).
As = [0.16205080756887713, 0.16205080756887713, 4.051270189221931, 1.4584572681198935].

Related

How to simplify algebra equations represented as list of list

With Prolog I want to simplify algebra expression represented as as list of list:
algebra equation
f = 3x+2
list of list
[[3,1],[2,0]]
3 and 2 are coefficients
1 and 0 are exponents
That should be obvious.
I am looking for some tips or suggestions on how to code the simplifications for this example:
f = 3x+2x+1+2
[[3,1],[2,1],[1,0],[2,0]]
simplified:
f = 5x+3
[[5,1],[3,0]]
I have tried some built in functions but did not get the proper idea about how to use them.
One liner, similar to what's proposed by joel76:
simplify(I,O) :-
bagof([S,E],L^(bagof(C,member([C,E],I),L),sum_list(L,S)),O).
The inner bagof collects C (coefficients) given E (exponents), the resulting list L is summed into S, and paired with E becomes [S,E], an element (monomial) of O.
If you omit the universal quantification specifier (that is L^) you get single monomials on backtracking.
You can solve your problem in this way:
simplify(_,_,S,S,[]):- !.
simplify(L,I,Sum,NTot,[[I,S]|T]):-
Sum =< NTot,
findall(X,member([X,I],L),LO),
length(LO,N),
S1 is Sum + N,
sum_list(LO,S),
I1 is I+1,
simplify(L,I1,S1,NTot,T).
write_function([]).
write_function([[D,V]|T]):-
write(' + '),write(V),write('x^'),write(D),
write_function(T).
test:-
L = [[3,1],[2,1],[1,0],[2,0]],
length(L,N),
simplify(L,0,0,N,LO),
LO = [[D,V]|T],
write('f='),write(V),write('x^'),write(D),
write_function(T).
The main predicate is simplify/5 which uses findall/3 to find all the coefficients with the same degree and then sums them using sum_list/2. Then you can write the result in a fancy way using write_function/1.
In SWI-Prolog You can use aggregate :
pred(>, [_,X], [_,Y]) :- X > Y.
pred(<, [_,X], [_,Y]) :- X < Y.
pred(=, [_,X], [_,X]).
simplify(In, Out) :-
aggregate(set([S,X]), aggregate(sum(P), member([P,X], In), S), Temp),
predsort(pred, Temp, Out).
For example :
?- simplify([[3,1],[2,1],[1,0],[2,0]], Out).
Out = [[5, 1], [3, 0]] ;
false.

Modifying a value on a logarithmic curve

I have one value that is a floating point percentage from 0-100, x, and another value that is a floating point from 0-1, y. As y gets closer to zero, it should reduce the value of x on a logarithmic curve.
So for example, say x = 28.0f and y = 0.8f. Since 0.8f isn't that far from 1.0f it should only reduce the value of x by a small amount, say bringing it down to x = 25.0f or something like that. As y gets closer to zero it should more and more drastically reduce the value of x. The only way I can think of doing this is with a logarithmic curve. I know what I want it to do, but I cannot for the life of me figure out how to implement this in C++. What would this algorithm look like in C++?
It sounds like you want this:
new_x = x * ln((e - 1) * y + 1)
I'm assuming you have the natural log function ln and the constant e. The number multiplied by x is a logarithmic function of y which is 0 when y = 0 and 1 when y = 1.
Here's the logic behind that function (this is basically a math problem, not a programming problem). You want something that looks like the ln function, rising steeply at first and then leveling off. But you want it to start at (0, 0) and then pass through (1, 1), and ln starts at (1, 0) and passes through (e, 1). That suggests that before you do the ln, you do a simple linear shift that takes 0 to 1 and 1 to e: ((e - 1) * y + 1.
We can try with the following assumption: we need a function f(y) so that f(0)=0 and f(1)=1 which follows some logarithmic curve, may be something like f(y)=Alog(B+Cy), with A, B and C constants to be determined.
f(0)=0, so B=1
f(1)=1, so A=1/log(1+C)
So now, just need to find a C value so that f(0.8) is roughly equal to 25/28. A few experiment shows that C=4 is rather close. You can find closer if you want.
So one possibility would be: f(y) = log(1.0 + 4.0*y) / log(5.0)

Multiplying two lists in prolog

I am currently working with prolog and want to multiply two lists together but in a certian way. For example:
[1,2,3] and [4,5,6] are my two lists.
I want to preform the following actions:
(1*4)+(2*5)+(3*6) = 32
Such that the first element of each list is multiplied to each other then added with the second elements multiplied together etc.
Is this possible to go in Prolog?
I know in other languages you can do a recursive function with takes the head of the list and the tail (the rest of the entries). This allows for a simple multiplication but I do not think that is possible in prolog?
Using built-ins:
mult(X, Y, Z) :- Z is X * Y.
sum_prod(A, B, SumProd) :-
maplist(mult, A, B, Prods),
sumlist(Prods, SumProd). % In GNU Prolog this is sum_list
Using simple recursion:
sum_prod([A|As], [B|Bs], SumProd) :-
sum_prod(As, Bs, SP),
SumProd is SP + A*B.
sum_prod([], [], 0).
Using tail recursion:
sum_prod(A, B, SumProd) :-
sum_prod(A, B, 0, SumProd).
sum_prod([A|As], [B|Bs], Acc, SumProd) :-
Acc1 is Acc + A*B,
sum_prod(As, Bs, Acc1, SumProd).
sum_prod([], [], Acc, Acc).
If all items of your lists are integers and your Prolog implementation offers clpfd, you can simply use the
clpfd built-in predicate scalar_product/4, like this:
?- scalar_product([1,2,3],[4,5,6],#=,Product).
Product = 32.
Edit:
You may also be interested in the related question "Prolog: Multiplying 2 lists with 1 of them not instantiated?", particularly in this answer.
as an alternative to 'hand coded' loops, using library(aggregate) and nth1/3:
sum_prod(A,B,S) :-
aggregate(sum(M), I^X^Y^(nth1(I,A,X), nth1(I,B,Y), M is X*Y), S).

Dot product Prolog/3 need Hint for the SUM

Good Day I am doing the problema of arithmetic in prolog and Yes its the dot Product I have Searched and found a mess of code that did not equal to what the book is asking me. Its a /3 so this is what i have so far but i need to sum the result of the product of both list. Any hint on what should be recommended to do?
dot([HD|TL],[HD2|TL2],Result):-
Mul is HD2 * HD,
dot(TL,TL2,Mul),
Result is Mul + Reuslt2.
dot([],[],0).
Your problems are that you're using Mul twice where you mean to use it once, and Reuslt2 doesn't exist anywhere. Probably what you mean is:
dot([], [], 0).
dot([H1|T1], [H2|T2], Result) :-
Prod is H1 * H2,
dot(T1, T2, Remaining),
Result is Prod + Remaining.
You use SWI-Prolog, so you can try :
:- use_module(library(lambda)).
my_dot(L1,L2,R) :-
foldl(\X^Y^Z^T^(T is X*Y + Z), L1,L2,0,R).

Prolog program won't compute variable answer?

This should be an easy fix, but I can't seem to tackle this, and it's getting frustrating. I've coded a program which computes or verifies that two lists are related because the elements of the second list are all incremented by one from the elements of the first list. This works when two lists are given, but not when it needs to compute a list.
Code is as follows:
inc([], []).
inc([X|XS],[Y|YS]) :-
Y =:= X+1,
inc(XS,YS).
ERROR: =:=/2: Arguments are not sufficiently instantiated
Any help would be greatly appreciated!
Your problem is essentially that =:=/2 is for testing rather than establishing bindings, though is/2 still doesn't really do what you want. For instance, while 2 is 1 + 1 is true, 2 is X+1 will not result in X being bound to 1, because is/2 expects there to be just one variable or value on the left and one expression on the right, and it does not behave "relationally" like the rest of Prolog. If you want arithmetic that behaves this way, you should check out clpfd; looking at the complexity it adds is a good explanation for why things are the way they are.
Fortunately, you don't need all of arithmetic to solve your problem. The succ/2 builtin will do exactly what you need, and bonus, you get a one line solution:
inc(X, Y) :- maplist(succ, X, Y).
In use:
?- inc([1,2,3], [2,3,4]).
true.
?- inc([1,2,3], X).
X = [2, 3, 4].
?- inc(X, [1,2,3]).
X = [0, 1, 2].
Your code also works fine if you use succ/2 instead of =:=/2:
inc([], []).
inc([X|XS],[Y|YS]) :-
succ(X, Y),
inc(XS,YS).
This must be the "easy fix" you suspected. :)
I'm not sure what #mbratch is referring to about there being "too many variables" for one predicate. I suspect this is a misunderstanding of Prolog on their part, perhaps a holdover from other languages where a function can return one value or something. There is no technical limitation here; predicates can take as many ground or nonground arguments and bind as many of them as you want; the limiting factor is your creativity.
Similarly, I don't think "asymmetry" is a meaningful concept here. It's quite normal to define predicates that have just a single instantiation pattern, but it's also normal and preferable to make predicates that are flexible about instantiation—you can't know ahead of time what uses may be needed in the future. You might think to yourself that an instantiation pattern that destroys information might preclude the inverse instantiation pattern, but in practice, frequently you can turn it into a generator instead.
To give a trite example, append/3's name seems to imply this pattern:
?- append([1,2], [3,4], X).
X = [1,2,3,4]
That's a perfectly good use, but so is:
?- append(X, Y, [1,2,3,4]).
This is a non-deterministic instantiation pattern and will produce five solutions:
X = [], Y = [1,2,3,4]
X = [1], Y = [2,3,4]
X = [1,2], Y = [3,4]
X = [1,2,3], Y = [4]
X = [1,2,3,4], Y = []
This seems to stand in contradiction to some of #mbratch's ideas, but there's no explicit testing for ground/nonground in the usual definition of append/3, because it isn't necessary, and likewise with the second calling pattern you get two "return values" from one input. SWI source:
append([], L, L).
append([H|T], L, [H|R]) :-
append(T, L, R).
Edit: Negative numbers. I forgot that succ/2 is defined only on positive integers. We can apply #mbratch's technique and still get a tidy solution with the desired properties:
isucc(X, Y) :- var(X), X is Y-1.
isucc(X, Y) :- Y is X+1.
inc(X, Y) :- maplist(isucc, X, Y).
In action:
?- inc(X, [-1,2]).
X = [-2, 1] ;
false.
Edit: Using clp(fd) (via #mat):
fdsucc(X,Y) :- Y #= X + 1.
inc(X, Y) :- maplist(fdsucc, X, Y).
This generates even for the most general query:
?- inc(X, Y).
X = Y, Y = [] ;
X = [_G467],
Y = [_G476],
_G467+1#=_G476 ;
X = [_G610, _G613],
Y = [_G622, _G625],
_G610+1#=_G622,
_G613+1#=_G625 ;
X = [_G753, _G756, _G759],
Y = [_G768, _G771, _G774],
_G753+1#=_G768,
_G756+1#=_G771,
_G759+1#=_G774
...
The utility of this is questionable, but presumably since you're using clp(fd) you'll eventually impose other constraints and get something useful.
inc([],[]).
inc([X|XS],[Y|YS]) :-
nonvar(X),
Z is X + 1,
Y = Z,
inc(XS,YS), !.
inc([X|XS],[Y|YS]) :-
nonvar(Y),
Z is Y - 1,
X = Z,
inc(XS,YS), !.
Here we need to get a real computation for the addition, then attempt instantiation with =. The predicate had to be split to deal with the case where X was not instantiated, versus when Y wasn't. The ! at the end of each is to prevent it from trying for more solutions after it has found one through one of the two similar paths.