Пример #1
0
        /// <summary>
        /// Analyse l'arbre issu de la première étape et le réduit
        /// </summary>
        /// <param name="LeNoeud"></param>
        private List <Noeud> AnalyserEtReduire(ref List <Noeud> Noeuds)
        {
            /*
             * J'ai testé plusieurs algorithmes, celui retenu est le plus rapide que j'ai trouvé 58 secondes contre 18 minutes pour le plus lent.
             *
             * J'utilise des List<T> et non des IEnumerable, car chaque collection est évaluée plusieurs fois, au final le cast en List est un gain de temps important.
             *
             * Pour ne pas prendre en modèle, un(des) noeud(s) équivalent(s) à un noeud déjà dans le dawg2etapes, la solution la plus efficace est de retirer ce(s) noeud(s) à la collection d'origine, le fait d'enlever des items au fur et à mesure rend chaque requête de plus en plus rapide.
             * Malgré cela l'exécution prend encore plus de 10 minutes si on travaille sur la liste "noeuds", qui contient au départ plus de 786 000 noeuds.
             *
             * Le fait de faire un groupBy sur la propriété "Rang" fait tomber le temps d'éxécution de façon considérable (+ de 10 minutes à 1 minute)
             * En effet deux noeuds de même rang sont quasiement équivalents. Ils ont la même profondeur, le même nombre d'enfants et ont le même état terminal. Pour l'équivalence, il reste à comparer les arcs sortants, la destination des arcs évoluant au fur et à mesure de la réduction, cette dernière comparaison ne peut pas être anticipée
             * Ce groupement crée 3742 collections dont la plus importante contient tous les noeuds terminaux sans enfants (254645 noeuds) qui de fait sont équivalents, donc une seule itération suffit à la réduire.
             */
            AnnonceEtape("Début de la réduction de l'arbre en graphe.");
            int pourcent   = 1;
            int progress   = 0;
            int nbreNoeuds = Noeuds.Count;

            List <Noeud> dawg2etapes = new List <Noeud>();

            List <IGrouping <string, Noeud> > parProfondeur = Noeuds.GroupBy(n => ((Noeud)n).Rang).OrderBy(x => x.Key).ToList();

            foreach (IGrouping <string, Noeud> item in parProfondeur)
            {
                List <Noeud> lesNoeuds = item.ToList();
                while (lesNoeuds.Count > 0)
                {
                    Noeud model = (Noeud)lesNoeuds[0];//on prend en modèle le premier noeud (encore) disponible pour le rang en cours

                    dawg2etapes.Add(FusionEquivalents(model, ref lesNoeuds, ref progress));

                    int x = progress * 100 / nbreNoeuds;
                    if (pourcent < x)
                    {
                        pourcent = x;
                        AnnonceProgression(pourcent);
                    }
                }
            }

            //on réindexe les noeuds
            return(dawg2etapes.OrderBy(n => n.Numero).Select((n, i) => (Noeud)n.ReIndex(i + 1)).ToList());
        }
Пример #2
0
        /// <summary>
        /// Retourne le préfixe du nouveau mot, s'il existe déja dans le dictionnaire
        /// </summary>
        /// <param name="Mot">Mot à préfixer</param>
        /// <returns></returns>
        private List <Arc> ExtrairePrefixeCommun(string Mot, Noeud Noeud0)
        {
            List <Arc> prefixe = new List <Arc>();
            Noeud      enCours = Noeud0;

            for (int i = 0; i < Mot.Length; i++)
            {
                Arc a = enCours.Sortants.SingleOrDefault(aa => aa.Lettre == Mot[i] && aa.Destination.Entrants.Count == 1); //on recherche si le noeud en cours possède la lettre suivante comme arc sortant
                if (a == null)                                                                                             //si ça n'est pas le cas on sort du for.
                {
                    break;
                }

                prefixe.Add(a);
                enCours = a.Destination;
            }

            return(prefixe);
        }
Пример #3
0
 /// <summary>
 /// Retourne si le noeud est équivalent à celui en paramètre
 /// </summary>
 /// <param name="Autre"></param>
 /// <returns></returns>
 public bool IsEquivalent(Noeud Autre)
 {
     /* Pour être équivalents deux noeuds doivent:
      * -avoir la même profondeur (condition fixe 1)
      * -être tous les 2 terminaux ou pas (condition fixe 2)
      * -avoir le même nombre d'enfants (condition fixe 3) avec le groupement ces 3 conditions sont déjà vérifiée lors de la construction en 2 temps
      * -les arcs sortants doivent avoir les mêmes lettres allant vers les mêmes destinations.Dans la construction en 2 temps, les destinations étant redirigées au fur et à mesure de la reduction ctte condition sera évaluée au moment nécessaire
      */
     if (this.Profondeur == Autre.Profondeur && this.IsTerminal == Autre.IsTerminal && this.NbreEnfants == Autre.NbreEnfants)
     {
         IEnumerable <string> destinationsThis  = this.Sortants.Select(a => a.Serialize).Distinct().OrderBy(i => i);
         IEnumerable <string> destinationsAutre = Autre.Sortants.Select(a => a.Serialize).Distinct().OrderBy(i => i);
         return(destinationsThis.SequenceEqual(destinationsAutre));
     }
     else
     {
         return(false);
     }
 }
Пример #4
0
        /// <summary>
        /// Affecte la profodeur et le dernier enfant du nouveau suffixe et met à jour les noeuds "au-dessus"
        /// </summary>
        /// <param name="Final">Noeud Final du nouveau suffixe</param>
        private void MettreAjourProfondeur(Noeud Final, ref List <Noeud> Noeuds)
        {
            //A ce moment là le dictionnaire à encore la forme d'un arbre, donc chaque noeud ne possède qu'un arc entrant
            Noeud enCours = (Noeud)Final.Entrants[0].Origine;

            int i = 1;

            do
            {
                if (enCours.Profondeur < i)//si le noeud a une profondeur inférieure à la celle induite par le nouveau suffixe, on met à jour
                {
                    enCours.Profondeur = i;
                }

                i++;
                enCours.NbreEnfants = enCours.Sortants.Count() + enCours.Sortants.Sum(a => a.Destination.NbreEnfants);

                enCours = enCours != Noeuds[0] ? (Noeud)enCours.Entrants[0].Origine : null;
            } while (enCours != null);
        }
Пример #5
0
        /// <summary>
        /// Crée les arcs et les noeuds nécessaires à joindre le dernier noeud du préfixe commun au premier noeud du suffixe commun
        /// </summary>
        /// <param name="Prefixe">Dernier noeud du préfixe commum</param>
        /// <param name="Suffixe">Premier noeud du suffixe commun</param>
        /// <param name="ACreer">Lettres restant à créer</param>
        private void JoindreLesChemins(Noeud Prefixe, Noeud Suffixe, char[] ACreer)
        {
            Noeud enCours = Prefixe;

            for (int i = 0; i < ACreer.Length; i++)
            {
                char  lettre = ACreer[i];
                Noeud destination;
                if (i == ACreer.Length - 1)
                {
                    destination = Suffixe;
                }
                else
                {
                    destination = new Noeud(NombreNoeuds++);
                    dawg.Add(destination);
                }
                enCours.Sortants.Add(new Arc(lettre, enCours, destination));
                enCours = destination;
            }
        }
Пример #6
0
        /// <summary>
        /// Thread de la construction en 2 étapes
        /// </summary>
        private void leThread2Etapes()
        {
            List <Noeud> noeuds = ConstruireArbre();

            dawg = AnalyserEtReduire(ref noeuds);

            //Affectation du résultat
            DAWG = dawg[0];

            AnnonceEtape("Ecriture du fichier.");
            Noeud.Serialize(dawg, Mots.Count, NomDicoDawgODS7);
            //chrono.Stop();

            //==================Ici la construction est finie

            //Cette étape n'est utile que pour la démo et le débug
            ComparerListeMotsEtDAWG();


            TravailEnCours = DawgResolver.TravailEnCours.Aucun;
        }
Пример #7
0
 /// <summary>
 /// Retourne cet arc aprés avoir changé l'origine
 /// </summary>
 /// <param name="N">Nouvelle origine</param>
 /// <returns>Le même arc, pour pouvoir être utilisé dans une requête Linq</returns>
 public Arc SetOrigine(Noeud N)
 {
     Origine = N;
     return(this);
 }
Пример #8
0
 /// <summary>
 /// Retourne cet arc aprés avoir changé la destination
 /// </summary>
 /// <param name="N">Nouvelle destination</param>
 /// <returns>Le même arc, pour pouvoir être utilisé dans une requête Linq</returns>
 public Arc SetDestination(Noeud N)
 {
     Destination = N;
     return(this);
 }
Пример #9
0
        /// <summary>
        /// Ajoute un mot au dictionnaire
        /// </summary>
        /// <param name="mot">Mot à ajouté</param>
        /// <remarks>On vérifie d'abord que le mot n'est pas déjà présent</remarks>
        public void AjouterUnMot(string mot)
        {
            //chrono.Restart();
            TravailEnCours = DawgResolver.TravailEnCours.AjoutMot;

            mot = mot.ToUpper();//au cas ou il soit en minuscule

            if (!MotAdmis(mot))
            {
                Noeud  aRejoinde = DAWG;
                string suffixe   = mot;

                List <Arc> prefixe  = ExtrairePrefixeCommun(mot, DAWG);
                int        longueur = prefixe.Count;

                if (longueur > 0)//si un suffixe existe, on continue à partir de là
                {
                    AnnonceEtape(string.Format("Préfixe trouvé \"{0}\".", Arc.RetourneMot(prefixe)));
                    aRejoinde = prefixe.Last().Destination;
                    suffixe   = mot.Substring(longueur);
                }
                else
                {
                    AnnonceEtape("Pas de préfixe trouvé");//théoriquement ça n'est pas possible, en effet, il n'existe aucune lettre qui ne commence aucun mot dans le dictionnaire ASCII
                }
                List <Arc> arcsSuffixe = new List <Arc>();

                //On part du noeud terminal sans enfants et on remonte le graphe pour trouver le suffixe commun
                Noeud enCours = dawg.Single(n => n.Sortants.Count == 0 && n.IsTerminal);
                int   i;
                for (i = suffixe.Length - 1; i > -1; i--)
                {
                    char lettre = suffixe[i];

                    Arc arc = enCours.Entrants.SingleOrDefault(a => a.Lettre == lettre && !a.Origine.IsTerminal && a.Origine.Sortants.Count == 1);

                    if (arc == null)//il n'y a plus de chemin possible
                    {
                        break;
                    }


                    arcsSuffixe.Insert(0, arc);
                    enCours = arc.Origine;
                }

                AnnonceEtape(string.Format("Suffixe trouvé \"{0}\".", Arc.RetourneMot(arcsSuffixe)));
                JoindreLesChemins(aRejoinde, enCours, suffixe.Take(i + 1).ToArray());

                Mots.Add(mot);

                AnnonceEtape("Ecriture du nouveau fichier DAWG.");
                Noeud.Serialize(dawg, Mots.Count, "DawgEn2Temps.txt");
                AnnonceEtape("Mot ajouté et fichier enregistré.");
            }
            else
            {
                AnnonceEtape(string.Format("Le mot \"{0}\" existe déja.", mot));
            }


            //chrono.Stop();



            //==================Ici l'ajout du mot est fini

            //Cette étape n'est utile que pour la démo et le débug
            ComparerListeMotsEtDAWG();


            TravailEnCours = DawgResolver.TravailEnCours.Aucun;
        }
Пример #10
0
        /// <summary>
        /// Charge le DAWG compressé dans un fichier.
        /// Ensuite compare la liste de mots avec celle du fichier ASCII
        /// </summary>
        /// <param name="FileName">Adresse du fichier compressé</param>
        /// <returns>DAWG</returns>
        public void ChargerFichierDAWG(string nomdico)
        {
            TravailEnCours = DawgResolver.TravailEnCours.ChargementFichierDAWG;

            //chrono.Restart();
            AnnonceEtape("Début du chargement du dictionnaire DAWG.");
            var    assembly     = Assembly.GetExecutingAssembly();
            string resourceName = assembly.GetManifestResourceNames().Single(str => str.EndsWith(nomdico));

            using (Stream stream = assembly.GetManifestResourceStream(resourceName))
                using (StreamReader reader = new StreamReader(stream, true))
                {
                    string   content = reader.ReadToEnd();
                    string[] lignes  = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
                    //string[] lignes = File.ReadAllLines(FileName);
                    //le nombre de mots est présent dans le fichier par soucis de compatibilité avec le tutoriel de CArlVB, mais il n'est pas utile
                    NombreNoeuds = lignes.Count() - 2; // Convert.ToInt32(lignes[1].Split(':')[1]);

                    /*On connait à l'avance le nombre de noeuds car c'est écrit en entête par soucis de compatibilité avec le tutoriel de CArlVB
                     * Cependant, on aurait pu le déduire à partir de lignes.Length
                     * Utiliser un tableau permet de regarder au bon index si le noeud a déjà été créé par un arc entrant ou s'il faut le faire
                     */
                    Legacy = new int[28, NombreNoeuds + 1];
                    Noeud[] noeuds = new Noeud[NombreNoeuds];

                    for (int i = 0; i < NombreNoeuds; i++)
                    {
                        if (noeuds[i] == null)            //on vérifie si le noeud à déserialiser n'a pas déjà été créé par un arc
                        {
                            noeuds[i] = new Noeud(i + 1); //on l'initialise si nécessaire
                        }
                        Noeud n = noeuds[i];

                        string ligne = lignes[i + 2];//on lit la ligne correspondante, il faut penser à sauter les 2 lignes d'entête

                        n.IsTerminal         = ligne.EndsWith("#");
                        Legacy[27, n.Numero] = n.IsTerminal ? 1 : 0;
                        if (ligne != "#")//on exclu le cas particulier du noeud terminal sans enfant
                        {
                            //on désérialize les arcs sortants
                            string[] arcs = ligne.Replace("#", "").Split("-".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

                            n.Sortants = (from a in arcs
                                          select new Arc(n, a, noeuds)
                                          ).ToList();
                            foreach (var s in n.Sortants)
                            {
                                Legacy[(int)s.Lettre - AscShift, s.Origine.Numero] = s.Destination.Numero;
                            }
                        }
                    }

                    DAWG = noeuds[0];
                    dawg = noeuds.ToList();

                    AnnonceEtape("Fin du chargement du dictionnaire DAWG.");

                    //chrono.Stop();

                    //==================Ici la lecture du fichier est finie

                    //Cette étape n'est utile que pour la démo et le débug
                    //ComparerListeMotsEtDAWG();

                    TravailEnCours = DawgResolver.TravailEnCours.Aucun;

                    //return dawg[0];
                }
        }