「上司の帰宅が遅れると、社員の帰宅も遅れる」の仮説検証プログラム

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;
}

2022年10月1日2017,江端さんの技術メモ

Posted by ebata