/*
このプログラムをtype1.cpp"という名前で保存して、
gcc -g v-ebata.cpp -o v-ebata
でコンパイルして下さい。
===============================================================
ダイエット仮想人格シミュレータ「バーチャル江端」
(ファジィ推論エンジン + 超シンプル体重変動シミュレータ拡張版)
2016/02/07
江端智一
このプログラムの利用によって発生する不利益対して、
江端の完全な免責を条件として、このプログラムの
無償かつ無制限の使用を許諾します。
*/
#include <stdio.h>
#include <stdlib.h>
#define LAST14 14 // 14日分の数字"14"を使う時
// 共通関数
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;
};
};
int main()
{
// ダイエット経過期間
condition_MF3 Period_Short(14, 14, LESS); // 0日
condition_MF3 Period_Middle(14, 14, ZERO); // 14日
condition_MF3 Period_Long(14, 14, MORE); // 28日
// いわゆる「停滞期」(14日間あたりの平均減重変化量)
condition_MF3 WeighChange_Small(1.0, 1.0, LESS); // 0kg
condition_MF3 WeighChange_Middle(1.0, 1.0, ZERO); // 1kg
condition_MF3 WeighChange_High(1.0, 1.0, MORE); // 2kg
// 苦痛度
condition_MF3 Suffer_Zerol(400.0, 400.0, LESS); // 0
condition_MF3 Suffer_Middle(400.0, 400.0, ZERO); // 400
condition_MF3 Suffer_High(400.0, 400.0, MORE); // 800
// BMI
condition_MF5 BMI_lowlow(22.0, 3.0, LESSLESS); // 痩せすぎ(15.0)
condition_MF5 BMI_low(22.0, 3.0, LESS);// 痩せぎみ(19.0)
condition_MF5 BMI_Norcmal(22.0, 3.0, ZERO);// 普通(22.0)
condition_MF5 BMI_High(22.0, 3.0, MORE); // 太りぎみ(25.0)
condition_MF5 BMI_HighHigh(22.0, 3.0, MOREMORE);// 太りすぎ(28.0)
// 一日の摂取カロリー
action_MF5 Eat_LittleLittle(2500, 1000, LESSLESS); // 500 Kcal
action_MF5 Eat_Little(2500, 1000, LESS);// 1500 Kcal
action_MF5 Eat_Normal(2500, 1000, ZERO);// 2500 Kcal
action_MF5 Eat_Lot(2500, 1000, MORE);// 3500 Kcal
action_MF5 Eat_LotLot(2500, 1000, MOREMORE);// 4500 Kcal
// 超シンプルシミュレータ投入
// 体重変動シミュレーションプログラム
//基礎代謝量と求め方 ?ハリス・ベネディクト方程式(日本人版) 計算式
//http://www.kintore.info/kisotaisya_mass/index.cgi
//【計算式】
// 男性 66.5+(体重kg×13.8)+(身長cm×5.0)-(年齢×6.8)
// 女性 665.1+(体重kg×9.6)+(身長cm×1.9)-(年齢×7.0)
// 江端智一 2014日2月4日現在のデータを使って計算
double weight = 78.0; // 開始時の体重 78.0kg
double lengthw = 172.0; // 身長 172cm
double age = 49.0 + 94/365; // 開始時の年齢 49歳と94日 (2014年2月4日現在)
// 最初の基礎代謝カロリー 男性 66.5+(体重kg×13.8)+(身長cm×5.0)-(年齢×6.8)
double consumption_calorie =
66.5 + weight * 13.8 + 172.0 * 5.0 - age * 6.8;
// このカロリーに運動消費カロリーを加えて、
// トータルの一日の消費カロリーを算出する
// (現在の体重 66.5kgにおいて)一日運動消費カロリー 708kcalと推定し、
// このカロリーは、体重に比例するものと仮定する。
consumption_calorie += weight/66.5 * 708.0;
//printf("consumption_calorie = %f\n", consumption_calorie);
// 摂取カロリーの初期値は、暫定的に消費カロリーと同値とする
// (第一回目の苦痛値の算出の為)
double source_calorie = consumption_calorie;
double last_weight[LAST14]; // 過去14日分の体重
// 最初は全部同じ体重とする 14日経過までは同じ体重とする
for (int i = 0; i < LAST14; i++ ){
last_weight[i] = weight;
}
printf("経過日,体重,消費カロリー,摂取カロリー\n");
// ここからループ開始
for (int day=0; day < 30; day++){ // ここで日数を入れてね(ここでは30日)
// 7kcal = 1g = 0.001kg とする
// 体重データを一日分ずらす
for (int i = 0; i < LAST14-1; i++ ){
last_weight[i+1] = last_weight[i];
}
last_weight[0] = weight;
// 線形補完の傾きだけを求める(14日分)
// http://d.hatena.ne.jp/rainlib/20090112/1231735459
double k1=0, k2=0, k3=0, k4=0;
for (int i = 0; i < LAST14; i++ ){
k1 += (double)i * (double)i; // Σ(x^2)
k2 += (double)i * last_weight[i]; // Σ(xy)
k3 += (double)i; // Σ(x)
k4 += last_weight[i]; // Σ(y)
}
double dW = ((double)LAST14 * k2 - k3 * k4) / ((double)LAST14 * k4 - k3*k3); // 14
// BMIの計算式 体重(Kg)÷身長(m)÷身長(m) 江端の身長172cm
double bmi = weight / 1.72 / 1.72;
// 苦痛値の算出
double suffer = consumption_calorie - source_calorie;
//printf("suffer=%f\n", suffer);
double r; // min-maxのmin値算出結果用の変数
// =============== ファジイルール入れ替え ===============
// =============== バーチャル江端 タイプ1(ここから) ===============
// [ルール1] ダイエット開始直後は、「無条件にがんばれる」
r = min(Period_Short.func(day));
Eat_Little.func_Max(r);
//printf("r1: %f\n",r);
// [ルール2] しかし、苦しいと感じたら、食べ過ぎてしまう。
r = min(Suffer_Middle.func(suffer));
Eat_Lot.func_Max(r);
//printf("r2: %f\n",r);
r = min(Suffer_High.func(suffer));
Eat_Lot.func_Max(r);
//printf("r2: %f\n",r);
// [ルール3] 後半になると、ダイエットのことを忘れて食べてしまう
r = min(Period_Long.func(day));
Eat_Lot.func_Max(r);
//printf("r3: %f\n",r);
// =============== バーチャル江端 タイプ1(ここまで) ===============
/*
ルールに振れない場合は、min-max重心法の分母がゼロになり、
ゼロ割が発生する場合がある為、それを回避する
*/
double denominator = // 分母
(Eat_LittleLittle.func_Y() +
Eat_Little.func_Y() +
Eat_Normal.func_Y() +
Eat_Lot.func_Y() +
Eat_LotLot.func_Y()) ;
double numerator = // 分子
(Eat_LittleLittle.func_X() * Eat_LittleLittle.func_Y() +
Eat_Little.func_X() * Eat_Little.func_Y() +
Eat_Normal.func_X() * Eat_Normal.func_Y() +
Eat_Lot.func_X() * Eat_Lot.func_Y() +
Eat_LotLot.func_X() * Eat_LotLot.func_Y());
// 推論結果 (分母がゼロの場合は、推論結果は前回と同値とする)
if ( denominator != 0.0){
source_calorie = numerator / denominator ;
}
// 後件部メンバーシップ関数のリセット(ループさせる時は必ずリセットする)
Eat_LittleLittle.func_Reset();
Eat_Little.func_Reset();
Eat_Normal.func_Reset();
Eat_Lot.func_Reset();
Eat_LotLot.func_Reset();
//printf("\n source_calorie = %f\n",source_calorie);
double diff_weight = (source_calorie - consumption_calorie)/7.0 / 1000.0;
weight += diff_weight;
age += 1.0/365.0;
consumption_calorie = 66.5 + weight * 13.8 + 172.0 * 5.0 - age * 6.8;
consumption_calorie += weight/66.5 * 708.0;
// printf("day:%d\tweight = %f\tconsumption_calorie =%f\n", day,weight, consumption_calorie);
printf("%d\t%5.2f\t%5.2f\t%5.2f\n", day,weight, consumption_calorie,source_calorie);
}
}
/*
必要に応じてルールを差し替えて下さい。
// =============== バーチャル江端 タイプ1(ここから) ===============
// [ルール1] ダイエット開始直後は、「無条件にがんばれる」
r = min(Period_Short.func(day));
Eat_Little.func_Max(r);
//printf("r1: %f\n",r);
// [ルール2] しかし、苦しいと感じたら、食べ過ぎてしまう。
r = min(Suffer_Middle.func(suffer));
Eat_Lot.func_Max(r);
//printf("r2: %f\n",r);
r = min(Suffer_High.func(suffer));
Eat_Lot.func_Max(r);
//printf("r2: %f\n",r);
// [ルール3] 後半になると、ダイエットのことを忘れて食べてしまう
r = min(Period_Long.func(day));
Eat_Lot.func_Max(r);
//printf("r3: %f\n",r);
// =============== バーチャル江端 タイプ1(ここまで) ===============
// =============== バーチャル江端 タイプ2(ここから) ===============
// [ルール1] 1月程度は「がんばれる」
r = min(Period_Short.func(day));
Eat_Little.func_Max(r);
//printf("r1: %f\n",r);
r = min(Period_Middle.func(day));
Eat_Little.func_Max(r);
//printf("r1: %f\n",r);
// [ルール2] しかし、苦しいと感じた日には普通に食べてしまうこともある。
r = min(Suffer_Middle.func(suffer));
Eat_Normal.func_Max(r);
//printf("r2: %f\n",r);
// [ルール3] 停滞が続くと食べてしまう。
r = min(WeighChange_Middle.func(dW));
Eat_Normal.func_Max(r);
// [ルール4] 全く停滞してしまうと、食べてしまう。
r = min(WeighChange_Small.func(dW));
Eat_Normal.func_Max(r);
// =============== バーチャル江端 タイプ2(ここまで) ===============
// =============== バーチャル江端 タイプ3(ここから) ===============
// [ルール1] いつだってがんばっている
r = min(Period_Short.func(day));
Eat_Little.func_Max(r);
//printf("r1: %f\n",r);
r = min(Period_Middle.func(day));
Eat_Little.func_Max(r);
//printf("r1: %f\n",r);
r = min(Period_Middle.func(day));
Eat_Little.func_Max(r);
//printf("r1: %f\n",r);
// [ルール2] やせてくると、コントロールできなくなる
r = min(BMI_low.func(bmi));
Eat_LittleLittle.func_Max(r);
r = min(BMI_lowlow.func(bmi));
Eat_LittleLittle.func_Max(r);
// =============== バーチャル江端 タイプ3(ここまで) ===============
*/