I don't know how exactly is implemented the original algorithm of Akinator, but we've solved a similar task using database. You may read about its implementation at https://github.com/nesterovsky-bros/KB.
Edited:
The suggested solution is an algorithm that takes as input a list of questions with answers, and offers the next question(s). That question and an answer to it are added to the set of questions and answers. When there is no more question, then a list of entities is determined by set of questions with answers on these questions.
In essence, SQL solution works like this:
- Define tables:
Table "Entity" defines objects:
create table Data.Entity
(
EntityID int not null primary key, -- Object key.
Name nvarchar(256) -- Object name
);
Table "PredicateType" defines question types:
create table Data.PredicateType
(
PredicateID int not null primary key, -- question id
Name nvarchar(128) not null unique -- question name.
);
Table "Predicate" stores entities for which predicate is true:
create table Data.Predicate
(
PredicateID int not null,
EntityID int not null,
constraint PK_Predicate primary key clustered(PredicateID, EntityID),
constraint IX_Predicate_Entity unique(EntityID, PredicateID)
);
- The solution is build a select that for a set of question and answers on input, offers a new question on output.
For example, if we have P(i) - questions, A(i) - answers, i = 1..5, and
A(1) = A(3) = 1, A(2) = A(4) = A(5) = 0 then the select will look like this:
with P1 as -- P(1), A(1) = 1
(
select EntityID from Data.Predicate where PredicateID = @p1
),
P2 as -- P(2), A(2) = 0
(
select EntityID from Data.Predicate where PredicateID = @p2
),
P3 as -- P(3), A(3) = 1
(
select EntityID from Data.Predicate where PredicateID = @p3
),
P4 as -- P(4), A(4) = 0
(
select EntityID from Data.Predicate where PredicateID = @p4
),
P5 as -- P(5), A(5) = 0
(
select EntityID from Data.Predicate where PredicateID = @p5
),
M as
(
select EntityID from Data.Entity
intersect
select EntityID from P1
intersect
select EntityID from P3
except
select EntityID from P2
except
select EntityID from P4
except
select EntityID from P5
),
P as
(
select
P.PredicateID,
count(*) EntityCount,
(select count(*) from M) TotalCount
from
Data.Predicate P
inner join
M
on
P.EntityID = M.EntityID
where
P.PredicateID not in (@p1, @p2, @p3, @p4, @p5)
group by
P.PredicateID
)
select top(5) PredicateID from P order by abs(TotalCount - EntityCount * 2);
Where returned result-set is best the next best predicates.