/// <summary>
        /// Treina as redes de captação necessárias para identificar a melhor rede para cada um dos dias de previsao
        /// Por exemplo: se forem permitidos 30 dias de previsao, teremos 30 redes para cada papel
        /// </summary>
        /// <param name="configuracaoCaptacao"></param>
        internal static void Treinar(ConfiguracaoCaptacaoRedes configuracaoCaptacao)
        {
            //Seleciona os treinamentos por dia de previsao, sendo o input os dados de entrada da rede de previsao financeira e como saida,
            //a taxa de acerto de cada uma das redes executadas em cada dia de previsao
            //OBS: A taxa de acerto da rede por dia é alimentada
            Dictionary<int, TrainingSet> treinamentosPorDia = SelecionarTreinamentosPorDia(configuracaoCaptacao);//dadosTreinamentoPorShift, configuracaoCaptacao.RedesPrevisao);

            GerarRelatorioCrossOver(String.Format("{0}_{1}_{2}", Directory.GetFiles(diretorioRelatorioCrossOver).Count() + 1, configuracaoCaptacao.Papel, DateTime.Now.ToString().Replace("/", "_").Replace(":", "_".Replace(" ", ""))), configuracaoCaptacao.RedesPrevisao);

            //foreach (KeyValuePair<int, TrainingSet> treinamento in treinamentosPorDia)
            //{
            //    string nomeRede = CaptacaoMelhoresRedesRN.RecuperarNomeRede(configuracaoCaptacao.Papel, treinamento.Key);
            //    TreinarRedeDiaria(nomeRede, treinamento.Value);
            //}
            int numeroProcessadores = 4;
            for (int indTrein = 0; indTrein < treinamentosPorDia.Count; indTrein += numeroProcessadores)
            {
                Thread[] threads = new Thread[numeroProcessadores];
                for (int indProcessador = 0; indProcessador < numeroProcessadores; indProcessador++)
                {
                    if (treinamentosPorDia.Count - (indTrein + indProcessador) <= 0)
                        continue;
                    string nomeRede = CaptacaoMelhoresRedesRN.RecuperarNomeRede(configuracaoCaptacao.Papel, indTrein + indProcessador);
                    TrainingSet ts = treinamentosPorDia[indTrein + indProcessador];
                    threads[indProcessador] = new Thread(() => TreinarRedeDiaria(nomeRede, ts));//[indTrein + indProcessador];
                    threads[indProcessador].Start();
                }
                for (int indProcessador = 0; indProcessador < numeroProcessadores; indProcessador++)
                {
                    if (threads[indProcessador] != null)
                        threads[indProcessador].Join();
                }
            }
        }
        public void TreinarRedesCaptacao()
        {
            string papel = "PETR4";

            //Lista todas as configurações de rede para o papel
            List<string> redes = RedeNeural_PrevisaoFinanceira.RNAssessor.ListarRedes(papel);
            ConfiguracaoCaptacaoRedes configuracaoCaptacao = new ConfiguracaoCaptacaoRedes();

            //Recupera os dados do papel
            List<DadosBE> dadosBE = DataBaseUtils.DataBaseUtils.RecuperarCotacoesAtivo(papel);
            configuracaoCaptacao.Dados = DataBaseUtils.DataBaseUtils.NormalizarDados(dadosBE.Select(dadoBE => (double)dadoBE.PrecoAbertura).ToList(), papel);
            configuracaoCaptacao.Papel = papel;

            //Adicionar cada uma das redes ao treinamento de captação
            foreach (string nomeRede in redes)
            {
                //Network redeNeuralPrevisaoFinanceira = RedeNeural_PrevisaoFinanceira.RNAssessor.RecuperarRedeNeural(nomeRede);
                RedePrevisaoFinanceira rpf = new RedePrevisaoFinanceira();
                rpf.NomeRede = nomeRede;
                rpf.JanelaEntrada = Convert.ToInt32(nomeRede.Split(new string[] { "_je", "_js" }, StringSplitOptions.RemoveEmptyEntries)[1]);
                rpf.JanelaSaida = Convert.ToInt32(nomeRede.Split(new string[] { "_js", "_nn" }, StringSplitOptions.RemoveEmptyEntries)[1]);
                rpf.RedeNeuralPrevisaoFinanceiraPorDivisaoCrossValidation = RedeNeural_PrevisaoFinanceira.RNAssessor.RecuperarRedesNeuraisAgrupadasPorConfiguracao(nomeRede); ;

                configuracaoCaptacao.RedesPrevisao.Add(rpf);
            }

            //Treina a rede de captação para a identificação da melhor dere por dia para o papel
            CaptacaoMelhoresRedesRN.Treinar(configuracaoCaptacao);
        }
        /// <summary>
        /// Roda as redes para cada um dos treinamentos, separando os treinamentos por dia
        /// Calcula também a taxa média de acerto de cada rede neural por dia
        /// </summary>
        /// <param name="dadosTreinamento"></param>
        /// <param name="redesPrevisaoFinanceira"></param>
        /// <returns></returns>
        private static Dictionary<int, TrainingSet> SelecionarTreinamentosPorDia(ConfiguracaoCaptacaoRedes configuracaoCaptacao)
        {
            int tamanhoEntrada = configuracaoCaptacao.RedesPrevisao.Max(rp => rp.JanelaEntrada);
            List<Treinamento> treinamentos = DataBaseUtils.DataBaseUtils.SelecionarTreinamentos(configuracaoCaptacao.Dados, tamanhoEntrada, totalDiasPrevisao, 2);
            //Dictionary<int, Dictionary<List<double>, List<double>>> dadosTreinamentoPorShift = new Dictionary<int, Dictionary<List<double>, List<double>>>();
            ////Resgata o maior tamanho das entradas das redes neurais
            ////Cria o dicionario de input output por shift
            //for (int numShift = 0; numShift < quantidadeShifts; numShift++)
            //{
            //    //Seleciona oa dados antes do shift
            //    dadosTreinamentoPorShift.Add(numShift, SelecionarInput_Output(configuracaoCaptacao.Dados.Take((configuracaoCaptacao.Dados.Count / (quantidadeShifts - 1)) * numShift).ToList(), tamanhoEntrada));
            //    //Seleciona os dados depois do shift
            //    dadosTreinamentoPorShift[numShift].ToList().AddRange(SelecionarInput_Output(configuracaoCaptacao.Dados.Skip((configuracaoCaptacao.Dados.Count / (quantidadeShifts - 1)) * (numShift + 1)).ToList(), tamanhoEntrada));
            //}

            //Inicializa todos os dias da previsao
            for (int diaTreinamento = 0; diaTreinamento < totalDiasPrevisao; diaTreinamento++)
            {
                foreach (RedePrevisaoFinanceira redePrevisao in configuracaoCaptacao.RedesPrevisao)
                {
                    redePrevisao.TaxaMediaAcertoPorDia.Add(diaTreinamento, 0);
                }
            }

            Dictionary<int, TrainingSet> treinamentosPorDia = new Dictionary<int, TrainingSet>();
            for (int dia = 0; dia < totalDiasPrevisao; dia++)
            {
                treinamentosPorDia.Add(dia, new TrainingSet(tamanhoEntrada, configuracaoCaptacao.RedesPrevisao.Count));
            }

            //Roda as redes para cada um dos dados de treinamento selecionados
            foreach (Treinamento treinamento in treinamentos)
            {
                //Guarda um historico dos outputs das redes para tratar as redes que tem output menor do que o tamanho da previsao
                List<List<double>> outputsRedes = new List<List<double>>();
                List<double[]> taxasAcertoRedes = new List<double[]>();
                foreach (RedePrevisaoFinanceira redePrevisao in configuracaoCaptacao.RedesPrevisao)
                {
                    Network redeNeural = redePrevisao.RedeNeuralPrevisaoFinanceiraPorDivisaoCrossValidation[treinamento.DivisaoCrossValidation];
                    //Roda a rede neural, gerando a previsao                             \/ como a rede pode receber um input menor do que os treinamentos, pegamos apenas os ultimos registros do mesmo
                    double[] outputPrevisao = redeNeural.Run(treinamento.Input.Skip(treinamento.Input.Count - redePrevisao.JanelaEntrada).Take(redePrevisao.JanelaEntrada).ToArray());
                    //Alimenta o historico de previsoes de cada rede
                    outputsRedes.Add(outputPrevisao.ToList());
                    double[] taxaAcertoPorDia = new double[totalDiasPrevisao];
                    for (int diasPrevistosRN = 0; diasPrevistosRN < outputPrevisao.Length; diasPrevistosRN++)
                    {
                        //Calcula a taxa de acerto da previsao
                        double ta = Math.Min(outputPrevisao[diasPrevistosRN], treinamento.Output[diasPrevistosRN]) / Math.Max(outputPrevisao[diasPrevistosRN], treinamento.Output[diasPrevistosRN]);
                        taxaAcertoPorDia[diasPrevistosRN] = ta;
                        redePrevisao.TaxaMediaAcertoPorDia[diasPrevistosRN] += ta;
                    }
                    taxasAcertoRedes.Add(taxaAcertoPorDia);
                }

                //Lista com o melhor desultado de cada dia (cada item da lista corresponde a um dia..)
                List<double> melhoresResultadosDia = new List<double>(treinamento.Input);
                //Trata as taxas de acerto que não foram calculadas pois a rede tem output menor do que a quantidade de dias da previsao
                for (int dia = 1; dia < totalDiasPrevisao; dia++)
                {
                    //Recupera o indice da rede que tem a melhor taxa de acerto para o dia anterior
                    int indMelhorRedeParaODiaAnterior = taxasAcertoRedes.IndexOf(taxasAcertoRedes.OrderByDescending(taxasAcertoRede => taxasAcertoRede[dia - 1]).First());
                    //Adiciona o melhor resultado a lista de melhores resultados
                    melhoresResultadosDia.Add(outputsRedes[indMelhorRedeParaODiaAnterior][dia - 1]);
                    for (int indRede = 0; indRede < configuracaoCaptacao.RedesPrevisao.Count; indRede++)
                    {
                        //Verifica se a taxa de acerto do dia para a rede ja foi calculada
                        if (taxasAcertoRedes[indRede][dia] == 0)
                        {
                            //Ultiliza os ultimos melhores dados para fazer uma nova previsao
                            double[] inputRede = melhoresResultadosDia.Skip(melhoresResultadosDia.Count - configuracaoCaptacao.RedesPrevisao[indRede].JanelaEntrada).Take(configuracaoCaptacao.RedesPrevisao[indRede].JanelaEntrada).ToArray();
                            double[] outputRede = configuracaoCaptacao.RedesPrevisao[indRede].RedeNeuralPrevisaoFinanceiraPorDivisaoCrossValidation[treinamento.DivisaoCrossValidation].Run(inputRede);
                            //Adiciona o dia previsto na lista de outputs
                            outputsRedes[indRede].Add(outputRede.Last());
                            //Calcula a taxa de acerto da previsao
                            double ta = Math.Min(outputRede.Last(), treinamento.Output[dia]) / Math.Max(outputRede.Last(), treinamento.Output[dia]);
                            //Adiciona a taxa de acerto da rede para o dia
                            taxasAcertoRedes[indRede][dia] = ta;
                            //Atualiza a taxa de acerto da rede
                            configuracaoCaptacao.RedesPrevisao[indRede].TaxaMediaAcertoPorDia[dia] += ta;
                        }
                    }
                }

                for (int dia = 0; dia < totalDiasPrevisao; dia++)
                {
                    double[] outputCaptacao = new double[configuracaoCaptacao.RedesPrevisao.Count];
                    for (int indRede = 0; indRede < configuracaoCaptacao.RedesPrevisao.Count; indRede++)
                    {
                        outputCaptacao[indRede] = taxasAcertoRedes[indRede][dia];
                    }
                    TrainingSample ts = new TrainingSample(treinamento.Input.ToArray(), outputCaptacao);
                    treinamentosPorDia[dia].Add(ts);
                }
            }

            //Divide a taxa de acerto pela quantidade de treinamentos para saber a taxa média
            foreach (RedePrevisaoFinanceira redePrevisao in configuracaoCaptacao.RedesPrevisao)
            {
                for (int i = 0; i < totalDiasPrevisao; i++)
                {
                    redePrevisao.TaxaMediaAcertoPorDia[i] /= treinamentos.Count;
                }
            }

            return treinamentosPorDia;
        }