Go言語によるファジィ(Fuzzy)推論コード

C言語によるファジィ(Fuzzy)推論コード

をGo言語に移植してみました。

で、できるだけ、

「GO言語でクラスっぽいことをする」をレビューする

に近づけてみました。

コメントのPrint分は、C++のを残しています(面倒だったので)。あとenum型がないので、テキトーに文字列を使うことにしました。

package main

import (
	"fmt"
	"os"
)

func max_2(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func max_3(a, b, c float64) float64 {
	return max_2(max_2(a, b), c)
}

func max_4(a, b, c, d float64) float64 {
	return max_2(max_3(a, b, c), d)
}

func min_1(a float64) float64 {
	return a
}

func min_2(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func min_3(a, b, c float64) float64 {
	return min_2(min_2(a, b), c))
}

func min_4(a, b, c, d float64) float64 {
	return min_2(min_3(a, b, c), d)
}

type condition_MF3 struct { // condition_MF3の基底クラス
	center  float64
	width   float64
	express string
}

func new_condition_MF3(_center, _width float64, _express string) *condition_MF3 {
	c3 := new(condition_MF3)
	c3.center = _center
	c3.width = _width
	c3.express = _express
	return c3
}

// 前件部メンバーシップ関数(山3つ)クラス
func (c3 *condition_MF3) func_X(_x float64) float64 {
	// x,yは、メンバーシップ関数上の座標を示す
	x := _x
	y := 0.0 // yの値は、必ず0以上1以下になる

	if c3.express == "LESS" {
		if x <= c3.center-c3.width {
			y = 1.0
		} else if x <= c3.center {
			y = -1.0 / c3.width * (x - c3.center)
		} else {
			y = 0.0
		}
	} else if c3.express == "ZERO" {
		if x <= c3.center-c3.width {
			y = 0.0
		} else if x <= c3.center {
			y = 1.0/c3.width*(x-c3.center) + 1.0
		} else if x <= c3.center+c3.width {
			y = -1.0/c3.width*(x-c3.center) + 1.0
		} else {
			y = 0.0
		}
	} else if c3.express == "MORE" {
		if x <= c3.center {
			y = 0.0
		} else if x <= c3.center+c3.width {
			y = 1.0 / c3.width * (x - c3.center)
		} else {
			y = 1.0
		}
	} else {
		fmt.Println("wrong expression\n")
		os.Exit(1)
	}
	return y
}

type condition_MF5 struct { // condition_MF5の基底クラス
	center  float64
	width   float64
	express string
}

func new_condition_MF5(_center, _width float64, _express string) *condition_MF5 {
	c5 := new(condition_MF5)
	c5.center = _center
	c5.width = _width
	c5.express = _express
	return c5
}

func (c5 *condition_MF5) func_X(_x float64) float64 {
	// 前件部メンバーシップ関数(山5つ)クラス
	// x,yは、メンバーシップ関数上の座標を示す
	x := _x
	y := 0.0 // yの値は、必ず0以上1以下になる

	if c5.express == "LESSLESS" {
		if x <= c5.center-2.0*c5.width {
			y = 1.0
		} else if x <= c5.center-c5.width {
			y = -1.0/c5.width*(x-(c5.center-2.0*c5.width)) + 1.0
		} else {
			y = 0.0
		}
	} else if c5.express == "LESS" {
		if x <= c5.center-2.0*c5.width {
			y = 0.0
		} else if x <= c5.center-c5.width {
			y = 1.0/c5.width*(x-(c5.center-c5.width)) + 1.0
		} else if x <= c5.center {
			y = -1.0/c5.width*(x-(c5.center-c5.width)) + 1.0
		} else {
			y = 0.0
		}
	} else if c5.express == "ZERO" {
		if x <= c5.center-c5.width {
			y = 0.0
		} else if x <= c5.center {
			y = 1.0/c5.width*(x-c5.center) + 1.0
		} else if x <= c5.center+c5.width {
			y = -1.0/c5.width*(x-c5.center) + 1.0
		} else {
			y = 0.0
		}
	} else if c5.express == "MORE" {
		if x <= c5.center {
			y = 0.0
		} else if x <= c5.center+c5.width {
			y = 1.0/c5.width*(x-(c5.center+c5.width)) + 1.0
		} else if x <= c5.center+2.0*c5.width {
			y = -1.0/c5.width*(x-(c5.center+c5.width)) + 1.0
		} else {
			y = 0.0
		}
	} else if c5.express == "MOREMORE" {
		if x <= c5.center+c5.width {
			y = 0.0
		} else if x <= c5.center+2.0*c5.width {
			y = 1.0/c5.width*(x-(c5.center+2.0*c5.width)) + 1.0
		} else {
			y = 1.0
		}
	}
	return y
}

type action_MF3 struct { // condition_MF5の基底クラス
	center  float64
	width   float64
	express string
	x       float64
	y       float64
}

func new_action_MF3(_center, _width float64, _express string) *action_MF3 {
	a3 := new(action_MF3)
	a3.center = _center
	a3.width = _width
	a3.express = _express
	a3.x = 0.0
	a3.y = 0.0

	return a3
}

// 後件部メンバーシップ関数(山3つ)クラス
func (a3 *action_MF3) func_Y() float64 {
	// x,yは、メンバーシップ関数上の座標を示す

	if a3.express == "LESS" {
		a3.x = a3.center - a3.width
	} else if a3.express == "ZERO" {
		a3.x = a3.center
	} else if a3.express == "MORE" {
		a3.x = a3.center + a3.width
	} else {
		fmt.Println("wrong scale expression\n")
		os.Exit(0)
	}
	return a3.x
}

func (a3 *action_MF3) func_Max(b float64) {
	a3.y = max_2(b, a3.y)
}

func (a3 *action_MF3) func_X() float64 {
	return a3.x
}

/////////////////////////////

type action_MF5 struct { // condition_MF5の基底クラス
	center  float64
	width   float64
	express string
	x       float64
	y       float64
}

func new_action_MF5(_center, _width float64, _express string) *action_MF5 {
	a5 := new(action_MF5)
	a5.center = _center
	a5.width = _width
	a5.express = _express
	a5.x = 0.0
	a5.y = 0.0

	return a5
}

// 後件部メンバーシップ関数(山3つ)クラス
func (a5 *action_MF5) func_Y() float64 {
	// x,yは、メンバーシップ関数上の座標を示す

	if a5.express == "LESSLESS" {
		a5.x = a5.center - 2.0*a5.width
	} else if a5.express == "LESS" {
		a5.x = a5.center - a5.width
	} else if a5.express == "ZERO" {
		a5.x = a5.center
	} else if a5.express == "MORE" {
		a5.x = a5.center + a5.width
	} else if a5.express == "MOREMORE" {
		a5.x = a5.center + 2.0*a5.width
	} else {
		fmt.Println("wrong scale expression\n")
		os.Exit(0)
	}
	return a5.x
}

func (a5 *action_MF5) func_Max(b float64) {
	a5.y = max_2(b, a5.y)
}

func (a5 *action_MF5) func_X() float64 {
	return a5.x
}

func main() {
	// ダイエット経過期間
	Period_Short := new_condition_MF3(14, 14, "LESS")  // 0日
	Period_Middle := new_condition_MF3(14, 14, "ZERO") // 14日
	Period_Long := new_condition_MF3(14, 14, "MORE")   // 28日

	// いわゆる「停滞期」(14日間あたりの平均減重変化量)
	WeighChange_Small := new_condition_MF3(1.0, 1.0, "LESS") // 0kg
	//WeighChange_Middle := new_condition_MF3(1.0, 1.0, "ZERO") // 1kg
	WeighChange_High := new_condition_MF3(1.0, 1.0, "MORE") // 2kg

	// 苦痛度
	/*
		Suffer_Zerol := new_condition_MF3(400.0, 400.0, "LESS")  // 0
		Suffer_Middle := new_condition_MF3(400.0, 400.0, "ZERO") // 400
		Suffer_Hight := new_condition_MF3(400.0, 400.0, "MORE")  // 800
	*/
	// BMI
	//BMI_lowlow := new_condition_MF5(22.0, 3.0, "LESSLESS")   // 痩せすぎ(15.0)
	//BMI_low := new_condition_MF5(22.0, 3.0, "LESS")          // 痩せぎみ(19.0)
	//BMI_Normal := new_condition_MF5(22.0, 3.0, "ZERO")       // 普通(22.0)
	BMI_High := new_condition_MF5(22.0, 3.0, "MORE")         // 太りぎみ(25.0)
	BMI_HighHigh := new_condition_MF5(22.0, 3.0, "MOREMORE") // 太りすぎ(28.0)

	// 一日の摂取カロリー
	Eat_LittleLittle := new_action_MF5(2500, 1000, "LESSLESS") // 500 Kcal
	Eat_Little := new_action_MF5(2500, 1000, "LESS")           // 1500 Kcal
	Eat_Normal := new_action_MF5(2500, 1000, "ZERO")           // 2500 Kcal
	Eat_Lot := new_action_MF5(2500, 1000, "MORE")              // 3500 Kcal
	Eat_LotLot := new_action_MF5(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日現在のデータを使って計算
	weight := 78.0 // 開始時の体重 78.0kg
	//lengthw := 172.0     // 身長 172cm
	age := 49.0 + 94/365 // 開始時の年齢 49歳と94日 (2014年2月4日現在)

	// 最初の基礎代謝カロリー 男性  66.5+(体重kg×13.8)+(身長cm×5.0)-(年齢×6.8)
	consumption_calorie :=
		66.5 + weight*13.8 + 172.0*5.0 - age*6.8
	// このカロリーに運動消費カロリーを加えて、
	// トータルの一日の消費カロリーを算出する

	// (現在の体重 66.5kgにおいて)一日運動消費カロリー 708kcalと推定し、
	// このカロリーは、体重に比例するものと仮定する。
	consumption_calorie += weight / 66.5 * 708.0
	fmt.Println("consumption_calorie = ", consumption_calorie)

	var last_weight [14]float64 // 過去14日分の体重

	// 最初は全部同じ体重とする 14日経過までは同じ体重とする
	for i := 0; i < 14; i++ {
		last_weight[i] = weight
	}
	// ここからループ開始
	for day := 0; day < 1460; day++ { // 1460日は、ちょうど4年分の日数
		// 7kcal = 1g = 0.001kg とする

		// 体重データを一日分ずらす
		for i := 0; i < 14-1; i++ {
			last_weight[i+1] = last_weight[i]
		}
		last_weight[0] = weight

		// 線形補完の傾きだけを求める(14日分)
		// http://d.hatena.ne.jp/rainlib/20090112/1231735459
		k1 := 0.0
		k2 := 0.0
		k3 := 0.0
		k4 := 0.0
		for i := 0; i < 14; i++ {
			k1 += float64(i) * float64(i)
			k2 += float64(i) * last_weight[i]
			k3 += float64(i)
			k4 += last_weight[i] * last_weight[i]
		}
		dW := (14.0*k2 - k3*k4) / (14.0*k4 - k3*k3) // 14

		// BMIの計算式 体重(Kg)÷身長(m)÷身長(m) 江端の身長172cm
		bmi := weight / 1.72 / 1.72

		// [ルール1] ダイエット開始直後は、「無条件にがんばれる」
		r1 := min_1(Period_Short.func_X(float64(day)))
		Eat_Little.func_Max(r1)
		//printf("r1: %f\n",r1);

		//printf("Eat_Little.func_X() = %f\n",Eat_Little.func_X());
		//printf("Eat_Little.func_Y() = %f\n",Eat_Little.func_Y());

		// [ルール2] ダイエット一ヶ月を過ぎても、停滞し続けると「切れる」
		r2 := min_2(Period_Long.func_X(float64(day)), WeighChange_Small.func_X(dW))
		Eat_LotLot.func_Max(r2)
		//printf("r2: %f\n",r2);

		// [ルール3] ダイエット2週間を過ぎて、停滞し続けると「緩んでくる」
		r3 := min_2(Period_Middle.func_X(float64(day)), WeighChange_Small.func_X(dW))
		Eat_Normal.func_Max(r3)
		//printf("r3: %f\n",r3);

		// [ルール4] 停滞期がなく順調に体重が落ちている場合は「がんばれる」
		r4 := min_1(WeighChange_High.func_X(dW))
		Eat_Little.func_Max(r4)
		//        printf("r4: %f\n",r4);

		// [ルール5] ダイエット一ヶ月を過ぎて、再び太り出してくると、またダイエットを再開してしまう
		r5 := min_2(Period_Long.func_X(float64(day)), BMI_High.func_X(bmi))
		Eat_Little.func_Max(r5)
		//printf("r5: %f\n",r5);

		// [ルール6] ダイエット一ヶ月を過ぎて、太り過ぎると、滅茶苦茶なダイエットを始めてしまう
		r6 := min_2(Period_Long.func_X(float64(day)), BMI_HighHigh.func_X(bmi))
		Eat_LittleLittle.func_Max(r6)

		//printf("r6: %f\n",r6);
		/*
		   printf("Eat_LittleLittle.func_X() = %f\n",Eat_LittleLittle.func_X());
		   printf("Eat_LittleLittle.func_Y() = %f\n",Eat_LittleLittle.func_Y());
		   printf("Eat_Little.func_X() = %f\n",Eat_Little.func_X());
		   printf("Eat_Little.func_Y() = %f\n",Eat_Little.func_Y());
		   printf("Eat_Normal.func_X() = %f\n",Eat_Normal.func_X());
		   printf("Eat_Normal.func_Y() = %f\n",Eat_Normal.func_Y());
		   printf("Eat_Lot.func_X() = %f\n",Eat_Lot.func_X());
		   printf("Eat_Lot.func_Y() = %f\n",Eat_Lot.func_Y());
		   printf("Eat_LotLot.func_X() = %f\n",Eat_LotLot.func_X());
		   printf("Eat_LotLot.func_Y() = %f\n",Eat_LotLot.func_Y());
		*/

		source_calorie :=
			(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()) /
				(Eat_LittleLittle.func_Y() +
					Eat_Little.func_Y() +
					Eat_Normal.func_Y() +
					Eat_Lot.func_Y() +
					Eat_LotLot.func_Y())

		fmt.Println("source_calorie = ", source_calorie)

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

 

2022/09,江端さんの技術メモ

Posted by ebata