2022年10月1日
/*
g++ -g seat.cpp -o seat
*/
/*
「上司の帰宅が遅れると、社員の帰宅も遅れる」の仮説検証
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// 共通関数
double max(double a){
return a;
}
double max(double a, double b){
if (a > b)
return a;
else
return b;
};
double max(double a, double b, double c ){
return max(max(a,b), max(b,c));
};
double max(double a, double b, double c, double d ){
return max(max(a,b,c), d);
};
double min(double a){
return a;
}
double min(double a, double b){
if (b > a)
return a;
else
return b;
};
double min(double a, double b, double c ){
return min(min(a,b), min(b,c));
};
double min(double a, double b, double c, double d ){
return min(min(a,b,c), d);
};
// ファジィ表現
typedef enum scale {LESSLESS, LESS, ZERO, MORE, MOREMORE} SCALE;
// 前件部メンバーシップ関数(山3つ)クラス
class condition_MF3
{
private:
double center;
double width;
SCALE express;
public:
condition_MF3(double _center, double _witdth, SCALE _express){
center = _center;
width = _witdth;
express = _express;
// 使用できないファジィ表現を使った場合は止める
if ((express == LESSLESS) || (express == MOREMORE)){
printf("wrong expression used \n");
exit(0);
}
};
double func(double _x);
};
double condition_MF3::func(double _x)
{
// x,yは、メンバーシップ関数上の座標を示す
double x = _x;
double y = 0.0; // yの値は、必ず0以上1以下になる
if (express == LESS){
if (x <= center - width){
y = 1.0;
}
else if (x <= center){
y = - 1.0 / width * (x - center);
}
else{
y = 0.0;
}
}
else if (express == ZERO){
if (x <= center - width){
y = 0.0;
}
else if (x <= center){
y = 1.0 / width * (x - center) + 1.0;
}
else if (x <= center + width){
y = -1.0 / width * (x - center) + 1.0;
}
else{
y = 0.0;
}
}
else if (express == MORE){
if (x <= center){
y = 0.0;
}
else if (x <= center + width){
y = 1.0 / width * (x - center);
}
else{
y = 1.0;
}
}
else {
printf("wrong expression\n");
exit(1);
}
return y;
};
// 前件部メンバーシップ関数(山5つ)クラス
class condition_MF5
{
private:
double center;
double width;
SCALE express;
public:
condition_MF5(double _center, double _witdth, SCALE _express){
center = _center;
width = _witdth;
express = _express;
};
double func(double _x);
};
double condition_MF5::func(double _x)
{
// x,yは、メンバーシップ関数上の座標を示す
double x = _x;
double y = 0.0; // yの値は、必ず0以上1以下になる
if (express == LESSLESS){
if (x <= center - 2.0 * width){
y = 1.0;
}
else if (x <= center - width){
y = - 1.0 / width * (x - (center - 2.0 * width)) + 1.0;
}
else{
y = 0.0;
}
}
else if (express == LESS){
if (x <= center - 2.0 * width){
y = 0.0;
}
else if (x <= center - width){
y = 1.0 / width * (x - (center - width)) + 1.0;
}
else if (x <= center){
y = -1.0 / width * (x - (center - width)) + 1.0;
}
else{
y = 0.0;
}
}
else if (express == ZERO){
if (x <= center - width){
y = 0.0;
}
else if (x <= center){
y = 1.0 / width * (x - center) + 1.0;
}
else if (x <= center + width){
y = -1.0 / width * (x - center) + 1.0;
}
else{
y = 0.0;
}
}
else if (express == MORE){
if (x <= center){
y = 0.0;
}
else if (x <= center + width){
y = 1.0 / width * (x - (center + width)) + 1.0;
}
else if (x <= center + 2.0 * width){
y = -1.0 / width * (x - (center + width)) + 1.0;
}
else{
y = 0.0;
}
}
else if (express == MOREMORE){
if (x <= center + width){
y = 0.0;
}
else if (x <= center + 2.0 * width){
y = 1.0 / width * (x - (center + 2.0 * width)) + 1.0;
}
else{
y = 1.0;
}
}
return y;
};
// 後件部メンバーシップ関数(山3つ)クラス
class action_MF3
{
private:
double center;
double width;
SCALE express;
double x;
double y;
public:
action_MF3(double _center, double _witdth, SCALE _express){
y = 0.0; // yの値は、必ず0以上1以下になる
center = _center;
width = _witdth;
express = _express;
if (express == LESS){
x = center - width;
}
else if (express == ZERO){
x = center;
}
else if (express == MORE){
x = center + width;
}
else{
printf("wrong scale expression\n");
exit(0);
}
};
// Y座標の値を最大値で更新する
void func_Max(double b){
y = max(b, y);
};
// Y座標の値をリセット(y=0)する
void func_Reset(){
y = 0.0;
};
// X座標を返す
double func_X(void){
return x;
};
// (最大値で更新された、最後の)Y座標を返す
double func_Y(){
return y;
};
};
// 後件部メンバーシップ関数(山5つ)クラス
class action_MF5
{
private:
double center;
double width;
SCALE express;
double x;
double y;
public:
action_MF5(double _center, double _witdth, SCALE _express){
y = 0.0; // yの値は、必ず0以上1以下になる
center = _center;
width = _witdth;
express = _express;
if (express == LESSLESS){
x = center - 2.0 * width;
}
else if (express == LESS){
x = center - width;
}
else if (express == ZERO){
x = center;
}
else if (express == MORE){
x = center + width;
}
else if (express == MOREMORE){
x = center + 2.0 * width;
}
else{
printf("wrong scale expression\n");
exit(-1); // 強制終了
}
};
// Y座標の値を最大値で更新する
void func_Max(double b){
y = max(b, y);
};
// Y座標の値をリセット(y=0)する
void func_Reset(){
y = 0.0;
};
// X座標を返す
double func_X(void){
return x;
};
// (最大値で更新された、最後の)Y座標を返す
double func_Y(){
return y;
};
};
typedef enum post{
HEAD = 1, // 部長
CHIEF = 2, // 課長
STAFF = 3 // 平社員
} POST;
typedef struct person{
int absences; // 0:帰宅、 1:残業
char name[10];
int section;
int number;
POST post;
double p_x;
double p_y;
double expected_return_time; // 17.5(17:30)から、21.0(21:00)までの時間)
double going_home_ratio;
} PERSON;
const int SECTION = 6;
const int MEMBER = 8;
const int ALL_PERSON_COUNTER = 49; // 6つの課、それぞれ8人と部長
PERSON person[] = {
{1,"部長",-1,-1, HEAD, 0.0, 0.0},
{1,"社員", 0, 0, STAFF, 3.5 + 0.0 + 0.0, -4.4 + 0.0 + 0.0},
{1,"社員", 0, 1, STAFF, 3.5 + 0.0 + 1.2, -4.4 + 0.0 + 0.0},
{1,"社員", 0, 2, STAFF, 3.5 + 0.0 + 2.4, -4.4 + 0.0 + 0.0},
{1,"社員", 0, 3, STAFF, 3.5 + 0.0 + 3.6, -4.4 + 0.0 + 0.0},
{1,"課長", 0, 4, CHIEF, 3.5 + 0.0 + 0.0, -4.4 + 0.0 + 2.0},
{1,"社員", 0, 5, STAFF, 3.5 + 0.0 + 1.2, -4.4 + 0.0 + 2.0},
{1,"社員", 0, 6, STAFF, 3.5 + 0.0 + 2.4, -4.4 + 0.0 + 2.0},
{1,"社員", 0, 7, STAFF, 3.5 + 0.0 + 3.6, -4.4 + 0.0 + 2.0},
{1,"社員", 1, 0, STAFF, 3.5 + 4.8 + 0.0, -4.4 + 0.0 + 0.0},
{1,"社員", 1, 1, STAFF, 3.5 + 4.8 + 1.2, -4.4 + 0.0 + 0.0},
{1,"社員", 1, 2, STAFF, 3.5 + 4.8 + 2.4, -4.4 + 0.0 + 0.0},
{1,"課長", 1, 3, CHIEF, 3.5 + 4.8 + 3.6, -4.4 + 0.0 + 0.0},
{1,"社員", 1, 4, STAFF, 3.5 + 4.8 + 0.0, -4.4 + 0.0 + 2.0},
{1,"社員", 1, 5, STAFF, 3.5 + 4.8 + 1.2, -4.4 + 0.0 + 2.0},
{1,"社員", 1, 6, STAFF, 3.5 + 4.8 + 2.4, -4.4 + 0.0 + 2.0},
{1,"社員", 1, 7, STAFF, 3.5 + 4.8 + 3.6, -4.4 + 0.0 + 2.0},
{1,"社員", 2, 0, STAFF, 3.5 + 0.0 + 0.0, -4.4 + 3.4 + 0.0},
{1,"社員", 2, 1, STAFF, 3.5 + 0.0 + 1.2, -4.4 + 3.4 + 0.0},
{1,"課長", 2, 2, CHIEF, 3.5 + 0.0 + 2.4, -4.4 + 3.4 + 0.0},
{1,"社員", 2, 3, STAFF, 3.5 + 0.0 + 3.6, -4.4 + 3.4 + 0.0},
{1,"社員", 2, 4, STAFF, 3.5 + 0.0 + 0.0, -4.4 + 3.4 + 2.0},
{1,"社員", 2, 5, STAFF, 3.5 + 0.0 + 1.2, -4.4 + 3.4 + 2.0},
{1,"社員", 2, 6, STAFF, 3.5 + 0.0 + 2.4, -4.4 + 3.4 + 2.0},
{1,"社員", 2, 7, STAFF, 3.5 + 0.0 + 3.6, -4.4 + 3.4 + 2.0},
{1,"社員", 3, 0, STAFF, 3.5 + 4.8 + 0.0, -4.4 + 3.4 + 0.0},
{1,"社員", 3, 1, STAFF, 3.5 + 4.8 + 1.2, -4.4 + 3.4 + 0.0},
{1,"社員", 3, 2, STAFF, 3.5 + 4.8 + 2.4, -4.4 + 3.4 + 0.0},
{1,"社員", 3, 3, STAFF, 3.5 + 4.8 + 3.6, -4.4 + 3.4 + 0.0},
{1,"社員", 3, 4, STAFF, 3.5 + 4.8 + 0.0, -4.4 + 3.4 + 2.0},
{1,"社員", 3, 5, STAFF, 3.5 + 4.8 + 1.2, -4.4 + 3.4 + 2.0},
{1,"課長", 3, 6, CHIEF, 3.5 + 4.8 + 2.4, -4.4 + 3.4 + 2.0},
{1,"社員", 3, 7, STAFF, 3.5 + 4.8 + 3.6, -4.4 + 3.4 + 2.0},
{1,"課長", 4, 0, CHIEF, 3.5 + 0.0 + 0.0, -4.4 + 6.8 + 0.0},
{1,"社員", 4, 1, STAFF, 3.5 + 0.0 + 1.2, -4.4 + 6.8 + 0.0},
{1,"社員", 4, 2, STAFF, 3.5 + 0.0 + 2.4, -4.4 + 6.8 + 0.0},
{1,"社員", 4, 3, STAFF, 3.5 + 0.0 + 3.6, -4.4 + 6.8 + 0.0},
{1,"社員", 4, 4, STAFF, 3.5 + 0.0 + 0.0, -4.4 + 6.8 + 2.0},
{1,"社員", 4, 5, STAFF, 3.5 + 0.0 + 1.2, -4.4 + 6.8 + 2.0},
{1,"社員", 4, 6, STAFF, 3.5 + 0.0 + 2.4, -4.4 + 6.8 + 2.0},
{1,"社員", 4, 7, STAFF, 3.5 + 0.0 + 3.6, -4.4 + 6.8 + 2.0},
{1,"社員", 5, 0, STAFF, 3.5 + 4.8 + 0.0, -4.4 + 6.8 + 0.0},
{1,"社員", 5, 1, STAFF, 3.5 + 4.8 + 1.2, -4.4 + 6.8 + 0.0},
{1,"社員", 5, 2, STAFF, 3.5 + 4.8 + 2.4, -4.4 + 6.8 + 0.0},
{1,"社員", 5, 3, STAFF, 3.5 + 4.8 + 3.6, -4.4 + 6.8 + 0.0},
{1,"課長", 5, 4, CHIEF, 3.5 + 4.8 + 0.0, -4.4 + 6.8 + 2.0},
{1,"社員", 5, 5, STAFF, 3.5 + 4.8 + 1.2, -4.4 + 6.8 + 2.0},
{1,"社員", 5, 6, STAFF, 3.5 + 4.8 + 2.4, -4.4 + 6.8 + 2.0},
{1,"社員", 5, 7, STAFF, 3.5 + 4.8 + 3.6, -4.4 + 6.8 + 2.0},
};
double distance(PERSON* person1, PERSON* person2)
{
double d = 0.0;
d = pow((person1->p_x - person2->p_x),2.0);
d += pow((person1->p_y - person2->p_y),2.0);
d = sqrt(d);
return d;
}
int main()
{
PERSON* cheif[SECTION];
// 初期状態 (最初は個人の帰宅時間は、乱数で設定)
for (int i = 0; i < ALL_PERSON_COUNTER; i++){
// 各個人の帰宅時間を設定
person[i].expected_return_time = 17.5 + (21.0 -17.5) * rand()/ (1.0 + RAND_MAX);
// person[i].expected_return_time = 18.5;
// 各課の課長を選定(後で探すのが面倒なので)
if (person[i].post == CHIEF){
cheif[person[i].section] = &person[i];
}
}
/*
for (int i = 0; i < ALL_PERSON_COUNTER; i++){
printf("%d, expected_return_time = %f\n",i, person[i].expected_return_time);
printf("%d, expected_return_time = %d\n",i, person[i].absences);
}
*/
// 残業時間
condition_MF3 OverTime_Short(1.0, 2.0, LESS); // 1時間
condition_MF3 OverTime_Middle(1.0, 2.0, ZERO); // 2時間
condition_MF3 OverTime_Long(1.0, 2.0, MORE); // 3時間
// 部長からの距離
condition_MF3 DistanceFrom_HEAD_Near(7.0, 7.0, LESS); // 7メートル
condition_MF3 DistanceFrom_HEAD_Middle(7.0, 7.0, ZERO); // 14メートル
condition_MF3 DistanceFrom_HEAD_Far(7.0, 7.0, MORE); // 21メートル
// 課長からの距離
condition_MF3 DistanceFrom_CHIEF_Near(1.5, 1.5, LESS); // 1.5メートル
condition_MF3 DistanceFrom_CHIEF_Middle(1.5, 1.5, ZERO); // 3.0メートル
condition_MF3 DistanceFrom_CHIEF_Far(1.5, 1.5, MORE); // 4.5メートル
// 残っている課の人数
condition_MF3 Staying_COUNT_Less(2, 2, LESS); // 2人
condition_MF3 Staying_COUNT_Middle(2, 2, ZERO); // 4人
condition_MF3 Staying_COUNT_Many(2, 2, MORE); // 6人
// 帰宅度数
action_MF3 GoingHome_Hard(0.5, 0.5, LESS);// 帰宅度数 0.0 → 帰宅できない
action_MF3 GoingHome_Middle(0.5, 0.5, ZERO);// 帰宅度数 0.5 → 帰宅ボーダ
action_MF3 GoingHome_Easy(0.5, 0.5, MORE);// 帰宅度数 1.0 → 帰宅できる
double present_time =17.5;
while (present_time < 24.0){
present_time += 0.01; // 一番後ろで加算すると忘れそうなので
printf("present_time = %f\n", present_time);
// 部の中の課の全体状況の把握
if (person[0].expected_return_time < present_time){ // 部長は特別あつかい
person[0].absences = 0;
}
int staying_count[SECTION] ={}; // 各課(Section)で残っている人数を格納(最初はゼロリセット)
for (int i = 0; i < ALL_PERSON_COUNTER; i++){
if (person[i].absences == 1){
staying_count[person[i].section] += 1; // (Section)に一人追加
}
}
printf("time = %f, %d,%d,%d,%d,%d,%d\n",
present_time,
staying_count[0],
staying_count[1],
staying_count[2],
staying_count[3],
staying_count[4],
staying_count[5]);
for (int i = 1; i < ALL_PERSON_COUNTER; i++){ // person[0]の部長は神様→誰からも影響を受けない(とする)
/*
[ルール1]
全社員は、予定時間の1時間のまでの帰宅度数0.0
1時間から2時間までの帰宅度数0.5
2時間以上の帰宅度数1.0
*/
double r11 = min(OverTime_Short.func(present_time - person[i].expected_return_time));
GoingHome_Hard.func_Max(r11);
double r12 = min( OverTime_Middle.func(present_time - person[i].expected_return_time));
GoingHome_Middle.func_Max(r12);
double r13 = min(OverTime_Long.func(present_time - person[i].expected_return_time));
GoingHome_Easy.func_Max(r13);
if (person[i].post == CHIEF){ // 課長の場合の付加条件
/*
ルール2
部長が残っている場合、部長から5m以内の課長は、帰宅度数0.0
5m~10m以内の課長は、帰宅度数0.5
10m以上の課長は、帰宅度数1.0
*/
double dis = distance(&person[0], &person[i]);
double r21 = min((double)(person[0].absences), DistanceFrom_HEAD_Near.func(dis));
GoingHome_Hard.func_Max(r21);
double r22 = min((double)(person[0].absences), DistanceFrom_HEAD_Middle.func(dis));
GoingHome_Middle.func_Max(r22);
double r23 = min((double)(person[0].absences), DistanceFrom_HEAD_Far.func(dis));
GoingHome_Easy.func_Max(r23);
GoingHome_Middle.func_Max(r23);
}
if (person[i].post == STAFF){ // (ヒラ)社員の場合の付加条件
/*
ルール3
課長が残っている場合、課長から1.5m以内のメンバは、帰宅度数0.0
課長から3.0m以内のメンバは、帰宅度数0.5
課長から4.5m以内のメンバは、帰宅度数0.7
*/
double dis = distance(cheif[person[i].section], &person[i]);
double r31 = min((double)(cheif[person[i].section]->absences), DistanceFrom_CHIEF_Near.func(dis));
GoingHome_Hard.func_Max(r31);
double r32 = min((double)(cheif[person[i].section]->absences), DistanceFrom_CHIEF_Middle.func(dis));
GoingHome_Middle.func_Max(r32);
double r33 = min((double)(cheif[person[i].section]->absences), DistanceFrom_CHIEF_Far.func(dis));
GoingHome_Easy.func_Max(r33);
GoingHome_Middle.func_Max(r33);
}
#if 0
/*
ルール4
同じ課のメンバのの人数が、6人以上残っている場合は、帰宅度数0.3
4人残っている場合は、帰宅度数0.6
2人以下の場合は、帰宅度数1.0
*/
int num = staying_count[person[i].section];
double r41 = min(Staying_COUNT_Less.func((double)num));
GoingHome_Easy.func_Max(r41);
double r42 = min(Staying_COUNT_Middle.func((double)num));
GoingHome_Middle.func_Max(r42);
double r43 = min(Staying_COUNT_Many.func((double)num));
GoingHome_Hard.func_Max(r43);
#endif
/*
ルールに振れない場合は、min-max重心法の分母がゼロになり、
ゼロ割が発生する場合がある為、それを回避する
*/
double denominator = // 分母
GoingHome_Easy.func_Y() +
GoingHome_Middle.func_Y() +
GoingHome_Hard.func_Y();
double numerator = // 分子
GoingHome_Easy.func_X() * GoingHome_Easy.func_Y() +
GoingHome_Middle.func_X() * GoingHome_Middle.func_Y() +
GoingHome_Hard.func_X() * GoingHome_Hard.func_Y();
// 推論結果 (分母がゼロの場合は、推論結果は前回と同値とする)
if ( denominator != 0.0){
person[i].going_home_ratio = numerator / denominator ;
}
if (person[i].going_home_ratio > 0.5){
person[i].absences = 0; // 0:帰宅、 1:残業
}
// 後件部メンバーシップ関数のリセット(ループさせる時は必ずリセットする)
GoingHome_Easy.func_Reset();
GoingHome_Middle.func_Reset();
GoingHome_Hard.func_Reset();
}
}
for (int i = 0; i < ALL_PERSON_COUNTER; i++){
printf("%d, absences = %d\n",i, person[i].absences);
}
return 0;
}