/// <summary>
 /// XORの学習の例
 /// <para>NeuralNetworkクラスを直接操作して論理演算を実行する例です。</para>
 /// </summary>
 public static void XOR_learning()
 {
     var para = new Parameter(2, 5, 1, 1, 0.1);                          // ニューラルネットのパラメータ設定(中間層が多いと、学習係数を小さくとる必要がある)7層が限界・・
     var NNforXOR = new NeuralNetwork();                                 // ニューラルネットクラスのインスタンス生成
     NNforXOR.Setup(para);
     for (int i = 0; i < 100; i++)                                       // 学習開始
     {
         var ans = new double[4];
         Console.Write("now i: " + i.ToString() + "@ ");
         for (int k = 0; k < 100; k++)
         {
             ans[0] = NNforXOR.Learn(new double[2] { 0.0, 0.0 }, 0.0);   // 特徴ベクトルと教師ベクトルを渡す
             ans[1] = NNforXOR.Learn(new double[2] { 0.0, 1.0 }, 1.0);
             ans[2] = NNforXOR.Learn(new double[2] { 1.0, 0.0 }, 1.0);
             ans[3] = NNforXOR.Learn(new double[2] { 1.0, 1.0 }, 0.0);
         }
         Console.WriteLine("ans = " + ans[0].ToString("0.000") + ", " + ans[1].ToString("0.000") + ", " + ans[2].ToString("0.000") + ", " + ans[3].ToString("0.000") +
             ", 最後の学習結果→Error variation," + NNforXOR.VariationOfError.ToString("0.000") + ", Total Error," + NNforXOR.TotalOutputError.ToString("0.000"));
     }
     NNforXOR.Save("NN.ini");
     return;
 }
 /// <summary>
 /// sin()関数の学習の例
 /// <para>NeuralNetworkクラスを直接利用して関数近似を行わせるサンプルです。</para>
 /// <para>
 /// 2011/12/25現在、0~1までの出力しか得られない。
 /// 出力関数と学習則を変更するか、関数近似時には値のマッピングが必要である。
 /// 他の人はどうやってんだろう?</para>
 /// </summary>
 public static void sin_learnig()
 {
     var para = new Parameter(1, 13, 1, 3, 0.01);            // ニューラルネットのパラメータ設定
     var NNforSin = new NeuralNetwork();                     // ニューラルネットクラスのインスタンス生成
     NNforSin.Setup(para);
     for (int i = 0; i < 500; i++)                           // 学習開始
     {
         Console.Write("\nnow i: " + i.ToString() + "@ ");
         Console.Write("ans = ");
         for (int k = 0; k < 15; k++)
         {
             double theta = (double)k / 15.0 * 2.0 * Math.PI;
             double ans = 0.0;
             for (int l = 0; l < 100; l++) ans = NNforSin.Learn(theta, Math.Sin(theta));       // 特徴ベクトルと教師ベクトルを渡す
             Console.Write(", " + ans.ToString("0.000"));
         }
     }
     NNforSin.Save("NN.ini");
     return;
 }
        /// <summary>
        /// 学習を実行させる
        /// <para>学習強度を引数で設定可能です。</para>
        /// </summary>
        /// <param name="learningTimes">学習回数</param>
        /// <param name="learningStrength">学習強度(要素数はモデル数と一致すること)</param>
        /// <param name="discriminator">学習させる識別器</param>
        /// <param name="classForLearning">学習用教師データモデル</param>
        /// <returns>識別器の出力誤差積算値</returns>
        private double Learn(int learningTimes, int[] learningStrength, NeuralNetwork discriminator, List<Model> classForLearning)
        {
            if (this.Ready == false)
                throw new SystemException("学習の準備はまだ完了しておりません。モデルを追加して下さい。");
            if (classForLearning.Count != learningStrength.Length)
                throw new SystemException("学習強度の要素数がモデル数と一致しません。");
            if (discriminator == null)
                throw new Exception("判別器のインスタンスが確保されておらず、学習ができません。");
            // 学習をスタート
            foreach (Model model in classForLearning)
                model.SequenceMode = this.PickOutMode;
            int learningCount = 0;
            double[] errorEachModel = new double[classForLearning.Count];
            Boolean need = CheckLearningStrength(learningStrength);                             // 学習させる必要性を学習強度からチェック
            int backupOfModelIndex = int.MaxValue;
            int sameCount = 0;

            while (learningCount < learningTimes && need)                                       // 学習回数をチェックしながらループ
            {
                int modelIndex;
                if (this.RandomModelChoice)
                    modelIndex = this.myRandom.Next(classForLearning.Count);
                else
                    modelIndex = learningCount % classForLearning.Count;
                if (backupOfModelIndex != modelIndex)                                           // 2回連続で学習させると学習が偏ってしまうので回避。エニグマみたい。
                {
                    Vector teature = this.CreateTeachingVector(modelIndex);
                    double error = 0.0;
                    int count = 0;
                    // 指定された強度分配に基づき、計算した回数分モデルを呼び出す
                    for (int i = 0; i < learningStrength[modelIndex]; i++)
                    {
                        Feature feature = classForLearning[modelIndex].GetFeature();
                        Vector outputForDebug = discriminator.Learn(feature, teature);          // 学習
                        learningCount++;
                        error += discriminator.TotalOutputError;
                        count++;
                    }
                    error /= (double)count;                                                     // 特徴量1件当たりの誤差量を計算
                    errorEachModel[modelIndex] = error;                                         // シーケンス上では何の役にも立っていないが、デバッグ用と思ってください。将来的には誤差の収束条件を入れたいと思っています。
                    sameCount = 0;
                }
                else
                {
                    if (sameCount < 100)                                                        // 同じ数が出るようになったらインスタンスをもう一度作ってみる
                        sameCount++;                                                            // 乱数ジェネレータが壊れるとかあほかと(並列演算が悪いの?)
                    else                                                                        // これで一応うまくいくことを確認した
                        this.myRandom = new Random();
                }
                backupOfModelIndex = modelIndex;
            }
            return discriminator.TotalOutputError;
        }