IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Algorithmes et structures de données Discussion :

Tri rapide - un nouvel algorithme pour concurrencer Quick Sort ?


Sujet :

Algorithmes et structures de données

  1. #1
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut Tri rapide - un nouvel algorithme pour concurrencer Quick Sort ?
    Bonjour à vous tous.
    J'ai développé un algorithme de tri qui me semble assez performant.
    D'après mes tests, il est même plus rapide que QSort sur les chaines de caractères volumineuses.
    Mais il est écrit en VBA et je voudrais savoir s'il est aussi performant dans d'autres langages, par exemple en C, que je ne maitrise pas.
    Je recherche donc des volontaires piqués par la curiosité.
    Je vous renvoie à cette documentation qui explique tout : https://hal.archives-ouvertes.fr/hal-01154432/document
    Le fichier EXCEL source (et aussi pour faire vos tests) est en pièce jointe dans : https://hal.archives-ouvertes.fr » (recherchez avec le mot clé : QuickRanking).

    Merci d'avance pour vos remarques.

  2. #2
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2008
    Messages
    26 772
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2008
    Messages : 26 772
    Par défaut


    Selon le point de vue, ce n'est pas difficile d'être plus rapide que le tri rapide : sa complexité algorithmique en temps dans le pire cas est Formule mathématique, alors que la complexité optimale du tri est plutôt Formule mathématique ; si tu connais un peu les données à trier, tu peux choisir un algorithme qui fonctionne mieux sur ce type de données, voire en écrire une variante (par exemple, si tes données sont à peu près triées : http://www.geeksforgeeks.org/nearly-sorted-algorithm/).

    As-tu effectué une analyse plus théorique de la performance de ton algorithme ? L'avantage d'une telle analyse, c'est qu'elle permet de comparer des algorithmes en ignorant les questions de langage : si la complexité est faible, tu peux t'attendre à avoir un algorithme plus rapide… sur de très grands volumes de données (analyse asymptotique). (En tout cas, je n'en vois pas de trace dans ton document, pas même le mot "complexité".)
    Vous souhaitez participer aux rubriques Qt (tutoriels, FAQ, traductions) ou HPC ? Contactez-moi par MP.

    Créer des applications graphiques en Python avec PyQt5
    Créer des applications avec Qt 5.

    Pas de question d'ordre technique par MP !

  3. #3
    Membre Expert
    Homme Profil pro
    sans emploi
    Inscrit en
    Janvier 2014
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2014
    Messages : 539
    Par défaut
    Bonjour,
    pour affiner les dires de Dourouc, il parle de complexité de type temporelle en terme de comparaisons, ce qui ne concerne que les algorithmes de tri par comparaison entre éléments. Il existe des algorithmes de tri qui ne sont pas des algorithmes de tri par comparaison entre élément → bucket sort, radix sort par exemple, leur complexité temporelle est en O(n).

    Tu t'es donné du mal pour écrire ton article, mais je suis frustré. Je ne vois aucun algorithme. Je vois un exemple, du code VBA (beaucoup de code VBA) mais aucun algorithme.
    La première chose à faire est sans doute de donner le pseudo code de ton algo, car sinon c'est très rébarbatif …

  4. #4
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut
    Merci pour ta réponse.
    En effet je ne suis pas mathématicien et je suis incapable d'expliquer cet algorithme en ces termes. Ce qui nuit à sa notoriété car la lecture du document donne l'impression d'un amateurisme ridicule. Je vous remercie de me pardonner cette faiblesse et de passer outre.

    Pour répondre à ta deuxième remarque, je propose ici un algorithme qui est générique, pas spécifique à une situation donnée, un passe partout : "liste idéalement aléatoire ou liste fortement classée, données volumineuses ou de taille réduite données numériques ou alphanumériques, la réalité se situe souvent entre ces extrêmes".

    Enfin, j'ai simplifié un peu la présentation, car c'est sûr qu'il est "facile" d'être plus rapide que QSort sur des chaînes de caractères (surtout si elles sont déjà partiellement triées), sauf que j'ai aussi comparé mon algorithme avec la version de QSort optimisée pour ce traitement : "QuickSort_AndRank, qui ne déplace pas directement les données mais déplace les références des indices des données. D’où un gain de temps sur les traitements des données alphanumériques volumineuses car l’on déplace des entiers de 4 octets et non plus des chaînes de caractères de longueurs variables".

    Pour répondre à ta remarque, je ne pense pas que la seule notion de complexité théorique suffise. Car d'après mon expérience, ce qui prend du temps de traitement, c'est aussi l'usage des conditions (if Then, Do Loop, etc.), et le déplacement des variables.
    Par exemple, pour la recherche dichotomique, dans un premier temps j'ai utilisé une boucle "Do... Loop While". Mais une boucle "For i = 1 to log(n)/log(2)... Next" est bien plus rapide. Pour une même complexité théorique.

    Du moins en VBA. Voir la synthèse de la page 16. Est-ce le cas en C ? C'est ce que je souhaiterais savoir.

  5. #5
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Bonjour, de ce que j'ai vu ton algorithme est une simple variante du tri par insertion, dans laquelle on utilise une liste chaînée plutôt que de permuter les éléments. J'ai le regret de t'apprendre qu'il n'y a là rien de bien neuf.

    Et le tri par insertion est justement connu pour être efficace lorsque les données sont déjà partiellement triées.


  6. #6
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 489
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 489
    Par défaut
    salut

    en regardant le programme vba tout ceci est bien confus des boucle dans tout les sens des goto en veut tu en voila
    je ne suis vraiment pas certain que le tout soit d'une optimisation total

    Tu Fait des recherche dichotomique tu scinde tes donné en sous éléments tout ceci ressemble fortement a un trie fusion si je me souvient bien

  7. #7
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut
    ça ressemble à un tri par fusion, oui. Ça ressemble ainsi à un tri par indexation. Mais alors comment expliquez vous sa rapidité. Quant aux GOTO, c'est justement pour gagner en vitesse, même si ça ne fait pas joli ni professionnel. Et le plus c'est que l'algorithme retourne le classement, en conservant l'ordre d'origine en cas d'égalités. Je reconnais qu'il ne donne pas envie d'y passer du temps pour l'implémenter en C. Merci pour vos remarques très pertinentes. je réfléchis à vous faire un pseudo code même si je n'y connais rien.

  8. #8
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Citation Envoyé par laurent_ott Voir le message
    ça ressemble à un tri par fusion, oui. Ça ressemble ainsi à un tri par indexation. Mais alors comment expliquez vous sa rapidité.
    Comme je te l'ai expliqué, le tri par insertion est très efficace pour les données partiellement triées.

    Et j'insiste : d'après ton PDF ça ne ressemble pas seulement à un tri par insertion, c'est bel et bien un tri par insertion. Je n'ai pas regardé le code en revanche.

    Citation Envoyé par laurent_ott Voir le message
    "QuickSort_AndRank, qui ne déplace pas directement les données mais déplace les références des indices des données. D’où un gain de temps sur les traitements des données alphanumériques volumineuses car l’on déplace des entiers de 4 octets et non plus des chaînes de caractères de longueurs variables".
    Un type chaîne de caractères est en fait une référence vers une chaîne. La chaîne n'est donc pas copiée, seulement sa référence qui fait 4 octets en 32 bits (8 en 64).

    Pour répondre à ta remarque, je ne pense pas que la seule notion de complexité théorique suffise.
    En général c'est vrai : il faut aussi regarder les motifs des accès mémoire. Mais dans le cas présent la performance de ton algorithme s'explique grâce à la seule complexité théorique sur des listes partiellement triées.

    Car d'après mon expérience, ce qui prend du temps de traitement, c'est aussi l'usage des conditions (if Then, Do Loop, etc.), et le déplacement des variables.
    En général ce sont les accès mémoire qui dominent les performances. Les branchements conditionnels ne sont plus tellement importants sur les CPU récents.

    Par exemple, pour la recherche dichotomique, dans un premier temps j'ai utilisé une boucle "Do... Loop While". Mais une boucle "For i = 1 to log(n)/log(2)... Next" est bien plus rapide. Pour une même complexité théorique.
    Un léger gain aurait été possible, un fort gain paraît plus douteux. Je t'encourage à vérifier.

  9. #9
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut
    Je comprends votre scepticisme, car j'imagine que dans ce forum consacré à l'algorithmie, vous devez souvent avoir des débutants qui trouvent un algorithme de tri révolutionnaire, et que vous êtes blasés. Mais malgré les apparences (trompeuses) cet algorithme, j'en suis persuadé, tient la route. Je vous renvoie à la page 16 du PDF qui compare des données sous diverses formes, numériques ou non, partiellement classées ou non. Comparaisons faite avec/contre QSort, (même dans sa version optimisée pour les chaînes de caractères) qui à mon avis reste une référence incontournable quand l'on parle des algorithmes de tri.
    Laissez moi un peu de temps pour vous apporter des preuves : tout ceci date de plus de deux ans et entre temps je suis passé à autre chose, alors il faut que je m'y replonge. Et si vous avez de cas de test à me proposez je suis preneur et prêt à relever vos défis.
    En tout cas, je suis très honoré de l’intérêt que vous portez à cette discussion, et du temps que vous me consacrez, et vous remercie pour vos remarques.

  10. #10
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Bien sûr qu'il tient la route puisque c'est un tri par insertion.

    Je ne nie pas les qualités de ton algorithme, seulement son originalité : c'est un tri par insertion via une liste chaînée.

  11. #11
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    C'est bien un tri par insertion avec liste chaînée.

    La variante que tu as introduit, c'est ton astuce pour ne pas avoir à toujours remonter la lise chaînée : une fois de temps à autre tu copies dans un tableau les éléments triés afin de pouvoir faire une recherche dichotomique de la borne supérieure depuis laquelle remonter la liste chaînée.

    Note que puisque cet algorithme n'a d'intérêt que pour les listes quasiment triées (QuickSort étant plus rapide dans l'autre cas), il serait peut-être plus astucieux de revenir au tri par insertion basique, sans liste chaînée. En effet la plupart des insertions seront plus rapides (pas de liste chaînée à maintenir) et les copies ne seront faîtes que pour les éléments pas à leur place, et elles engendreront des accès mémoire davantage prédictibles.

    Mais ton algorithme est peut-être plus efficace que le tri par insertion de base lorsque la liste est partiellement triée mais pas tant que ça. Dans ce mince intervalle cet algorithme pourrait être utile. A voir... Cela dit je serais surpris que ta variante soit nouvelle.


    En termes de complexité, ça donne avec P = Nombre_Passages :
    C(n) = coût des copies + coût des recherches dichotomiques + coût des recherches linéaires + coût des conditions d'entrée
    C(n) = n.n / 2P + n.log(n) + n.P / 2 + n

    Mais si l'on présume que seule une fraction k des éléments sont non-triés, ça donne alors :
    C(n, k) = (n.n / 2P + n.log(n) + n.P / 2) * k + n

    A comparer au tri par insertion standard :
    C(n, k) = (n.n / 2) * k + n


    Enfin tu dois avoir un bogue au niveau de la recherche dichotomique : à l'issue de celle-ci "i" se réfère à un indice dans TableauTps alors que tu voudrais l'indice correspondant dans T. Il te faut un second tableau pour stocker les correspondances. Tu ne l'as pas vu car tu n'as pas fait de test sur des séries aléatoires.

    Dans le même genre, certains cas particuliers peuvent briser la stabilité de ton tri. Il faut que tu modifies ta recherche dichotomique pour utiliser "T(n) >= TableauTps(i)" plutôt que ">". En effet tu veux partir du premier élément supérieur, et non pas du premier élément supérieur ou égal.

  12. #12
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut
    J'ai corrigé le pseudocode qui était un peu trop simplifié :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    Procédure QuickRanking(Tableau T)
     
    ' Classe le 2 premiers éléments du tableau T par ordre croissant:
    Si T(1) < T(2) Alors
    <div style="margin-left:40px">    mini <- 1
        Indice_Suivant(1) <- 2
        maxi <- 2
        Indice_Suivant(2) <- 2</div>Sinon
       <div style="margin-left:40px"> mini <- 2
        Indice_Suivant(2) <- 1
        maxi <- 1
        Indice_Suivant(1) <- 1</div>Fin Si
     
    ' Boucle sur les n autres éléments de tableau T:
    Pour n de 3 à Taille de T
     
    <div style="margin-left:40px">    ' Si la valeur de n est inférieure ou égale à la valeur minimale:
        Si T(n) <= T(mini) Alors
     <div style="margin-left:40px">       Indice_Suivant(n) <- mini
            mini <- n</div>    Fin si</div>
    <div style="margin-left:40px">    ' Si la valeur de n est supérieure ou égale à la valeur maximale:
        Si T(n) >= T(maxi) Alors
    <div style="margin-left:40px">        Indice_Suivant(maxi) <- n
            maxi <- n
            Indice_Suivant(n) <- n</div>    Fin Si</div>    
    <div style="margin-left:40px">    ' Autres cas:
     
        ' Faire la mise à jour du tableau temporaire "TableauTps" des données déjà classées
        ' si le nombre de passages infructueux est trop élevé :
        Si Nombre_Passages > n Ou Nombre_Passages = 0 Alors
    <div style="margin-left:40px">        ' Alimenter le tableau des données déjà classées:
            i <- mini
            Pour j de 1 à n - 1
    <div style="margin-left:40px">            TableauTps(j) <- i
                i <- Indice_Suivant(i)</div>        Fin Pour
    Taille <- j - 1
            Nombre_Passages <- 1</div>Fin Si</div>    
    <div style="margin-left:40px">    ' Recherche Dichotomique de T(n) dans le tableau des données déjà classées:
        Début <- 1
        Fin <- Taille
        Pour j de 2 à Entier( (Log(n) / Log(2)) )
    <div style="margin-left:40px">        i <- (Début + Fin) / 2
            Si T(n) > T(TableauTps(i)) Alors
    <div style="margin-left:40px">            Début <- i</div>        Sinon
    <div style="margin-left:40px">            Fin <- i</div>        Fin si</div>    Fin Pour</div>    
    <div style="margin-left:40px">    ' Ce qui donne la plus proche données inférieure connue:
        i <- TableauTps(Début)</div>    
    <div style="margin-left:40px">    ' Et permet ainsi de trouver la donnée exacte:
        Faire
    <div style="margin-left:40px">        j <- i ' dernière solution
            i <- Indice_Suivant(i) ' Indice suivant
            Nombre_Passages <- Nombre_Passages + 1 ' Nombre de passages infructueux.</div>    Boucle tant que T(n) > T(i)</div>    
    <div style="margin-left:40px">    ' Mise à jour des indices suivants:
        Indice_Suivant(n) <- Indice_Suivant(j) ' Qui est la donnée suivante de n.
        Indice_Suivant(j) <- n '  n devient la donnée suivante de l'ancien élément.</div>
    Fin Pour
    Fin Procédure
    Je suis d'accord avec toi, c'est un tri basé sur le principe d'une liste chaînée avec mise à jour de deux indices par itération.
    C'est très efficace (et pas seulement sur les listes partiellement triées) car cela évite le déplacement des variables. Surtout sur les alphanumériques.
    Mais tu dois déjà savoir tout cela.
    Je vais me pencher sur ton calcul de la complexité.
    Merci encore pour toutes tes remarques qui sont très intéressantes.

  13. #13
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Pour ceux qui ne voudraient pas entrer dans les détails, en substance ça donne cela :
    On initialise une liste chaînée vide qui contiendra les éléments triés.
    On initialise un tableau de recherche dichotomique initialement vide qui contiendra les noeuds triés de la liste chaînée, mais qui ne sera mis à jour que sporadiquement.
    
    Pour chaque élément x
         On teste d'abord le cas rapide où x doit être inséré en début ou fin de liste. Si c'est le cas on agit et on saute à l'élément suivant. Sinon...
    
         Une fois toutes les N insertions, on rafraîchit le tableau de recherche dichotomique en copiant tous les éléments triés jusque là.
         Dans ce tableau on cherche par dichotomie le plus petit noeud supérieur à x de la liste chaînée.
         Depuis ce noeud on remonte la liste chaînée jusqu'à trouver la position d'insertion à jour.
         On insère x à cette place dans la liste chaînée.
    Fin

    A comparer au tri par insertion normal :
    Pour chaque élément x
         On remonte les éléments déjà triés jusqu'à trouver la position d'insertion, tout en décalant les éléments supérieurs d'un cran.
         On insère x à cette place.
    Fin

  14. #14
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut
    Au lieu de "on insère x à cette place dans la liste chaînée" je préfère "on met à jour 2 indices dans la liste chaînée : Indice suivant de n est l'indice suivant de j, indice suivant de j est n. (j est l'indice trouvé à l'étage précédente). Le terme "insérer" pourrait être interprété comme un déplacement des indices de la chaîne, comme dans un tri par insertion, ce qui n'est pas le cas.

  15. #15
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut
    Bonjour. il manquait des lignes dans le Pseudo Code permettant la mise à jour du tableau temporaire utilisé par la recherche dichotomique, et donc un tri correct.
    Voici le code corrigé:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    ' Classe le 2 premiers éléments du tableau T par ordre croissant:
    si T(1) < T(2) Alors
        Mini = 1
        Indice_Suivant(1) = 2
        Maxi = 2
        Indice_Suivant(2) = 2
    Sinon
        Mini = 2
        Indice_Suivant(2) = 1
        Maxi = 1
        Indice_Suivant(1) = 1
    Fin si
     
    ' Initialise la taille du tableau temporaire TableauTps" des données déjà classées
    TailleTps = 1
     
    ' Boucle sur les n autres éléments de tableau T:
    Pour n = 3 jusqu'à Taille de (T)
     
        ' Si la valeur de n est inférieure ou égale à la valeur minimale:
        Si T(n) <= T(Mini)
            Indice_Suivant(n) = Mini
            Mini = n
            TableauTps(1) = n
        Fin si
     
     
        ' Si la valeur de n est supérieure ou égale à la valeur maximale:
        Si T(n) >= T(Maxi)
            Indice_Suivant(Maxi) = n
            Maxi = n
            Indice_Suivant(n) = n
            TailleTps = TailleTps + 1
            TableauTps(TailleTps) = n
        Fin Si
     
        ' Autres cas:
     
            ' Faire la mise à jour du tableau temporaire "TableauTps" des données déjà classées
            ' si le nombre de passages infructueux est trop élevé :
            Si Nombre_Passages > n OU Nombre_Passages = 0 Alors
            ' Alimenter le tableau des données déjà classées:
                i = Mini
                Pour j = 1 jusqu'à n - 1
                    TableauTps(j) = i
                    i = Indice_Suivant(i)
                Fin pour
                TailleTps = j - 1
                Nombre_Passages = 1
            Fin Si
     
            ' Recherche Dichotomique de T(n) dans le tableau des données déjà classées:
            Début = 1
            Fin = TailleTps
            Pour j = 1 Jusqu'à Log(n) / Log(2)
                i = (Début + Fin) / 2
                Si T(n) > T(TableauTps(i)) Alors
                    Début = i
                Sinon
                    Fin = i
                Fin Si
            Fin Pour
     
            ' Ce qui donne la plus proche données inférieure connue:
            i = TableauTps(Début)
     
            ' Et permet ainsi de trouver la donnée exacte:
            Faire
            j = i ' dernière solution
            i = Indice_Suivant(i) ' Indice suivant
            Nombre_Passages = Nombre_Passages + 1 ' Nombre de passages infructueux.
            Boucle tant que T(n) > T(i)
     
            ' Mise à jour des indices suivants:
            Indice_Suivant(n) = Indice_Suivant(j) ' Qui est la donnée suivante de n.
            Indice_Suivant(j) = n ' n devient la donnée suivante de l'ancien élément.
     
    Fin Pour
    Et le code en VBA non optimisé pour ceux qui veulent tester:

    Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    '-------------------------------------------------------------------------------
    Sub Test()
    '-------------------------------------------------------------------------------
    ' Déclaration des variables:
    Dim T() As Variant
    Dim y As Long, i As Long
     
    ' Lecture des données à trier en colonne A lignes 2 à suivantes:
    y = 2: i = 0
    While Cells(y, 1) <> ""
        i = i + 1
        ReDim Preserve T(1 To i)
        T(i) = Cells(y, 1)
        y = y + 1
    Wend
     
    ' Lance le tri:
    Call QuickRanking(T)
     
    ' Affiche les données triée en colonne B lignes 2 à suivantes:
    y = 2
    For i = 1 To UBound(T())
        Cells(y, 2) = T(i)
        y = y + 1
    Next i
     
    End Sub
     
    '-------------------------------------------------------------------------------
    Sub QuickRanking(T() As Variant)
    '-------------------------------------------------------------------------------
    ' Déclaration des variables:
    Dim Nombre_Passages As Long, TailleTps As Long
    Dim Mini As Long, Maxi As Long, n As Long, i As Long, j As Long
    Dim Début As Long, Fin As Long
    ' Déclaration et dimensionnement des tableaux:
    ReDim Indice_Suivant(1 To UBound(T)) As Long
    ReDim TableauTps(1 To UBound(T)) As Long
     
    ' Classe le 2 premiers éléments du tableau T par ordre croissant:
    If T(1) < T(2) Then
        Mini = 1
        Indice_Suivant(1) = 2
        Maxi = 2
        Indice_Suivant(2) = 2
    Else
        Mini = 2
        Indice_Suivant(2) = 1
        Maxi = 1
        Indice_Suivant(1) = 1
    End If
     
    ' Initialise la taille du tableau temporaire TableauTps" des données déjà classées
    TailleTps = 1
     
    ' Boucle sur les n autres éléments de tableau T:
    For n = 3 To UBound(T)
     
        ' Sélectionne l'élément n du tableau T et analyse les différents cas:
        Select Case T(n)
     
        ' Si la valeur de n est inférieure ou égale à la valeur minimale:
        Case Is <= T(Mini)
            Indice_Suivant(n) = Mini
            Mini = n
            TableauTps(1) = n
     
        ' Si la valeur de n est supérieure ou égale à la valeur maximale:
        Case Is >= T(Maxi)
            Indice_Suivant(Maxi) = n
            Maxi = n
            Indice_Suivant(n) = n
            TailleTps = TailleTps + 1
            TableauTps(TailleTps) = n
     
        ' Autres cas:
        Case Else
     
            ' Faire la mise à jour du tableau temporaire "TableauTps" des données déjà classées
            ' si le nombre de passages infructueux est trop élevé :
            If Nombre_Passages > n Or Nombre_Passages = 0 Then
            ' Alimenter le tableau des données déjà classées:
                i = Mini
                For j = 1 To n - 1
                    TableauTps(j) = i
                    i = Indice_Suivant(i)
                Next j
                TailleTps = j - 1
                Nombre_Passages = 1
            End If
     
            ' Recherche Dichotomique de T(n) dans le tableau des données déjà classées:
            Début = 1
            Fin = TailleTps
            For j = 1 To Int((Log(n) / Log(2)))
                i = (Début + Fin) / 2
                If T(n) > T(TableauTps(i)) Then
                    Début = i
                Else
                    Fin = i
                End If
            Next j
     
            ' Ce qui donne la plus proche données inférieure connue:
            i = TableauTps(Début)
     
            ' Et permet ainsi de trouver la donnée exacte:
            Do
            j = i ' dernière solution
            i = Indice_Suivant(i) ' Indice suivant
            Nombre_Passages = Nombre_Passages + 1 ' Nombre de passages infructueux.
            Loop While T(n) > T(i)
     
            ' Mise à jour des indices suivants:
            Indice_Suivant(n) = Indice_Suivant(j) ' Qui est la donnée suivante de n.
            Indice_Suivant(j) = n ' n devient la donnée suivante de l'ancien élément.
     
        End Select
     
    Next n
     
    ' Copie le tableau T dans le tableau temporaire:
    For i = 1 To UBound(T)
        TableauTps(i) = T(i)
    Next i
     
    ' Retourne T classé:
    i = Mini
    For j = 1 To UBound(T)
        T(j) = TableauTps(i)
        i = Indice_Suivant(i)
    Next j
     
    End Sub

    Je pense que ma question était mal formulée, donc difficile d'y répondre.
    Et que ce forum n'est peut-être pas celui que j'aurais dû contacter.
    Merci encore pour vos remarques et votre implication.

  16. #16
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 489
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 489
    Par défaut
    salut

    si je lit bien ton algo tu parcours 3 fois le tableau T ?

  17. #17
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut
    Bonjour anapurna.
    Je ne comprends pas bien ta question. J'essaie quand même d'y répondre:
    En fait, à l'origine, je cherchais à faire un algorithme qui ne trie pas uniquement, mais surtout, qui retourne l'ordre de classement, et plus précisément, en respectant l'ordre d'origine quand il y a des doublons. C'est utile dans certaines situations (c'est expliqué dans le lien de mon premier message).
    Finalement j'ai écrit un algorithme en VBA, QuickRanking, qui me semble plus rapide que QuickSort pour trier et surtout pour retourner l'ordre de reclassement des données alphanumériques, même sur les données aléatoires (bien-sûr, sinon c'est "trop" facile). Sur les données numériques QuickSort reste plus rapide, je le reconnais (même que je l'utilise partiellement dans mon algorithme).
    Puis j'ai lu que "Visual Basic (VBA) est critiqué pour sa gestion mémoire peu performante", et comme cet algorithme repose en grande partie sur la gestion de la mémoire, je voulais savoir s'il serait aussi performant dans un autre langage, comme en C par exemple.
    Mais la question était très mal formulée.
    Et avec 300 lignes de codes à lire, bien que le VBA se lit comme du Pseudo code, ça devient vite barbant. Donc beaucoup ont zappé, je le comprends.
    J'ai essayé de faire simple, mais c'est trop difficile de faire simple et compréhensible quand on extrait un bout d'algorithme sorti de son contexte. Alors je n'ai repris que le cœur de l'algorithme, simplifié, et ça devient sans intérêt...

    Bref, en laissant de coté les discutions sur la méthode "insertion ou pas", ma question adressée aux courageux qui veulent implémenter 300 lignes de VBA en C est : cet algorithme peut-il être ou non une alternative à QuickSort, ou tout autre algorithme connu, pour retourner le classement des données ?

  18. #18
    Membre Expert
    Homme Profil pro
    sans emploi
    Inscrit en
    Janvier 2014
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2014
    Messages : 539
    Par défaut
    Bonjour,

    ce que tu sembles avoir du mal à saisir est que ce n'est pas vraiment le langage qui fait «la performance». Évidemment il est fort à parier qu'implémenté en C le même algo aura certainement un temps d'exécution moins long que son implémentation en VBA, son empreinte mémoire sera sans doute plus faible aussi ; mais cela reste évidemment à confirmer.
    En revanche quand on parle de comparer deux algos on ne parle pas dl'implémentation. L'algorithmique tient justement à cela, on fait très peu d'hypothèses (certaines opérations ont telle ou telle compléxité spatiale, temporelle, …), on définit une mesure (on donne telle entrée à l'algo, on va mesurer une grandeur définie en fonction de cette entrée) et on regarde comment la complexité désirée évolue en fonction de différente entrée ; on réduit cette mesure à un ensemble de fonctions qui se comportent de la même manière asymptotiquement.
    Le problème du tri en général (donc tri classique, ou avec un tableau auxilliaire, stable ou non, …) est un des sujets les plus étudiés et certainement (mais là je m'avance) un des mieux compris.
    Comparer des algos n'est donc pas comparer des programmes qui les implémentent.
    Utiliser quicksort comme étalon n'est pas non plus forcément une bonne idée, quicksort (le classique de Hoare) est un algo en complexité quadratique en fonction de la taille du tableau d'entré en fonction du nombre de comparaisons entre éléments. Oui en O(n²), ce n'est qu'en moyenne qu'il est en O(n lg n).
    Dans ton algo, que je n'ai qu'effleuré, tu utilises un tableau d'éléments déjà triés pour effectuer une recherche dichotomique. Le coût (en complexité temporelle en fonction de … j’abrégerai à partir de maintenant) de cette recherche est évidemment classiquement en O(lg n) ; mais tient compte du coût de création, maintenance de l'ordre ordre, … cela fait exploser ton algo. Du moins, me semble-t-il, car il faudrait creuser un peu plus, mais pour cela il faudrait un algo, il faudrait une âme charitable qui le produise.
    Tu utilises plusieurs tableaux auxiliaires, ce qui a un coût spatial, bien supérieur à la majorité des algorithmes de tris (conventionnels).

    D'un point de vue algorithmique, il est certainement plus simple, mais à nouveau je m'avance, de simplement trier avec un tableau auxiliaire pour obtenir la permutation ce qu'on sait faire efficacement puis seulement ensuite à partir de cette permutation d’obtenir ce que tu appelles le ranking. Le tri sera fait en O(n lg n) complexité temporelle (sur le nombre de comparaisons) par exemple, et en O(n) spatiale car il faut créer le tableau auxiliaire de même taille que l'entrée. Ensuite à creuser mais obtenir le ranking de la permutation doit pouvoir se faire en O(n) temporelle et O(n) max spatiale (si on ne le fait pas inplace).

  19. #19
    Rédacteur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2013
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Août 2013
    Messages : 1 022
    Par défaut
    A défaut de savoir calculer la complexité de l’algorithme, j’ai simplement mis des compteurs sur les traitements effectués : nombre de tests conditionnels sur des alphanumériques, nombre de de tests conditionnels sur des entiers longs, nombre d’écritures d’entiers longs, nombre de mise à jour du tableau temporaire pour la recherche dichotomique et coût de cette mise à jour (le détail des compteurs est dans le code source joint).
    Puis j’ai lancé plusieurs fois le traitement, sur 100.00 données (comprises entre 1 et 10 millions) réparties de façon aléatoires. Et j’ai fait une moyenne.
    J'ai fait le même exercice sur 1 million de données.

    Ce qui permet d’avoir une idée de la complexité de l’algorithme (et accessoirement de la comparer avec Quicksort où j’ai fait le même exercice dans une version qui retourne aussi une indexation, car le but est de pouvoir retourner l'ordre de classement de toutes les données (pas d'une seule), et pas un simple tri, voir le code source).

    Moyennes sur 100.000 données		QuickRanking	QuickSort	Ecart
    Tests conditionnels Alphanumériques	1 836 188	1 365 100	-25,66%
    Tests conditionnels entiers longs	   99 979	  970 768	870,97%
    Total Tests conditionnels		1 936 167	2 335 868	20,64%
    Ecritures Entiers longs			4 129 541	3 693 887	-10,55%
    
    Détail des 4 129 541 :
    Nombre de mise à jour du tableau dichotomique : 11,7
    Ecritures Entiers longs lors de la mise à jour du tableau dichotomique = environ 190.000
    Ecritures Entiers longs autres = environ 3.9 millions
    
    Moyennes sur 1M de données		QuickRanking	QuickSort	Ecart
    Tests conditionnels Alphanumériques	21 635 485	16 505 249	-23,71%
    Tests conditionnels entiers longs	   999 976	11 271 202	1027,15%
    Total Tests conditionnels		22 635 460	27 776 451	22,71%
    Ecritures Entiers longs			48 605 342	43 728 317	-10,03%
    
    Détail des 48 605 342 :
    Nombre de mise à jour du tableau dichotomique : 15
    Ecritures Entiers longs lors de la mise à jour du tableau dichotomique = environ 3 millions
    Ecritures Entiers longs autres = environ 45,5 millions
    Reste à savoir si ces informations peuvent vous être utiles.

    Codes source :

    Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    '-------------------------------------------------------------------------------
    Sub QuickRanking(T() As Variant)
    '-------------------------------------------------------------------------------
    ' Déclaration des variables:
    Dim Nombre_Passages As Long, TailleTps As Long
    Dim Mini As Long, Maxi As Long, n As Long, i As Long, j As Long
    Dim Début As Long, Fin As Long
    ' Déclaration et dimensionnement des tableaux:
    ReDim Indice_Suivant(1 To UBound(T)) As Long
    ReDim TableauTps(1 To UBound(T)) As Long
     
    ' Classe le 2 premiers éléments du tableau T par ordre croissant:
    If T(1) < T(2) Then
        Mini = 1
        Indice_Suivant(1) = 2
        Maxi = 2
        Indice_Suivant(2) = 2
    Else
        Mini = 2
        Indice_Suivant(2) = 1
        Maxi = 1
        Indice_Suivant(1) = 1
    End If
     
    ' Initialise la taille du tableau temporaire TableauTps" des données déjà classées
    TailleTps = 1
     
    ' Boucle sur les n autres éléments de tableau T:
    For n = 3 To UBound(T)
     
        ' Sélectionne l'élément n du tableau T et analyse les différents cas:
        CptCompareAlpha = CptCompareAlpha + 2
        Select Case T(n)
     
        ' Si la valeur de n est inférieure ou égale à la valeur minimale:
        Case Is <= T(Mini)
            Indice_Suivant(n) = Mini
            Mini = n
            TableauTps(1) = n
     
        ' Si la valeur de n est supérieure ou égale à la valeur maximale:
        Case Is >= T(Maxi)
            Indice_Suivant(Maxi) = n
            Maxi = n
            Indice_Suivant(n) = n
            TailleTps = TailleTps + 1
            TableauTps(TailleTps) = n
     
        ' Autres cas:
        Case Else
     
            ' Faire la mise à jour du tableau temporaire "TableauTps" des données déjà classées
            ' si le nombre de passages infructueux est trop élevé :
            CptCompareNum = CptCompareNum + 1
            If Nombre_Passages > n Or Nombre_Passages = 0 Then
            ' Alimenter le tableau des données déjà classées:
                CptDicoNbPassages = CptDicoNbPassages + 1
                CptDicoEcritNum = CptDicoEcritNum + 1
                CptEcritNum = CptEcritNum + 1
                i = Mini
                For j = 1 To n - 1
                    CptEcritNum = CptEcritNum + 2
                    CptDicoEcritNum = CptDicoEcritNum + 2
                    TableauTps(j) = i
                    i = Indice_Suivant(i)
                Next j
                CptEcritNum = CptEcritNum + 2
                CptDicoEcritNum = CptDicoEcritNum + 2
                TailleTps = j - 1
                Nombre_Passages = 1
            End If
     
            ' Recherche Dichotomique de T(n) dans le tableau des données déjà classées:
            CptEcritNum = CptEcritNum + 2
            Début = 1
            Fin = TailleTps
            For j = 1 To Int((Log(n) / Log(2)))
                CptEcritNum = CptEcritNum + 1
                i = (Début + Fin) / 2
                CptCompareAlpha = CptCompareAlpha + 1
                If T(n) > T(TableauTps(i)) Then
                    Début = i: CptEcritNum = CptEcritNum + 1
                Else
                    Fin = i: CptEcritNum = CptEcritNum + 1
                End If
            Next j
     
            ' Ce qui donne la plus proche données inférieure connue:
            CptEcritNum = CptEcritNum + 1
            i = TableauTps(Début)
     
            ' Et permet ainsi de trouver la donnée exacte:
            Do
            CptEcritNum = CptEcritNum + 3
            j = i ' dernière solution
            i = Indice_Suivant(i) ' Indice suivant
            Nombre_Passages = Nombre_Passages + 1 ' Nombre de passages infructueux.
            CptCompareAlpha = CptCompareAlpha + 1
            Loop While T(n) > T(i)
     
            ' Mise à jour des indices suivants:
            CptEcritNum = CptEcritNum + 2
            Indice_Suivant(n) = Indice_Suivant(j) ' Qui est la donnée suivante de n.
            Indice_Suivant(j) = n ' n devient la donnée suivante de l'ancien élément.
     
        End Select
     
    Next n
     
    ' Copie le tableau T dans le tableau temporaire:
    For i = 1 To UBound(T)
        TableauTps(i) = T(i)
    Next i
     
    ' Retourne T classé:
    i = Mini
    For j = 1 To UBound(T)
        T(j) = TableauTps(i)
        i = Indice_Suivant(i)
    Next j
     
    End Sub
     
    '-------------------------------------------------------------------------------
    Sub QuickSort_AndRank(ByRef T() As Variant)
    '-------------------------------------------------------------------------------
    Dim i As Long, Mini As Long, Maxi As Long
     
    Mini = 1
    Maxi = UBound(T)
    ReDim Ref(Mini To Maxi) As Long
    ReDim mémo(Mini To Maxi) As Long
     
    ' Mémorise les données avant de les trier:
    For i = Mini To Maxi
        Ref(i) = i
        mémo(i) = T(i)
    Next i
     
    ' Trie les données et récupère l'ordre de classement:
    Call QS(T(), Ref(), Mini, Maxi)
     
    ' Retourne le tri:
    For i = Mini To Maxi
        T(i) = mémo(Ref(i))
    Next i
     
    End Sub
     
     
    '-------------------------------------------------------------------------------
    Private Sub QS(ByRef T() As Variant, ByRef Ref() As Long, _
                   ByVal Gauche As Long, ByVal Droite As Long)
    '-------------------------------------------------------------------------------
    Dim i As Long, j As Long, Temp As Long, ValQS As Variant
     
    CptEcritNum = CptEcritNum + 3
    i = Gauche
    j = Droite
     
    ValQS = T(Ref((Gauche + Droite) / 2))
     
    Do
        While ValQS > T(Ref(i)): i = i + 1: CptCompareAlpha = CptCompareAlpha + 1: CptEcritNum = CptEcritNum + 1: Wend
        While ValQS < T(Ref(j)): j = j - 1: CptCompareAlpha = CptCompareAlpha + 1: CptEcritNum = CptEcritNum + 1: Wend
     
        CptCompareNum = CptCompareNum + 1
        If j + 1 > i Then
            CptEcritNum = CptEcritNum + 5
            Temp = Ref(i)
            Ref(i) = Ref(j)
            Ref(j) = Temp
            j = j - 1: i = i + 1
        End If
     
        CptCompareNum = CptCompareNum + 1
    Loop Until i > j
     
    CptCompareNum = CptCompareNum + 1
    If Gauche < j Then Call QS(T(), Ref(), Gauche, j)
    If i < Droite Then Call QS(T(), Ref(), i, Droite)
     
    End Sub
    '-------------------------------------------------------------------------------

Discussions similaires

  1. [XL-2010] Adapter Tri rapide (quickSort) pour listbox
    Par crissud dans le forum Macros et VBA Excel
    Réponses: 5
    Dernier message: 02/02/2014, 14h24
  2. Pour ses 15 ans Google annonce son nouvel algorithme de recherche
    Par Stéphane le calme dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 07/10/2013, 18h22
  3. [TP] Tri rapide pour liste simplement chaînée
    Par druzy dans le forum Turbo Pascal
    Réponses: 2
    Dernier message: 25/11/2007, 15h52
  4. Réponses: 3
    Dernier message: 18/04/2006, 12h45
  5. Quel algorithme pour insertion d'objets "triés" da
    Par phplive dans le forum Langage
    Réponses: 3
    Dernier message: 04/08/2005, 09h27

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo