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

Python Discussion :

Différence entre deux dates


Sujet :

Python

  1. #21
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Le but serait plutôt de terminer la fonction en réduisant le nombre de tests.
    Oui nous sommes d'accord mais ce que je voulais expliquer c'est que pour faire cela on pouvait faire ce dont Sve@r avait parlé...

    Et oui nous sommes d'accord que ce dont il parlait pour les conditions avec if s'applique aussi pour les expressions booléennes, c'est d'ailleurs ce que je lui avais répondu pour le "ou logique" mais ça vaut aussi pour le "et logique" comme tu l'as rappelé...
    Merci.

    Citation Envoyé par wiztricks Voir le message
    Et de ce côté là, les instructions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        if annee%4   : return False
        if annee%100 : return True
        if annee%400 : return False
        return True
    sont équivalentes à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        return annee % 4 == 0 and annee % 100 != 0 or annee % 400 == 0
    car lorsque le premier terme d'un AND est faux, on retournera False sans évaluer la suite.
    Il me semble que ces deux solutions ne sont pas tout-à-fait équivalentes...

    Au début je me suis dit la même chose que toi pour le "et logique" (je pensais aussi que peut-être josmiley n'avait pas vu que ça marchait aussi avec le "et logique") mais j'ai testé le code de josmiley et effectivement il est plus performant...

    La raison est la suivante selon moi :

    On est d'accord que si annee % 4 == 0 est faux (c'est-à-dire "si annee n'est pas multiple de 4") l'expression annee % 100 != 0 ne sera pas évaluée mais (et c'est là que se situe la différence) l'expression annee % 400 == 0 sera évaluée...

    Ce qui implique que l'expression annee % 400 == 0 sera évaluée au moins trois fois sur 4.

    Or ce n'est pas le cas avec le code de josmiley :

    Si l'expression annee % 4 est vraie (c'est-à-dire "si annee n'est pas multiple de 4") on retourne false, les deux autres expressions ne sont donc pas évaluées...

    Ce qui implique que l'expression annee % 400 == 0 sera évaluée tout au plus une fois sur 4.

    Citation Envoyé par wiztricks Voir le message
    Après on peut discutailler sur la justesse de l'expression pour retourner l'information qu'on veut sur année.
    A mon sens, annee % 4 == 0 and (annee % 100 != 0 or annee % 400 == 0) serait plus correct.
    Ben il me semble que toutes ces solutions sont correctes en ce sens qu'elles donnent toutes le bon résultat mais elles ne sont pas équivalentes en terme de performance...

    Par contre cette solution avec les parenthèses que tu proposes me semble plus performante que celle sans les parenthèses...

    Avec les parenthèses elle me semble équivalente (ou presque cf. le message suivant pour savoir pourquoi) à la fonction proposée par josmiley ...

  2. #22
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut
    Si on veut vraiment chipoter/pinailler/discutailler alors j'ai remarqué qu'avec les opérateurs de comparaison l'évaluation des conditions semblent prendre un peu plus de temps...

    Exemple, l'évaluation de if annee % 4: serait plus rapide que if annee % 4 != 0:...

    La différence est très faible mais bon elle existe...

    Encore un point (mais plus petit celui-là) pour la fonction proposée par josmiley...

  3. #23
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 865
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 865
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Et de ce côté là, les instructions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        if annee%4   : return False
        if annee%100 : return True
        if annee%400 : return False
        return True
    sont équivalentes à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        return annee % 4 == 0 and annee % 100 != 0 or annee % 400 == 0
    car lorsque le premier terme d'un AND est faux, on retournera False sans évaluer la suite
    Beginner l'a bien dit, ce n'est pas exact. Il a juste oublié de préciser que c'est parce que le "and" est prioritaire sur le "or". Ce qui équivaut à return (annee % 4 == 0 and annee % 100 != 0) or annee % 400 == 0.
    Ainsi, si la première partie est fausse, le "or" offrant une seconde chance, continue alors l'évaluation sur annee % 400 == 0, chose que ne fait pas le code de josmiley.

    Et on peut le montrer avec ce code (test sur les années de 1 à 5000)...
    Code python : 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
    #!/usr/bin/env python3
     
    def mod(y, n, fp):
    	print("y=%d, n=%d" % (y, n), file=fp)
    	return y%n
     
    def isb1(annee, fp):
    	if mod(annee, 4, fp): return False
    	if mod(annee, 100, fp): return True
    	if mod(annee, 400, fp): return False
    	return True
     
    def isb2(annee, fp):
    	return mod(annee, 4, fp) == 0 and mod(annee, 100, fp) != 0 or mod(annee, 400, fp) == 0
     
    with open("b1", "w") as b1, open("b2", "w") as b2:
    	for y in range(1, 5001):
    		assert isb1(y, b1) == isb2(y, b2)
    ... le fichier b1 (code de josmiley) ne contient que 6300 lignes (donc 6300 tests) tandis que le fichier b2 en contient 10050.

    Citation Envoyé par wiztricks Voir le message
    A mon sens, annee % 4 == 0 and (annee % 100 != 0 or annee % 400 == 0) serait plus correct.
    Là je suis d'accord. Et effectivement dans ce cas les deux fichiers ont tous deux 6300 lignes...

    Citation Envoyé par Beginner. Voir le message
    j'ai remarqué qu'avec les opérateurs de comparaison l'évaluation des conditions semblent prendre un peu plus de temps...

    Exemple, l'évaluation de if annee % 4: serait plus rapide que if annee % 4 != 0:...

    La différence est très faible mais bon elle existe...
    Je viens de coder un petit bench à ce propos...
    Code python : 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
    #!/usr/bin/env python3
    # coding: utf-8
     
    import random
    import timeit
    from functools import partial
     
    # Initialisation random
    random.seed()
     
    # Les fonctions à tester
    def implicite(d):
    	return (x%y for (x, y) in d)
     
    def explicite(d):
    	return ((x%y) != 0 for (x, y) in d)
     
    fct={
        "implicite" : implicite,
        "explicite" : explicite,
    }
     
    # Les données à traiter
    data=tuple((n, random.randint(1, n)) for n in range(2, 20002))
    #data=tuple((n, random.randint(1, n)) for n in range(2, 20))
     
    # Vérification fonctions identiques
    x=tuple(tuple(map(bool, f(data))) for f in fct.values())
    #print(data)
    #print(x)
    assert x[0] == x[1]
    #exit(0)
     
    # Le nombre de répétitions (les moyennes se feront sur cette valeur)
    repeat=20
     
    # Appel des fonctions dans un ordre aléatoire et affichage du chrono
    print("start=%d, repeat=%d" % (len(data), repeat))
    for (k, v) in random.sample(tuple(fct.items()), len(fct)):
        t=timeit.Timer(partial(v, data)).repeat(repeat=repeat, number=1_000_000)
        print("%s: min=%f, max=%f, avg=%f" % (k, min(t), max(t), sum(t)/len(t)))
    # for

    Le résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    start=20000, repeat=20
    implicite: min=1.213242, max=1.491701, avg=1.287143
    explicite: min=1.241484, max=1.438572, avg=1.317145
    Effectivement ça te donne raison. Mais vu la différence vraiment minime (une moyenne de 3 centièmes plus rapide pour 20 millions de tests sur 20000 valeurs) je pense quand-même m'en tenir à la PEP Python disant clairement qu'explicite vaut mieux qu'implicite
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  4. #24
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Beginner l'a bien dit, ce n'est pas exact. Il a juste oublié de dire que c'est parce que le "and" étant prioritaire, il est effectué en premier et le "or" reste présent. Ce qui équivaut à return (annee % 4 == 0 and annee % 100 != 0) or annee % 400 == 0.
    Ainsi, si la première partie est fausse, le "or" offrant une seconde chance, continue alors l'évaluation sur annee % 400 == 0, chose que ne fait pas le code de josmiley.
    Oui c'est vrai que je n'ai pas précisé que "and" est prioritaire mais quelque part j'applique cela dans mon explication :

    Citation Envoyé par Beginner. Voir le message
    On est d'accord que si annee % 4 == 0 est faux (c'est-à-dire "si annee n'est pas multiple de 4") l'expression annee % 100 != 0 ne sera pas évaluée mais (et c'est là que se situe la différence) l'expression annee % 400 == 0 sera évaluée...
    Je part du cas où annee % 4 == 0 est faux dans ce cas le deuxième opérande du "and" (annee % 100 != 0) ne sera pas évalué car on sait que le résultat (du "and") sera false quelque soit sa valeur... Mais ce résultat est le premier opérande du "or" et comme il vaut false le second opérande annee % 400 == 0 de ce "or" sera évalué...

    Dans ce cas-là on économise* l'évaluation du second opérande du "and" et je pense que c'est surtout cela que voulait souligner wiztricks :

    Citation Envoyé par wiztricks Voir le message
    car lorsque le premier terme d'un AND est faux, on retournera False sans évaluer la suite.
    Mais hélas avec cette solution on n'économise pas l'évaluation du second opérande du "or"...


    Citation Envoyé par Sve@r Voir le message
    Ce qui équivaut à return (annee % 4 == 0 and annee % 100 != 0) or annee % 400 == 0.
    Ah oui bonne idée, les parenthèses facilite la compréhension...

    Citation Envoyé par Sve@r Voir le message

    Et on peut le montrer avec ce code (test sur les années de 1 à 5000)...
    Merci pour ce code, j'avais commencé à en faire un mais je n'aurais peut-être pas à le terminer car je pense qu'avec ton code on peut vérifier ce que je racontais :

    Citation Envoyé par Beginner. Voir le message
    ...
    Ce qui implique que l'expression annee % 400 == 0 sera évaluée au moins trois fois sur 4.
    ...
    Ce qui implique que l'expression annee % 400 == 0 sera évaluée tout au plus une fois sur 4.

    Citation Envoyé par Sve@r Voir le message
    Je viens de coder un petit bench à ce propos...
    Merci je vais regarder ça...

    Citation Envoyé par Sve@r Voir le message
    Effectivement ça te donne raison. Mais vu la différence vraiment minime (une moyenne de 3 centièmes plus rapide pour 20 millions de tests sur 20000 valeurs) je pense quand-même m'en tenir à la PEP Python disant clairement qu'explicite vaut mieux qu'implicite
    Je ne savais pas que la PEP disait ça mais je suis d'accord avec toi, j'avais d'ailleurs répondu à josmiley que je préfère mettre les opérateurs pour une question de lisibilité...

  5. #25
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut
    Citation Envoyé par LeNarvalo Voir le message
    Petite adaptation :
    ...
    Quelques 222 Mo de RAM et environ 1.1 seconde, pour un diff('1111/01/01','5000/01/01'). L'idée ensuite serait de ne pas recharger le calendrier à chaque fois !
    Bon maintenant que le problème du Nombre d'années bissextiles entre deux années a été résolu j'ai pu testé la solution* que j'avais en tête...

    Le temps d'exécution est de moins de 4µs (sur mon PC qui est moins rapide que le tient) même quand l'intervalle est très grand, genre par exemple diff_dates("01/01/1", "31/12/9999")...

    Pour la RAM je n'ai pas regardé, je n'ai pas compris s'il fallait ajouter des choses (comme tu l'as fait) pour faire le teste...

    Et comment tu fais pour que le programme reste en exécution, un input ?



    ---------
    * Voici le code pour ceux que cela intéresserait :

    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
     
    nbr_jours = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
    nbr_jours_b = [0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]  # année bissextile
     
    def is_bissextile(annee):
        return annee % 4 == 0 and (annee % 100 != 0 or annee % 400 == 0)
     
    # différence entre deux dates d'une même année
    # format date : (j,m,a)
    def diff_dates_meme_annee(d1, d2):
        if d1 == d2:
            return 0
        # juste pour la lisibilité...
        (j1, m1, a1) = d1
        (j2, m2, a2) = d2
     
        lst_nbr_jours = nbr_jours
        if is_bissextile(a1):
            lst_nbr_jours = nbr_jours_b
     
        # nombre de jours entre le 01/01 et d1 non inclus
        nbr_jours_annee_d1 = j1 + lst_nbr_jours[m1]
        # nombre de jours entre le 01/01 et d2 non inclus
        nbr_jours_annee_d2 = j2 + lst_nbr_jours[m2]
     
        return nbr_jours_annee_d2 - nbr_jours_annee_d1  
     
     
    def nbr_annees_bissextiles(a1, a2):
        if a1 == a2:
            return 0
        a1, a2 = a1 - 1, a2 - 1
        return (a2 // 4) - (a1 // 4) - ((a2 // 100) - (a1 // 100)) + (a2 // 400) - (a1 // 400)
     
     
    def diff_dates(d1, d2):
        (j1, m1, a1) = map(int, d1.split("/"))
        (j2, m2, a2) = map(int, d2.split("/"))
     
        diff = a2 - a1
        if diff == 0:        
            return diff_dates_meme_annee((j1, m1, a1), (j2, m2, a2))
     
        nbr_jours_annees_completes = 365 * \
            (diff-1) + nbr_annees_bissextiles(a1 + 1, a2)
     
        # nombre de jours à prendre en compte dans dans l'année a1
        # ---> nombre de jours entre d1 et le 31/12/a1 inclus
        nbr_jours_a1 = 1 + diff_dates_meme_annee((j1, m1, a1), (31, 12, a1))
     
        # nombre de jours à prendre en compte dans dans l'année a2
        # ---> nombre de jours entre le 01/01/a2 et d2 non inclus
        nbr_jours_a2 = diff_dates_meme_annee((1, 1, a2), (j2, m2, a2))    
     
        return nbr_jours_a1 + nbr_jours_a2 + nbr_jours_annees_completes
    On peut l'optimiser mais cela ne vaut pas le coup par rapport à la perte de lisibilité et puis moins de 4µs ça ira je pense...

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Réponses: 2
    Dernier message: 21/07/2006, 15h04
  2. Nombre de minutes de différence entre deux dates
    Par Oberown dans le forum SQL Procédural
    Réponses: 4
    Dernier message: 05/05/2006, 16h41
  3. Différence entre deux dates
    Par Azharis dans le forum Access
    Réponses: 3
    Dernier message: 11/01/2006, 11h58
  4. Différence entre deux dates
    Par pittzz dans le forum Oracle
    Réponses: 5
    Dernier message: 18/07/2005, 13h24
  5. Comment obtenir la différence entre deux dates ?
    Par jbat dans le forum Langage
    Réponses: 4
    Dernier message: 02/06/2005, 10h34

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