/// <summary>
        /// ニューラルネットの演算結果を分かり易くIDと尤度にまとめて返す
        /// </summary>
        /// <param name="outputVector">ニューラルネットの出力ベクトル</param>
        /// <param name="productionVolume">配列の生成数(1以上かつ出力ベクトルの次元を超えないようにして下さい)</param>
        /// <returns>ID・尤度の組を配列に加工したもの<para>本クラスのIDnamesメンバがセットされていない場合は""が返ります。</para></returns>
        /// <exception cref="SystemException">要求サイズに異常があるとスロー</exception>
        /// <example>
        /// 以下にサンプルコードを示します。
        /// このコードは処理の流れを大まかに示しています。
        /// 実際にはnnclassは結合係数等をセットしなければ識別は行えません。
        /// <code>
        /// NeuralNetwork.Parameter para = new NeuralNetwork.Parameter(2, 6, 1, 1, 0.01);           // ニューラルネットのパラメータ設定
        /// NeuralNetwork nnclass = new NeuralNetwork(para);                                        // ニューラルネットクラスのインスタンス生成
        /// double[] feature10 = new double[2] { 1, 0 };                                            // 特徴ベクトル生成(XORを意識している)
        /// 
        /// IDandLikelihood[] result = nnclass.ToIDandLikelihood(nnclass.Recognize(feature10), 3);  // 出力ベクトルをリレーさせて、構造体配列に変換している
        /// </code>
        /// </example>
        private IDandLikelihood[] GetIDandLikelihood(Vector outputVector, int productionVolume)
        {
            IDandLikelihood[] ans = new IDandLikelihood[productionVolume];
            double val = double.MaxValue;

            if (productionVolume == 0 || productionVolume > outputVector.Length) throw new SystemException("要求サイズが異常です。");
            for (int i = 0; i < productionVolume; i++)  // 大きい順に並び替えながら要素番号も把握する
            {
                double max = 0.0;
                int index = -1;
                for (int k = 0; k < outputVector.Length; k++)
                {
                    if (max < outputVector[k] && val > outputVector[k])
                    {
                        max = outputVector[k];
                        index = k;
                    }
                }
                val = max;
                if (this._IDnames != null && index != -1)
                    ans[i] = new IDandLikelihood(this._IDnames[index], max);
                else
                    ans[i] = new IDandLikelihood("Non", max);
            }
            return ans;
        }
 /// <summary>
 /// コピーコンストラクタ
 /// </summary>
 /// <param name="_vector">Vectorクラスオブジェクト</param>
 public Vector(Vector _vector)
 {
     if (_vector == null) throw new SystemException("オブジェクトはnullです。");
     this.Add(_vector.ToArray());
 }
        /// <summary>
        /// 判別表をCSV形式で保存する処理の本体です
        /// <para>正解率などの数値は引数で渡された結果を基に計算しています。</para>
        /// </summary>
        /// <param name="decisionChart">判定表</param>
        /// <param name="classForTest">識別対象の教師データモデル</param>
        /// <param name="format">文字出力フォーマット</param>
        /// <param name="fname">保存ファイル名</param>
        /// <param name="delimiter">区切り文字</param>
        /// <returns>文字列で表した判別表</returns>
        private string SaveDecisionChartBody(Vector[] decisionChart, List<Model> classForTest, string format = "0", string fname = "decision chart.csv", string delimiter = ",")
        {
            StringBuilder sb = new System.Text.StringBuilder(1000);

            // ラベルを出力
            sb.Append(delimiter);
            for (int j = 0; j < classForTest.Count; j++) sb.Append(delimiter + "output");
            sb.Append(System.Environment.NewLine);
            sb.Append(delimiter);
            for (int j = 0; j < classForTest.Count; j++) sb.Append(delimiter + classForTest[j].ID);
            sb.Append(System.Environment.NewLine);
            // 識別結果を出力
            for (int i = 0; i < decisionChart.Length; i++)
            {
                sb.Append("input" + delimiter + classForTest[i].ID);
                sb.Append(delimiter + decisionChart[i].ToString(format)).Append(System.Environment.NewLine);
            }
            // 個々の正答率を出力(あるモデルがそのモデルであると判別された割合)
            sb.Append(delimiter + "Accuracy Rate");
            for (int modelIndex = 0; modelIndex < classForTest.Count; modelIndex++)
            {
                double accuracyRate = decisionChart[modelIndex][modelIndex] / decisionChart[modelIndex].Total;
                sb.Append(delimiter + accuracyRate.ToString("f2"));
            }
            sb.Append(System.Environment.NewLine);
            double featureSize = 0.0;                                               // 特徴ベクトルの総数
            double rightAnswerSize = 0.0;                                           // 正答の総数
            for (int modelIndex = 0; modelIndex < classForTest.Count; modelIndex++)
            {
                featureSize += decisionChart[modelIndex].Total;
                rightAnswerSize += decisionChart[modelIndex][modelIndex];
            }
            // トータルでの正答率を出力
            double totalAccuracyRate = rightAnswerSize / featureSize;
            sb.Append(delimiter + "Total Accuracy Rate" + delimiter + totalAccuracyRate.ToString("f2")).Append(System.Environment.NewLine);

            using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fname, false, Encoding.UTF8))
            {
                sw.Write(sb.ToString());
            }
            return sb.ToString();
        }
 /// <summary>
 /// 指定されたクラス番号に応じた教師ベクトルを返す
 /// </summary>
 /// <param name="index">クラス要素番号</param>
 /// <returns>教師ベクトル</returns>
 private Vector CreateTeachingVector(int index)
 {
     Vector teachingVector = new Vector(this._class.Count);
     for (int i = 0; i < this._class.Count; i++)
     {
         if (i == index)
             teachingVector[i] = 1.0;
         else
             teachingVector[i] = 0.0;
     }
     return teachingVector;
 }
        /// <summary>
        /// 指定されたクラス名に応じた教師ベクトルを返す
        /// </summary>
        /// <param name="id">クラス名</param>
        /// <returns>教師ベクトル</returns>
        private Vector CreateTeachingVector(string id)
        {
            Vector teachingVector = new Vector(this._class.Count);

            for (int i = 0; i < this._class.Count; i++)
            {
                if (id == this._class[i].ID)
                    teachingVector[i] = 1.0;
                else
                    teachingVector[i] = 0.0;
            }
            return teachingVector;
        }
        /// <summary>
        /// 一通りの判別を行い、結果をヒストグラム(表:実態はVector[])にまとめる
        /// <para>閾値による棄却は行っておりません。最大値の出力を持ったクラスを識別器の出した答えだとして集計を行っています。</para>
        /// <para>並列演算による交差確認法の演算のために、引数としてニューラルネットのインスタンスを受け取るようにしています。</para>
        /// </summary>
        /// <param name="discriminator">識別器</param>
        /// <param name="classForTest">識別対象の教師データモデル</param>
        /// <returns>判別結果を表にまとめたもの(実態はVector[])</returns>
        /// <exception cref="SystemException">クラスモデルがセットされた状態でなければ例外をスロー</exception>
        private Vector[] CreateDecisionChart(NeuralNetwork discriminator, List<Model> classForTest)
        {
            if (this.Ready == false)
                throw new SystemException("実験の準備はまだ完了しておりません。モデルを追加して下さい。");
            if (discriminator == null)
                throw new Exception("判別器のインスタンスが確保されておらず、判別表を作ることができません。");

            Vector[] decisionChart = new Vector[classForTest.Count];
            for (int i = 0; i < decisionChart.Length; i++)
                decisionChart[i] = new Vector(this.Length);
            int modelIndex = 0;
            foreach (Model model in classForTest)                               // 保持しているモデルの数だけループ
            {

                Model.ReadSequenceMode backup = model.SequenceMode;
                model.SequenceMode = Model.ReadSequenceMode.InRotation;         // 重複を避けるため、特徴ベクトルの選出方法はローテーションとする
                // 特徴モデルを順番に呼び出す
                for (int count = 0; count < model.LengthForGetFeature; count++)
                {
                    Feature feature = model.GetFeature();                       // 特徴ベクトルをモデルから取得
                    Vector outputForDebug = discriminator.Recognize(feature);   // 識別
                    // 最大値となるインデックスを検索
                    double max = double.MinValue;
                    int maxIndex = 0;
                    for (int j = 0; j < outputForDebug.Length; j++)
                    {
                        if (max < outputForDebug[j])
                        {
                            max = outputForDebug[j];
                            maxIndex = j;
                        }
                    }
                    // 最大値となったインデックスについて、加算
                    decisionChart[modelIndex][maxIndex] += 1;
                }
                model.SequenceMode = backup;
                modelIndex++;
            }
            return decisionChart;
        }
        /// <summary>
        /// 交差確認法(Cross Validation)を用いた特徴ベクトルの評価を行います
        /// <para>確認結果をファイルとして出力します。ファイル名は引数で指定可能です。</para>
        /// <para>モデル選択はランダム、モデルからの特徴データの取得もランダムとします。</para>
        /// <para>本メソッドによる学習成果はModelクラスの保持する識別器へは反映されません。</para>
        /// <para>交差検定で使用する分割数は教師データ数に合わせて自動的に決定されます。</para>
        /// <para>コード可読性が低いのでいつか変えると思う。。。</para>
        /// </summary>
        /// <param name="learningTimes">一回の試行当たりの学習回数</param>
        /// <param name="checkNum">試行回数</param>
        /// <param name="fname">保存ファイル名</param>
        /// <returns>交差検定で使用した分割数<para>最大でも10となります。</para></returns>
        public int CV(int learningTimes, int checkNum, string fname = "decision chart.csv")
        {
            if (learningTimes == 0 || checkNum == 0)
                throw new Exception("Teacherクラスにおいてエラーがスローされました。CV()の引数の値が不正です。");
            var backupOfPom = this.PickOutMode;                                 // 各設定のバックアップを取る
            this.PickOutMode = Model.ReadSequenceMode.AtRandom;                 // Lean()での学習をランダムにする
            var backupOfMode = this.RandomModelChoice;
            this.RandomModelChoice = true;

            int divideLimit = int.MaxValue;
            foreach (Model model in this._class)
            {
                model.Shuffle();                                                // 学習に使用する教師データをシャッフル
                if (divideLimit > model.Length)
                    divideLimit = model.Length;                                 // モデル数に合わせて分割数を調整するために、最小の教師データ数を計る
                model.FeatureSupplyMode = Model.SupplyMode.NeglectParticularGroup;
            }
            if (divideLimit > 10)
                divideLimit = 10;                                               // 最大の分割数を10とする
            foreach (Model model in this._class)
                model.DivisionNum = divideLimit;
            var sequence = new Vector[this.Length, checkNum * divideLimit];     // 時系列の識別率を格納する
            Parallel.For(0, checkNum * divideLimit, i =>
            //for(int i = 0; i < checkNum * divideLimit; i++)
            {
                NeuralNetwork nn = new NeuralNetwork();                         // ニューラルネットのインスタンスを確保
                nn.Setup(this._NNet.GetParameter());
                var localModel = new List<Model>(0);
                int maskIndex = i % divideLimit;
                foreach (Model model in this._class)
                {
                    var cpyModel = new Model(model);
                    cpyModel.IndexForDivision = maskIndex;
                    cpyModel.FeatureSupplyMode = Model.SupplyMode.NeglectParticularGroup;       // 特定のグループを無視するモードに切り替える
                    localModel.Add(cpyModel);
                }
                this.Learn(learningTimes, nn, localModel);                                      // 学習させる
                foreach (Model model in localModel)
                {
                    model.FeatureSupplyMode = Model.SupplyMode.TakeParticularGroup;             // 特定のグループのみをテストするモードに切り替える
                }
                var tempResult = this.CreateDecisionChart(nn, localModel);                      // 学習に使わなかった教師データを用いた識別を行う
                for (int modelIndex = 0; modelIndex < this.Length; modelIndex++) sequence[modelIndex, i] = tempResult[modelIndex]; // 識別の結果を保存しておく
            });
            // 平均の識別率を計算し、ファイルへ保存する
            var averageDecisionChart = new Vector[this.Length];
            for (int modelIndex = 0; modelIndex < this.Length; modelIndex++)
                averageDecisionChart[modelIndex] = new Vector(this.Length);
            for (int i = 0; i < sequence.GetLength(1); i++)                                     // 平均の識別率を求めるために積算
                for (int modelIndex = 0; modelIndex < this.Length; modelIndex++)
                    averageDecisionChart[modelIndex] += sequence[modelIndex, i];
            for (int modelIndex = 0; modelIndex < this.Length; modelIndex++)                    // 平均の識別率を計算
                averageDecisionChart[modelIndex] /= ((double)checkNum * divideLimit);
            this.SaveDecisionChartBody(averageDecisionChart, this._class, "f1", fname);         // ファイルに保存
            // 識別率の標準偏差を計算し、ファイルへ保存する
            string fnameForDev = System.IO.Path.GetFileNameWithoutExtension(fname) + ".dev";    // 標準偏差を計算する準備
            var deviation = new Vector[this.Length];
            for (int modelIndex = 0; modelIndex < this.Length; modelIndex++)
                deviation[modelIndex] = new Vector(this.Length);
            for (int i = 0; i < sequence.GetLength(1); i++)                                     // 分散*(n-1)を計算
                for (int modelIndex = 0; modelIndex < this.Length; modelIndex++)
                    deviation[modelIndex] += (sequence[modelIndex, i] - averageDecisionChart[modelIndex]).Pow(2.0);
            for (int modelIndex = 0; modelIndex < this.Length; modelIndex++)                    // 標準偏差を求める
                deviation[modelIndex] = (deviation[modelIndex] / (double)(checkNum * divideLimit - 1)).Pow(0.5);
            using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fnameForDev, false, Encoding.UTF8))
            {
                for (int j = 0; j < this.Length; j++) sw.Write("," + this.ModelIDs[j].ToString() + "を出力"); // ラベル
                sw.Write("\n");
                for (int i = 0; i < deviation.Length; i++)
                {
                    sw.Write(this.ModelIDs[i].ToString() + "を入力");
                    sw.WriteLine("," + deviation[i].ToString("f2"));
                }
                sw.Write("\n");
            }
            // 後始末
            this.PickOutMode = backupOfPom;
            this.RandomModelChoice = backupOfMode;
            for (int modelIndex = 0; modelIndex < this.Length; modelIndex++)
            {
                this._class[modelIndex].FeatureSupplyMode = Model.SupplyMode.NonDivide;     // 分割しないように戻しておく
            }
            return divideLimit;
        }