Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
8 views14 pages

Unity University: Department of Computer Science

Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
You are on page 1/ 14

UNITY UNIVERSITY

Department of Computer Science


Introduction to Artificial Intelligence
Name ID NO

Nahom Lulseged
03396/13

Part I: Describing the Concepts with Use Case Examples

1. Adversarial Search Strategies

Adversarial search strategies are used in competitive environments where agents with opposing goals
compete, such as in games like chess, checkers, or tic-tac-toe. Two common algorithms used in
adversarial search are the Minimax algorithm and the Alpha-Beta Pruning algorithm.
Minimax Algorithm

The Minimax algorithm is a recursive search algorithm used to choose an optimal move for a player
assuming that the opponent is also playing optimally. It works by simulating all possible moves and
counter-moves, and then choosing the move that maximizes the player's minimum gain (hence the name
"minimax").

Use Case Example:

Chess: In a chess game, the Minimax algorithm can be used to decide the next move. The algorithm
simulates all possible moves (for both players) from the current position to a certain depth, and
evaluates the board position at the leaves of the search tree. The move that maximizes the minimum
possible gain (considering the opponent's best possible responses) is chosen.

Alpha-Beta Pruning Algorithm

The Alpha-Beta Pruning algorithm is an optimization of the Minimax algorithm. It reduces the number of
nodes that need to be evaluated in the search tree by pruning branches that cannot possibly influence
the final decision. This is done by maintaining two values, alpha and beta, which represent the minimum
score that the maximizing player is assured of and the maximum score that the minimizing player is
assured of, respectively.

Use Case Example:

Tic-Tac-Toe: In a tic-tac-toe game, Alpha-Beta pruning can be applied to the Minimax algorithm to
efficiently decide the next move. By pruning branches that do not need to be explored (because they
cannot affect the outcome), the algorithm runs faster and can search deeper within the same time
constraints.

2. Constraint Satisfaction Problem Search

A Constraint Satisfaction Problem (CSP) involves finding a solution to a problem that satisfies a number
of constraints. CSPs can be solved using search algorithms that systematically explore the possible
assignments of values to variables while checking against constraints.
Use Case Example:

Sudoku: In a Sudoku puzzle, the goal is to fill a 9x9 grid with digits so that each column, each row, and
each of the nine 3x3 subgrids contain all the digits from 1 to 9. This can be modeled as a CSP where each
cell in the grid is a variable, the values are the digits from 1 to 9, and the constraints are the rules of
Sudoku. A CSP solver would explore different assignments of digits to cells while ensuring the constraints
are not violated.

3. Inference Systems: Forward and Backward Chaining

An inference system is used in artificial intelligence to deduce new information from known facts using
logical rules. There are two main types of inference: forward chaining and backward chaining.

Forward Chaining

Forward chaining is a data-driven inference technique that starts with known facts and applies inference
rules to extract more data until a goal is reached.

Example:

Expert Systems: Consider an expert system for diagnosing diseases. The system starts with known
symptoms (facts) and applies medical rules to deduce possible diseases (conclusions). If the system
knows that a patient has a fever and a cough, it can infer (using forward chaining) that the patient might
have the flu.

Backward Chaining

Backward chaining is a goal-driven inference technique that starts with a goal and works backward to
determine which facts must be true to achieve that goal.

Example:

Problem Solving: Consider a troubleshooting system for fixing a car. The system starts with the goal (e.g.,
"The car will not start") and works backward to identify possible causes (e.g., "The battery is dead", "The
fuel tank is empty"). It then checks these hypotheses against known facts to find the cause of the
problem.
Exercise 1.1

(a) Which of the following are valid Prolog atoms?

f: Valid atom.

loves(john, mary): Not an atom, this is a compound term.

Mary: Not an atom, this is a variable (starts with an uppercase letter).

_c1: Not an atom, this is a variable (starts with an underscore).

'Hello': Valid atom (atoms can be enclosed in single quotes).

this_is_it: Valid atom.

(b) Which of the following are valid names for Prolog variables?

a: Not a valid variable (starts with a lowercase letter).

A: Valid variable (starts with an uppercase letter).

Paul: Valid variable (starts with an uppercase letter).

'Hello': Not a valid variable (enclosed in quotes, considered an atom).

a_123: Not a valid variable (starts with a lowercase letter).

_: Valid anonymous variable.

_abc: Valid variable (starts with an underscore).

x2: Not a valid variable (starts with a lowercase letter).

(c) What would a Prolog interpreter reply given the following query?

?- f(a, b) = f(X, Y).

Prolog would unify the terms and return:

X = a,

Y = b.
(d) Would the following query succeed?

?- loves(mary, john) = loves(John, Mary).

No, the query would not succeed because mary and Mary are different terms in Prolog (one is an atom,
and the other is a variable).

(e) Assume a program consisting only of the fact

a(B, B).

has been consulted by Prolog. How will the system react to the following query?

?- a(1, X), a(X, Y), a(Y, Z), a(Z, 100).

Prolog will unify the terms step by step:

a(1, X) succeeds with X = 1.

a(X, Y) with X = 1 succeeds with Y = 1.

a(Y, Z) with Y = 1 succeeds with Z = 1.

a(Z, 100) with Z = 1 fails because 1 is not equal to 100.

So, the query will fail.

Exercise 1.2

(a)

?- myFunctor(1, 2) = X, X = myFunctor(Y, Y).

First, X is unified with myFunctor(1, 2). Then, X is unified with myFunctor(Y, Y). This unification will fail
because myFunctor(1, 2) cannot match myFunctor(Y, Y) (1 = 2).

(b)

?- f(a, _, c, d) = f(a, X, Y, _).


Prolog will unify the terms:

a matches a.

_ matches X.

c matches Y.

d matches _.

Thus, the result will be:

X = c,

Y = d.

(c)

?- write('One '), X = write('Two ').

Prolog will execute write('One ') first, printing "One ". Then X is unified with write('Two '), but this does
not immediately execute the write('Two '). To execute it, you need another goal that uses X, for example:

?- write('One '), X = write('Two '), X.

This would print "One Two ".

Exercise 1.3

Let's draw the family tree and define the new predicates.

Family Tree:

Bob Juliet Peter

| | |

--- --- ---


| | | | | |

Lisa Paul Mary Lisa Harry

| |

Dick Sandra

Predicates:

father(X, Y) :-

parent(X, Y),

male(X).

sister(X, Y) :-

parent(Z, X),

parent(Z, Y),

female(X),

X \= Y.

grandmother(X, Y) :-

parent(X, Z),

parent(Z, Y),

female(X).

cousin(X, Y) :-

parent(A, X),

parent(B, Y),

parent(C, A),

parent(C, B),
X \= Y.

Exercise 1.4

Original rules:

is_bigger(elephant, horse).

is_bigger(horse, donkey).

is_bigger(A, B) :- is_bigger(A, X), is_bigger(X, B).

is_bigger(A, B) :- is_bigger(A, X), is_bigger(X, B).

Swapping subgoals in the second rule:

is_bigger(A, B) :- is_bigger(X, B), is_bigger(A, X).

Query:

?- is_bigger(A, donkey).

The query will first look for A such that is_bigger(A, X) and is_bigger(X, donkey) both hold. This may
cause an infinite loop or repeated solutions, because Prolog will keep looking for larger elements
without terminating.

Exercise 1.5

Reviewing and practicing the syntax, matching, and goal execution in Prolog after gaining some
experience will help solidify the concepts and improve understanding.

Exercise 2.1: analyse_list/1

The predicate analyse_list/1 should analyze the given list and print its head and tail, or report if the list is
empty or not a list.

analyse_list([]) :-

write('This is an empty list.'),

nl.
analyse_list([Head|Tail]) :-

write('This is the head of your list: '),

write(Head),

nl,

write('This is the tail of your list: '),

write(Tail),

nl.

analyse_list(_) :-

fail.

Exercise 2.2: membership/2

The predicate membership/2 checks if an element is a member of a list.

membership(X, [X|_]).

membership(X, [_|Tail]) :-

membership(X, Tail).

Exercise 2.3: remove_duplicates/2

The predicate remove_duplicates/2 removes duplicate elements from a list.

remove_duplicates([], []).

remove_duplicates([Head|Tail], Result) :-

member(Head, Tail),
remove_duplicates(Tail, Result).

remove_duplicates([Head|Tail], [Head|Result]) :-

\+ member(Head, Tail),

remove_duplicates(Tail, Result).

Exercise 2.4: reverse_list/2

The predicate reverse_list/2 reverses a list.

reverse_list(List, Reversed) :-

reverse_list_helper(List, [], Reversed).

reverse_list_helper([], Accumulator, Accumulator).

reverse_list_helper([Head|Tail], Accumulator, Reversed) :-

reverse_list_helper(Tail, [Head|Accumulator], Reversed).

Exercise 2.5: whoami/1

The predicate whoami/1 succeeds if the list contains an even number of elements.

whoami([]).

whoami([_, _ | Rest]) :-

whoami(Rest).

A goal of the form whoami(X) will succeed if X is a list with an even number of elements.

Exercise 2.6: last1/2 and last2/2

The predicates last1/2 and last2/2 return the last element of a list.
(a) Using recursion:

last1([X], X).

last1([_|Tail], X) :-

last1(Tail, X).

(b) Using append/3:

last2(List, X) :-

append(_, [X], List).

Exercise 2.7: replace/4

The predicate replace/4 replaces all occurrences of an element in a list with another element.

replace([], _, _, []).

replace([Old|Tail], Old, New, [New|NewTail]) :-

replace(Tail, Old, New, NewTail).

replace([Head|Tail], Old, New, [Head|NewTail]) :-

Head \= Old,

replace(Tail, Old, New, NewTail).

Exercise 2.8: power/2

The predicate power/2 computes the power set of a list.

power([], [[]]).

power([Head|Tail], PowerSet) :-
power(Tail, TailPowerSet),

add_head_to_subsets(Head, TailPowerSet, HeadPowerSet),

append(TailPowerSet, HeadPowerSet, PowerSet).

add_head_to_subsets(_, [], []).

add_head_to_subsets(Head, [Subset|Tail], [[Head|Subset]|NewTail]) :-

add_head_to_subsets(Head, Tail, NewTail).

Exercise 2.9: longer/2

The predicate longer/2 succeeds if the second list is longer than the first.

longer([], [_|_]).

longer([_|Tail1], [_|Tail2]) :-

longer(Tail1, Tail2).

Exercise 2.10: Unary Numbers Operations

(a) successor/2

The successor of a number is the number we obtain if we add one to it. In unary notation, adding one
means appending one more x to the list.

successor(Unary, [x|Unary]).

Examples:

?- successor([x, x, x], Result).

Result = [x, x, x, x].


?- successor([], Result).

Result = [x].

(b) plus/3

The predicate plus/3 computes the sum of two given unary numbers. In unary notation, the sum of two
numbers is simply their concatenation.

plus([], Y, Y).

plus([x|X], Y, [x|Z]) :-

plus(X, Y, Z).

Examples:

?- plus([x, x], [x, x, x, x], Result).

Result = [x, x, x, x, x, x].

(c) times/3

The predicate times/3 multiplies two given unary numbers. In unary notation, the product of two
numbers can be obtained by repeatedly concatenating the first number to itself, the number of times
equal to the second number.

times([], _, []).

times([x|X], Y, Result) :-

times(X, Y, PartialResult),

plus(Y, PartialResult, Result).

Examples:

?- times([x, x], [x, x, x, x], Result).


Result = [x, x, x, x, x, x, x, x].

?- times([x, x, x], [x, x, x, x, x], Result).

Result = [x, x, x, x, x, x, x, x, x, x, x, x, x, x, x].

You might also like