So I would like to make this code to execute in linear time and I'm not sure what's making it take long than that so all help would be appriciated.
Sorry for not explaining earlier what the program does, medellangd is pretty much calculating the average length of the words sent in within the string.
medellangd([],0) :- !.
medellangd([Head1|[]], 0) :-
\+ check(Head1), !.
medellangd([Head1|[]], 1) :- !.
medellangd(Text, AvgLen) :-
medel(Text, X, Y),
AvgLen is X/Y.
medel([], 0, 0) :- !.
medel([Head1,Head2|Tail], Letters, Words) :-
check(Head1),
check(Head2), !,
medel([Head2|Tail], Letters1, Words),
Letters is Letters1 + 1.
medel([Head1|Tail], Letters, Words) :-
check(Head1),
medel(Tail, Letters1, Words1), !,
Letters is Letters1 + 1,
Words is Words1 + 1.
medel([_|T], Avg, Words) :-
medel(T, Avg, Words).
% We check that the input is a letter.
check(X) :-
(X >= 65, X =< 90);
(X >= 97, X =< 122).
Alright, seems like it was my method of checking it that was the problem, by using char-type on head instead of my check it solved my problem and I got i to run in linear time.
Related
I have written code including count function that calculates how many times elements occurs in list, then I have function that go through all array and deletes the elements that occur odd number of times. But I got problem, an warning telling me "Arithmetic: `element_count' is not a function".
What is wrong and how to fix it?
countX([],_,0).
countX([X|T], X, P1) :- countX(T, X, P), P1 is P+1.
countX([H|T], X, P) :- H=\=X, countX(T, X, P).
goal([], []).
goal(Start, Result):-
remove_odd_count_numbers(Start, Start, Result).
remove_odd_count_numbers(_, [], []).
remove_odd_count_numbers(Start, [A|List], Result):-
count(Start, A, count_element),
count_element mod 2 =:= 0,
remove_odd_count_numbers(Start, List, Result), !.
remove_odd_count_numbers(Start, [Head|Tail], [Head|TailResult]):-
remove_odd_count_numbers(Start, Tail, TailResult), !.
count(Start, A, count_element),
count_element mod 2 =:= 0,
Variable names cannot start with lowercase letters in Prolog. You need Count_element. (At least that; it may not be enough to make it work).
This question already has an answer here:
how do you print stars n amount of times on multiple lines using prolog
(1 answer)
Closed 3 years ago.
I need to print out stars on one line and then n-1 on the next until one star, and then back to n amount.
What about as follows?
line(N) :-
foreach(between(1, N, _), write('*')),
nl.
star(1) :-
line(1).
star(N) :-
line(N),
NM1 is N-1,
star(NM1),
line(N).
Calling star(N).
This should work:
%this is the main function you want to call
star(N) :-
starl(N),
starr(1,N).
% counts down from n to zero and
% writes according amount of stars
starl(0):- !.
starl(N):-
write_n_times("*", N),
Nm is N-1,
starl(Nm).
% counts up from X to Np and
% writes according amount of stars
starr(N, N):- write_n_times("*", N), !.
starr(X, Np):-
write_n_times("*", X),
Xp is X+1,
starr(Xp, Np).
% writes X for I times and makes then a new line
write_n_times(_,0) :- nl, !.
write_n_times(X,I) :-
write(X),
Im is I - 1,
write_n_times(X,Im).
I need to define a predicate in prolog which takes a list as input and sums the squares of numbers >= 5 and subtract sum of absolute value of numbers <=2.
This is what I have currently :-
pred([], 0).
pred([Head|Tail], Result) :-
gr85(Head),
pred(Tail, Total),
Result is Head*Head + Total.
pred([Head|Tail], Result) :-
leq2(Head),
pred(Tail, Total),
Result is Total - Head.
gr85(Number):-
Number >= 5.
leq2(Number):-
Number =< 2.
My question is how do I exclude anything between 2 and 5. If I input 3 in the list, it returns false.
Expected input
pred([3,6,2,-1], Result).
Expected output
Result= 33 (6*6 -2-1)
add a 'skip all' clause:
weird_sum([_|Tail], Result) :- weird_sum(Tail, Result).
I think you will gain some insight into Prolog working when you analyze where this clause should be added.
Maybe it is simpler if you keep it more simple, you can use accumulator to make it simpler. And also it is maybe less typing if you move the condiditional out, like this;
weird_sum(List, Sum) :-
weird_sum(List, 0, Sum).
weird_sum([], Acc, Sum) :- Sum is Acc.
weird_sum([X|Xs], Sum0, Sum) :-
once( weird_sum_helper(X, Sum0, Sum1) ),
weird_sum(Xs, Sum1, Sum).
weird_sum_helper(X, Acc, Acc + X*X) :- X >= 5.
weird_sum_helper(X, Acc, Acc - abs(X)) :- X =< 2.
weird_sum_helper(X, Acc, Acc) :- X < 5, X > 2.
But the actual answer to your original question is in the last line above, just say that when X is smaller than 5 AND X is larger than 2 than you don't do anything.
But I get different answer from 33 because when number is negative and you subtract you get positive not negative but I am not sure if this is what you mean.
I used once once just because you want to do this only once since the three conditions are excluding each other but somehow Prolog doesn't know that it should only take one and tries the others too.
I've got this Prolog program below that examines if a password fulfills certain rules (the password must contain a letter(a-z), a number(0-9),a double letter (aa,ll,ww etc.), must start with a letter (a, aa, c etc.), must be at least 6 characters long).
How can I expand it so that double letters would be counted as one letter? (For example, aa25b1 wouldn't be a correct password as it's only five characters long).
contains_letter(Password) :- wildcard_match('*[a-zA-Z]*', Password).
contains_number(Password) :- wildcard_match('*[0-9]*', Password).
contains_double_letter(Password) :-
(between(65, 90, Letter) ; between(97, 122, Letter)),
append([_, [Letter, Letter], _], Password),
!.
starts_with_letter(Password) :- wildcard_match('[a-zA-Z]*', Password).
long_enough(Password) :-
length(Password, Length),
Length >= 6.
check_everything(Password) :-
contains_letter(Password),
contains_number(Password),
contains_double_letter(Password),
starts_with_letter(Password),
long_enough(Password).
Preprocess the password with a predicate that squashes together equal characters, e.g.
uniq([], []).
uniq([X], [X]).
uniq([X,X|L], R) :-
!,
uniq([X|L], R).
uniq([X,Y|L], [X|R]) :-
uniq([Y|L], R).
(I named this after the Unix tool uniq; you might want to rename it without_adjacent_repetitions or something more to your taste.)
First and as I precised in your first question, note that you can combine that :
contains_letter(Password) :- wildcard_match('*[a-zA-Z]*', Password).
contains_number(Password) :- wildcard_match('*[0-9]*', Password).
starts_with_letter(Password) :- wildcard_match('[a-zA-Z]*', Password).
Into that :
letter_start_and_number(Password) :-
wildcard_match('[a-zA-Z]*[0-9]*', Password).
Now, you can handle double letters and length as follows :
length_double_letters([], Acc, yes, Acc).
length_double_letters([Char, Char|Password], Acc, _Contains, Length) :-
!,
NewAcc is Acc + 1,
length_double_letters(Password, NewAcc, yes, Length).
length_double_letters([_Char|Password], Acc, Contains, Length) :-
NewAcc is Acc + 1,
length_double_letters(Password, NewAcc, Contains, Length).
Using that predicate into this main predicate :
check_everything(Password) :-
letter_start_and_number(Password),
length_double_letters(Password, 0, no, Length),
Length >= 6.
PS : please take the time to accept the answers you find the most constructive and upvote the ones that helped you.
Write your own lengthWithDoubleLetters/2 rule taking a list of characters and returning its length counting double letters as one:
lengthWithDoubleLetters([],0).
lengthWithDoubleLetters([F,F|T],C) :-
lengthWithDoubleLetters(T,TC),
!,
C is TC + 1.
lengthWithDoubleLetters([H|T], C) :-
lengthWithDoubleLetters(T,TC),
C is TC + 1.
I have made two programs in Prolog for the nqueens puzzle using hill climbing and beam search algorithms.
Unfortunately I do not have the experience to check whether the programs are correct and I am in dead end.
I would appreciate if someone could help me out on that.
Unfortunately the program in hill climbing is incorrect. :(
The program in beam search is:
queens(N, Qs) :-
range(1, N, Ns),
queens(Ns, [], Qs).
range(N, N, [N]) :- !.
range(M, N, [M|Ns]) :-
M < N,
M1 is M+1,
range(M1, N, Ns).
queens([], Qs, Qs).
queens(UnplacedQs, SafeQs, Qs) :-
select(UnplacedQs, UnplacedQs1,Q),
not_attack(SafeQs, Q),
queens(UnplacedQs1, [Q|SafeQs], Qs).
not_attack(Xs, X) :-
not_attack(Xs, X, 1).
not_attack([], _, _) :- !.
not_attack([Y|Ys], X, N) :-
X =\= Y+N,
X =\= Y-N,
N1 is N+1,
not_attack(Ys, X, N1).
select([X|Xs], Xs, X).
select([Y|Ys], [Y|Zs], X) :- select(Ys, Zs, X).
I would like to mention this problem is a typical constraint satisfaction problem and can be efficiency solved using the CSP module of SWI-Prolog. Here is the full algorithm:
:- use_module(library(clpfd)).
queens(N, L) :-
N #> 0,
length(L, N),
L ins 1..N,
all_different(L),
applyConstraintOnDescDiag(L),
applyConstraintOnAscDiag(L),
label(L).
applyConstraintOnDescDiag([]) :- !.
applyConstraintOnDescDiag([H|T]) :-
insertConstraintOnDescDiag(H, T, 1),
applyConstraintOnDescDiag(T).
insertConstraintOnDescDiag(_, [], _) :- !.
insertConstraintOnDescDiag(X, [H|T], N) :-
H #\= X + N,
M is N + 1,
insertConstraintOnDescDiag(X, T, M).
applyConstraintOnAscDiag([]) :- !.
applyConstraintOnAscDiag([H|T]) :-
insertConstraintOnAscDiag(H, T, 1),
applyConstraintOnAscDiag(T).
insertConstraintOnAscDiag(_, [], _) :- !.
insertConstraintOnAscDiag(X, [H|T], N) :-
H #\= X - N,
M is N + 1,
insertConstraintOnAscDiag(X, T, M).
N is the number of queens or the size of the board (), and , where , being the position of the queen on the line .
Let's details each part of the algorithm above to understand what happens.
:- use_module(library(clpfd)).
It indicates to SWI-Prolog to load the module containing the predicates for constraint satisfaction problems.
queens(N, L) :-
N #> 0,
length(L, N),
L ins 1..N,
all_different(L),
applyConstraintOnDescDiag(L),
applyConstraintOnAscDiag(L),
label(L).
The queens predicate is the entry point of the algorithm and checks if the terms are properly formatted (number range, length of the list). It checks if the queens are on different lines as well.
applyConstraintOnDescDiag([]) :- !.
applyConstraintOnDescDiag([H|T]) :-
insertConstraintOnDescDiag(H, T, 1),
applyConstraintOnDescDiag(T).
insertConstraintOnDescDiag(_, [], _) :- !.
insertConstraintOnDescDiag(X, [H|T], N) :-
H #\= X + N,
M is N + 1,
insertConstraintOnDescDiag(X, T, M).
It checks if there is a queen on the descendant diagonal of the current queen that is iterated.
applyConstraintOnAscDiag([]) :- !.
applyConstraintOnAscDiag([H|T]) :-
insertConstraintOnAscDiag(H, T, 1),
applyConstraintOnAscDiag(T).
insertConstraintOnAscDiag(_, [], _) :- !.
insertConstraintOnAscDiag(X, [H|T], N) :-
H #\= X - N,
M is N + 1,
insertConstraintOnAscDiag(X, T, M).
Same as previous, but it checks if there is a queen on the ascendant diagonal.
Finally, the results can be found by calling the predicate queens/2, such as:
?- findall(X, queens(4, X), L).
L = [[2, 4, 1, 3], [3, 1, 4, 2]]
If I read your code correctly, the algorithm you're trying to implement is a simple depth-first search rather than beam search. That's ok, because it should be (I don't see how beam search will be effective for this problem and it can be hard to program).
I'm not going to debug this code for you, but I will give you a suggestion: build the chess board bottom-up with
queens(0, []).
queens(N, [Q|Qs]) :-
M is N-1,
queens(M, Qs),
between(1, N, Q),
safe(Q, Qs).
where safe(Q,Qs) is true iff none of Qs attack Q. safe/2 is then the conjunction of a simple memberchk/2 check (see SWI-Prolog manual) and your not_attack/2 predicate, which on first sight seems to be correct.
A quick check on Google has found a few candidates for you to compare with your code and find what to change.
My favoured solution for sheer clarity would be the second of the ones linked to above:
% This program finds a solution to the 8 queens problem. That is, the problem of placing 8
% queens on an 8x8 chessboard so that no two queens attack each other. The prototype
% board is passed in as a list with the rows instantiated from 1 to 8, and a corresponding
% variable for each column. The Prolog program instantiates those column variables as it
% finds the solution.
% Programmed by Ron Danielson, from an idea by Ivan Bratko.
% 2/17/00
queens([]). % when place queen in empty list, solution found
queens([ Row/Col | Rest]) :- % otherwise, for each row
queens(Rest), % place a queen in each higher numbered row
member(Col, [1,2,3,4,5,6,7,8]), % pick one of the possible column positions
safe( Row/Col, Rest). % and see if that is a safe position
% if not, fail back and try another column, until
% the columns are all tried, when fail back to
% previous row
safe(Anything, []). % the empty board is always safe
safe(Row/Col, [Row1/Col1 | Rest]) :- % see if attack the queen in next row down
Col =\= Col1, % same column?
Col1 - Col =\= Row1 - Row, % check diagonal
Col1 - Col =\= Row - Row1,
safe(Row/Col, Rest). % no attack on next row, try the rest of board
member(X, [X | Tail]). % member will pick successive column values
member(X, [Head | Tail]) :-
member(X, Tail).
board([1/C1, 2/C2, 3/C3, 4/C4, 5/C5, 6/C6, 7/C7, 8/C8]). % prototype board
The final link, however, solves it in three different ways so you can compare against three known solutions.