FUZZY - The job news filter: Rank - Salary - Citizen problem


(0 comments)

Omarine.org welcomes the new year 2019 with an information filter that can be applied to job networks, using fuzzy inference. When there is a job news from the employer, that information is provided to the candidate who has logged in to the network. But is the job news suitable for candidates? How to choose only the jobs that job hunters can apply feasibly?


This
article will give you the key to unlock the secret door and solve
this challenge easily using FUZZY programming language.


Where
is FUZZY programming language?


It
is right in front of you, in FUZZY tab. I offer you all the source
code of the interpreter program. You can freely use, modify and
distribute. But first, let's use the program in this article.


Personnel
problem onboard


We
use shipboard personnel to illustrate because seafarers are one of
the few areas spread globally with multi-national crew and many
standardized ranks.


The
$ 10,000 is an attractive salary for a Korean captain, but it is
impossible for a Vietnamese captain because the ship owner usually
does not hire Vietnamese crew at that price. So a job news with too
high a salary will have little value for a Vietnamese candidate, it
will be scored low as we will see below. On the contrary, that news
reached a high score for Japanese candidates.


Similarly,
for low ratings such as sailors or oilers, the job bulletin with the
highest salary should be the lowest score so that it is excluded from
the interest of these candidates.


With
the language FUZZY is an artificial intelligence language we can set
up natural rules like when we normally state about the way we
calculate scores.


Implementing


We create a source file called job.fuz (or job.c), first defining 4 fuzzy terms:


term SALARY(10)
{
fuzzy discrete bottom(1/1000, 0.33/2000, 0/3000, 0/4000, 0/5000, 0/6000, 0/7000, 0/8000, 0/9000, 0/10000);
fuzzy discrete low(0/1000, 0.33/2000, 1/3000, 0.33/4000, 0/5000, 0/6000, 0/7000, 0/8000, 0/9000, 0/10000);
fuzzy discrete medium(0/1000, 0/2000, 0/3000, 0.33/4000, 1/5000, 1/6000, 0.33/7000, 0/8000, 0/9000, 0/10000);
fuzzy discrete high(0/1000, 0/2000, 0/3000, 0/4000, 0/5000, 0/6000, 0.33/7000, 1/8000, 0.33/9000, 0/10000);
fuzzy discrete top(0/1000, 0/2000, 0/3000, 0/4000, 0/5000, 0/6000, 0/7000, 0/8000, 0.33/9000, 1/10000);
};

term RANK(5)
{
fuzzy discrete low(1/1, 0.33/2, 0/3, 0/4, 0/5);
fuzzy discrete medium(0/1, 0.33/2, 1/3, 0.33/4, 0/5);
fuzzy discrete high(0/1, 0/2, 0/3, 0.33/4, 1/5);
};

term CITIZEN(5)
{
fuzzy discrete bottom(1/1, 0.33/2, 0/3, 0/4, 0/5);
fuzzy discrete low(0.33/1, 1/2, 0.33/3, 0/4, 0/5);
fuzzy discrete medium(0/1, 0.33/2, 1/3, 0.33/4, 0/5);
fuzzy discrete high(0/1, 0/2, 0.33/3, 1/4, 0.33/5);
fuzzy discrete top(0/1, 0/2, 0/3, 0.33/4, 1/5);
};

term SCORE(10)
{
fuzzy discrete bottom(1/1, 0.33/2, 0/3, 0/4, 0/5, 0/6, 0/7, 0/8, 0/9, 0/10);
fuzzy discrete low(0/1, 0.33/2, 1/3, 0.33/4, 0/5, 0/6, 0/7, 0/8, 0/9, 0/10);
fuzzy discrete medium(0/1, 0/2, 0/3, 0.33/4, 1/5, 1/6, 0.33/7, 0/8, 0/9, 0/10);
fuzzy discrete high(0/1, 0/2, 0/3, 0/4, 0/5, 0/6, 0.33/7, 1/8, 0.33/9, 0/10);
fuzzy discrete top(0/1, 0/2, 0/3, 0/4, 0/5, 0/6, 0/7, 0/8, 0.33/9, 1/10);
};

Add two functions to convert from string to number as follows:


int Rank2Num(String rank) {

switch (rank) {

case "O/S":
case "A/B":
case "Oiler":
return 1;
case "3/O":
case "4/E":
case "Bosun":
case "No.1 Oiler":
case "Chief Cook":
case "Electrician":
return 2;
case "2/O":
case "3/E":
return 3;
case "Chief Officer":
case "2/E":
return 4;
case "Chief Engineer":
case "Master":
return 5;
default:
return -1;
}
}

int Citizen2Num(String citizen) {

switch (citizen) {

case "American":
case "British":
case "Japanese":
return 5;
case "Korean":
return 4;
case "Chinese":
case "Philippines":
return 3;
case "Thailand":
case "Vietnamese":
case "Indonesian":
return 2;
case "Cambodian":
return 1;
default:
return -1;
}
}

Note that citizen classification is just an illustration. I do not mean the hierarchy in fact.


The main part of the program is the JOB structure, with a set of rules:


struct JOB
{
SALARY salary;
RANK rank;
CITIZEN citizen;
SCORE score;

bool Read(int salaryVal, String rankVal, String citizenVal)
{
int i;

switch ( salaryVal )
{
case 1000:
put salary := (0.5/salaryVal, 0/2000, 0/3000, 0/4000, 0/5000, 0/6000, 0/7000, 0/8000, 0/9000, 0/10000);
break;
case 2000:
put salary := (0/1000, 0.5/salaryVal, 0/3000, 0/4000, 0/5000, 0/6000, 0/7000, 0/8000, 0/9000, 0/10000);
break;
case 3000:
put salary := (0/1000, 0/2000, 0.5/salaryVal, 0/4000, 0/5000, 0/6000, 0/7000, 0/8000, 0/9000, 0/10000);
break;
case 4000:
put salary := (0/1000, 0/2000, 0/3000, 0.5/salaryVal, 0/5000, 0/6000, 0/7000, 0/8000, 0/9000, 0/10000);
break;
case 5000:
put salary := (0/1000, 0/2000, 0/3000, 0/4000, 0.5/salaryVal, 0/6000, 0/7000, 0/8000, 0/9000, 0/10000);
break;
case 6000:
put salary := (0/1000, 0/2000, 0/3000, 0/4000, 0/5000, 0.5/salaryVal, 0/7000, 0/8000, 0/9000, 0/10000);
break;
case 7000:
put salary := (0/1000, 0/2000, 0/3000, 0/4000, 0/5000, 0/6000, 0.5/salaryVal, 0/8000, 0/9000, 0/10000);
break;
case 8000:
put salary := (0/1000, 0/2000, 0/3000, 0/4000, 0/5000, 0/6000, 0/7000, 0.5/salaryVal, 0/9000, 0/10000);
break;
case 9000:
put salary := (0/1000, 0/2000, 0/3000, 0/4000, 0/5000, 0/6000, 0/7000, 0/8000, 0.5/salaryVal, 0/10000);
break;
case 10000:
put salary := (0/1000, 0/2000, 0/3000, 0/4000, 0/5000, 0/6000, 0/7000, 0/8000, 0/9000, 0.5/salaryVal);
break;
default:
return false;
}

switch ( i = Rank2Num(rankVal) )
{
case 1:
put rank := (0.5/i, 0/2, 0/3, 0/4, 0/5);
break;
case 2:
put rank := (0/1, 0.5/i, 0/3, 0/4, 0/5);
break;
case 3:
put rank := (0/1, 0/2, 0.5/i, 0/4, 0/5);
break;
case 4:
put rank := (0/1, 0/2, 0/3, 0.5/i, 0/5);
break;
case 5:
put rank := (0/1, 0/2, 0/3, 0/4, 0.5/i);
break;
default:
return false;
}

switch ( i = Citizen2Num(citizenVal) )
{
case 1:
put citizen := (0.5/i, 0/2, 0/3, 0/4, 0/5);
break;
case 2:
put citizen := (0/1, 0.5/i, 0/3, 0/4, 0/5);
break;
case 3:
put citizen := (0/1, 0/2, 0.5/i, 0/4, 0/5);
break;
case 4:
put citizen := (0/1, 0/2, 0/3, 0.5/i, 0/5);
break;
case 5:
put citizen := (0/1, 0/2, 0/3, 0/4, 0.5/i);
break;
default:
return false;
}

return true;
}

void SetRules()
{
ZeroFuzz(score);

rule rank.high AND ( citizen.top AND salary.top OR citizen.high AND VERY salary.high OR citizen.medium AND LITTLE salary.high OR ( citizen.low OR citizen.bottom ) AND LITTLE salary.low ) => score.top;

rule rank.high AND ( citizen.top AND salary.high OR citizen.high AND ( salary.top OR salary.medium ) OR citizen.medium AND salary.high OR ( citizen.low OR citizen.bottom ) AND salary.medium ) => score.high;

rule rank.high AND ( citizen.top AND salary.medium OR citizen.high AND REALLY salary.medium OR citizen.medium AND ( salary.top OR salary.medium) OR ( citizen.low OR citizen.bottom ) AND salary.high ) => score.medium;

rule rank.high AND ( ( citizen.top OR citizen.high OR citizen.medium ) AND salary.low ) => score.low;

rule rank.medium AND ( citizen.top AND salary.high OR citizen.high AND LITTLE salary.high OR citizen.medium AND salary.medium OR citizen.low AND VERY salary.low OR citizen.bottom AND LITTLE salary.bottom ) => score.top;

rule rank.medium AND ( ( citizen.top OR citizen.high ) AND ( salary.top OR salary.medium ) OR citizen.medium AND salary.high OR ( citizen.low OR citizen.bottom ) AND ( salary.medium OR salary.bottom ) ) => score.high;

rule rank.medium AND ( ( citizen.top OR citizen.high ) AND LITTLE salary.low OR citizen.medium AND salary.top OR ( citizen.low OR citizen.bottom ) AND LITTLE salary.high ) => score.medium;

rule rank.medium AND ( ( citizen.top OR citizen.high OR citizen.medium ) AND VERY salary.low OR ( citizen.low OR citizen.bottom ) AND salary.high ) => score.low;


rule rank.low AND ( ( citizen.top OR citizen.high ) AND salary.low OR (citizen.medium OR citizen.low OR citizen.bottom ) AND salary.bottom ) => score.top;

rule rank.low AND ( ( citizen.top OR citizen.high ) AND salary.medium OR ( citizen.medium OR citizen.low OR citizen.bottom ) AND LITTLE salary.bottom ) => score.high;

rule rank.low AND ( citizen.top OR citizen.high ) AND LITTLE salary.high => score.medium;

rule rank.low AND ( citizen.top OR citizen.high ) AND LITTLE salary.top => score.low;

rule rank.low AND ( citizen.medium OR citizen.low OR citizen.bottom ) AND NOT salary.bottom => score.low;

rule rank.high AND salary.bottom => score.bottom;
rule ( citizen.top OR citizen.high ) AND LITTLE salary.bottom => score.bottom;
rule ( citizen.low OR citizen.bottom ) AND salary.top => score.bottom;

}

void Process(int salaryVal, String rankVal, String citizenVal)
{
if ( Read(salaryVal, rankVal, citizenVal) )
{
SetRules();
Display(salaryVal, rankVal, citizenVal);
}
else
print "Invalid input!\n";
}

void Display(int salaryVal, String rankVal, String citizenVal)
{
Number val = Defuzz(score);
String valStr = round ( val );
String salaryStr = salaryVal, msg = "SALARY: " + salaryStr;
msg += ", RANK: " + rankVal;
msg += ", CITIZEN: " + citizenVal;
msg += ", SCORE: " + valStr;
print msg;
}
};

There are 16 rules. They are easy to understand so I only explain the last 3 rules:


rule rank.high AND salary.bottom  => score.bottom;


In above rule, for candidates with high rank while the job bulletin salary is the lowest, regardless of citizenship rankings, the score for the news is the lowest.


rule ( citizen.top OR citizen.high ) AND LITTLE salary.bottom  => score.bottom;



If the candidate is a high or
highest citizen, and the salary of news is close to the bottom, the
bulletin will get the lowest score for all positions.


On the contrary, in the final rule


rule ( citizen.low OR citizen.bottom ) AND salary.top => score.bottom;



if the candidate is a low or
lowest citizen and the news salary is the highest, the job news will
also receive the lowest score for all positions.


The program is run with the following code lines:


JOB job;
job.Process(3000, "Master", "Japanese");
job.Process(3000, "Master", "Korean");
job.Process(3000, "Master", "Philippines");
job.Process(3000, "Master", "Vietnamese");
job.Process(3000, "Master", "Cambodian");

print "";

job.Process(3000, "A/B", "Japanese");
job.Process(3000, "A/B", "Korean");
job.Process(3000, "A/B", "Philippines");
job.Process(3000, "A/B", "Vietnamese");
job.Process(3000, "A/B", "Cambodian");

print "";

job.Process(10000, "Master", "Japanese");
job.Process(10000, "Master", "Korean");
job.Process(10000, "Master", "Philippines");
job.Process(10000, "Master", "Vietnamese");
job.Process(10000, "Master", "Cambodian");

print "";

job.Process(1000, "2/O", "Japanese");
job.Process(1000, "2/O", "Korean");
job.Process(1000, "2/O", "Philippines");
job.Process(1000, "2/O", "Vietnamese");
job.Process(1000, "2/O", "Cambodian");


Analysing the output


The output of the program is as follows:




There are 4 output groups,
each consisting of 5 information lines.


The
first group is for the candidates who are the captain. The employer
offers a salary of $ 3,000. $ 3,000 is too low a salary for Japanese
or Korean captains. Therefore this newsletter is of low value to
them, only score of 3. But the value of the newsletter gradually
increases for those with lower rankings of citizen: 4 for Philippines
captains, 6 for Vietnamese captains, and highest for Cambodian
captains, 9.


The
second group is the opposite. The employer need an A/B position, also
with a salary of $ 3,000. That salary is suitable for Japanese
sailors with a score of 9, while it is too high for Vietnamese
sailors, the newsletter does not fit them and the score is only 3.


Now you can analyze the rest yourself, so there is no need to explain further. This is when you start your own FUZZY program.




O/S: Ordinary seaman


A/B:
Able seaman


3/O:
Third Officer


4/E:
Fourth Engineer


2/O:
Second Officer


3/E:
Third Engineer


2/E:
Second Engineer


Currently unrated

Comments

There are currently no comments

New Comment

required

required (not published)

optional

required