Beispiel #1
0
        /// <summary>
        /// 最急降下法で最適な位置を計算する
        /// </summary>
        /// <param name="currentTable"></param>
        /// <param name="beforeRuleTable"></param>
        /// <param name="afterRuleTable"></param>
        /// <param name="changes"></param>
        private void DescentMethod(IVRiscuitObjectSet currentTable, IVRiscuitObjectSet beforeTable, IVRiscuitObjectSet afterRuleTable, IVRiscuitObjectSet beforeRuleTable)
        {
            var limit       = 100;
            var beforeScore = 0.0f;
            var beforec     = new VRiscuitObjectSet(beforeTable);
            var currentc    = new VRiscuitObjectSet(currentTable);
            var parameters  = currentc.ToParameters();
            var firstParam  = currentc.ToParameters();
            var beforeDelta = new float[parameters.Length];

            Func <float[], float> func = delegate(float[] param)
            {
                (currentc as IVRiscuitObjectSet).SetParameter(param);
                var scores = RuleManager.CalcAppliedFieldScore(currentc, beforec, afterRuleTable, beforeRuleTable, (this as IRule).RuleScoreCoefficient);
                return(scores[0] / scores[1]);
            };
            var alpha = _paramLength;

            var f = 0.001; // scoreの変動がこの値以下になったら終わり

            for (int i = 0; i < limit; i++)
            {
                var score = func(parameters);
                // var message = String.Format("{0}: {1}, {2}, {3}, {5}, {6}, {7} => {4} points", i, parameters[0], parameters[1], parameters[2], score, parameters[3], parameters[4], parameters[5]);
                //Debug.Log(message);
                // Debug.Log("alpha = " + alpha);
                if (Mathf.Abs(beforeScore - score) <= f && i != 0)
                {
                    // 終了
                    break;
                }
                beforeScore = score;
                var delta = Differential(func, parameters);
                delta = NormalizeArray(delta);

                // deltaの中に0の要素があったら、beforeDeltaをもとに前回の変更を半分もとに戻す
                // その場合次のbeforeDeltaは
                // 同時にbeforeDeltaの更新
                for (int di = 0; di < delta.Length; di++)
                {
                    if (delta[di] == 0)
                    {
                        delta[di]        = -(beforeDelta[di] / 2);
                        beforeDelta[di] /= 2;
                    }
                    else
                    {
                        beforeDelta[di] = delta[di];
                    }
                }

                // Debug.Log("delta = " + delta.Skip(1).Aggregate(delta[0].ToString(), (acc, next) => acc + ", " + next.ToString()));
                for (int j = 0; j < parameters.Length; j++)
                {
                    parameters[j] += delta[j] * alpha;
                }
                // Debug.Log("parameter = " + parameters.Skip(1).Aggregate(parameters[0].ToString(), (acc, next) => acc + ", " + next.ToString()));
                alpha *= Mathf.Max((float)Math.Exp(-alpha), 0.1f);
            }
            // オブジェクトの生成、削除がある場合には、1fで動かす
            // そうでない場合には、1秒かけて動かすように調整する
            if (!isGenerateOrDeleteObject)
            {
                var len = firstParam.Length;
                var d   = new float[len];
                for (int i = 0; i < len; i++)
                {
                    var di = parameters[i] - firstParam[i];
                    // 細かい部分を四捨五入, 秒間スピードに変更
                    di            = ((float)Math.Round(di, 1)) * Time.deltaTime;
                    parameters[i] = firstParam[i] + di;
                }
            }
            currentTable.SetParameter(parameters);
        }