# F:\informatiques\Python_MPSI\DM\suite_syracuse\dm_syracuse.py 001| # coding utf 8
002|
003|
#####################################################################
############################
004| # Corrigé du Devoir maison n°1: 2017-2018 Suite de Syracuse d'après Euler Project 14
005|
#####################################################################
################################
006|
007| ## Modules utilisés 008|
009| import time 010|
011|
012| ## Fonctions utilisées 013|
014|
015| # Q1 016| def f(n):
017| if n % 2 ==0:
018| return n//2 019| else:
020| return 3*n+1 021|
022| # Q2 023|
024| def longueur_chaine(c):
025| n = 1 026| u = c
027| while u != 1:
028| u = f(u) 029| n += 1 030| return n 031|
032| # Q3 033|
034| def max_chaine(c):
035| uMax = c 036| u = c
037| while u != 1:
038| u = f(u) 039| if u > uMax:
040| uMax = u 041| return uMax 042|
043| # Q4 044|
045| def longueur_maximale(N):
046| """Données N un entier strictement positif
047| Résultat: la plus grande longueur de chaîne et sa valeur de départ c parmi les chaînes dont la valeur de départ c est comprise entre 1 et N"""
1
048| longueur_max = 1
049| c_max = 1 # la valeur de départ de la chaîne de longueur maximale
050| for c in range(1, N+1):
051| longueur_actuelle = longueur_chaine(c) 052| if longueur_actuelle > longueur_max : 053| longueur_max = longueur_actuelle 054| c_max = c
055| return longueur_max, c_max 056|
057|
058| # Remarque:
059| '''Si l'on remplace ci-dessus le bloc for par le bloc ci-
dessous, on calcule deux fois longueur_chaine(c) au lieu de une seule fois. C'est moins efficace.
060| for c in range(1, N+1):
061| if longueur_chaine(c) > longueur_max:
062| longueur_max = longueur_chaine(c) 063| c_max = c
064| ''' 065|
066| ## Pour répondre à la question posée, on exécute le script suivant, qui donne en plus le temps de calcul
067|
068| t0 = time.clock()
069| print(longueur_maximale(10**6)) 070| t1 = time.clock()
071| duree_calcul = t1 -t0
072| print("temps de calcul ", duree_calcul) 073|
074| # renvoie (351, 77031) pour N = 10**5 en 3 s et (525,837799) pour 10**6 en 37s
075|
076| # Parmi les chaînes dont la valeur de départ c est comprise entre 1 et 10^6, la plus longue est obtenue pour une valeur de départ c = 837799, et sa longueur vaut 525.
077| # Le temps de calcul est de 37 secondes sur mon PC.
078|
079|
080| ## Améliorations possibles 081|
082| """On doit pouvoir accélérer les temps de calculs en mémorisant les calculs de longueurs de chaînes... On va gagner du temps en perdant de l'espace...
083| On crée un dictionnaire dans lequel, on va stocker au fur et à mesure les longueurs calculées (on note l(c) la longueur de la chaîne démarrant à c):
084| * Pour c = 2, 2 n'est pas encore dans le dico, on itère on tombe sur 1 qui est dans dico. Dans dico, on a maintenant les longueurs de 1 et 2.
085| * On passe à c =3. 3 n'est pas dans dico, on itère, on obtient 10 qui n'est pas dico, on continue jusqu'à ce l'on tombe dans le dico. On obtient ainsi comme chaîne [3, 10, 5, 16, 8, 4, 2]. On s'arrête à 2 car la longueur de 2 avait été calculée et vallait 2 086| On obtient non seulement la longueur de 3 mais aussi celles des
2
086| On obtient non seulement la longueur de 3 mais aussi celles des éléments de la chaîne: l(4) = 3, l(8)= 4, l(16)= 5, l(5) = 6, l(10) = 7 et l(3) = 8. On les enregistre alors dans le dico.
087| * Maintenant on passe au cas c= 4. Et bien il n'y a rien à faire car 4 est déjà dans le dico. De même pour c= 5.
088| * On passe donc à c = 6. La chaîne est [6,3], car 3 est dans le dico.
089| * On passe à c= 7. La chaîne est
[7,22,11,34,17,52,26,13,40,20,10], car 10 est dans le dico...
090| """
091|
092|
093| def longueur_maximale_bis(N):
094| dico_longueurs = {1: 1} # on initialise un dictionnaire contenant les longueurs des chaînes.
095| # La chaîne issue de 1 a pour longueur 1
096| longueur_max = 1 097| c_max = 1
098| for c in range(2, N+1):
099| t =[c] # t est un tableau qui va contenir les valeurs de la chaîne issue de c
100| u = c
101| while u not in dico_longueurs: # on calcule la chaîne jusquà ce que
102| # l'on tombe sur un nombre dont on connaît la longueur.
103| u = f(u) 104| t.append(u)
105| # arrivé ici u le dernier élément de t est dans dico, 106| # on met à jour le dico en rentrant les nouvelles longueurs des éléments de t
107| n = len(t)
108| for k in range(n):
109| dico_longueurs[t[k]] = dico_longueurs[u] + (n-1-k) 110| if dico_longueurs[t[k]] > longueur_max:
111| longueur_max = dico_longueurs[t[k]]
112| c_max = t[k]
113| return longueur_max, c_max 114|
115|
116| t0 = time.clock()
117| print(longueur_maximale_bis(10**7)) 118| t1 = time.clock()
119| duree_calcul = t1 -t0
120| print("temps de calcul ", duree_calcul) 121|
122| # renvoie (351, 77031) pour N = 10**5 en 0.4 s et (525,837799) pour 10**6 en 4 s
123| # C'est une jolie amélioration!!
124| # renvoie (686, 8400511) pour N = 10^7 en 41 s 125|
126|
127|
128| ## Attention à la division flottante n/ 2 au lieu de n//2 129| def f(n):
3
130| if n % 2 ==0:
131| return n/2 # on écrit n/2 au lieu de n//2 132| else:
133| return 3*n+1 134|
135| for c in [2**1024,2**10252]:
136| print( longueur_chaine(c)) 137|
138| """on obtient 1025 puis
139| OverflowError: integer division result too large for a float 140| car 2**1023 est la plus grande puissance de deux représentable par un nbre flottant"""
141|
142|
143|
4