Partage
  • Partager sur Facebook
  • Partager sur Twitter

[exercice] La suite de Conway

1 -> 11-> 21 -> 1211 -> 111221 -> 312211 : suivant = ????

    14 juillet 2010 à 14:36:16

    Bonjour,

    Il s'agit d'un exercice que j'ai déjà proposé ICI sur le forum C il y a bientôt un an, désolé pour ceux qui connaissent.


    Voici la fameuse suite de Conway :


    1
    11
    21
    1211
    111221
    312211
    etc


    Il s'agit d'une suite de nombres (ou encore de chaînes). Le premier nombre est 1, le second 11, le troisième 21, etc. Chaque nombre se déduit du précédent par une règle que je vais préciser maintenant.


    Un exemple suffira à comprendre : pour passer de la ligne 5

    111221


    à la ligne 6, on fait comme ceci : on regarde la ligne 5, on la lit en regroupant les chiffres identiques qui se suivent, ce qui donne :

    "3 fois le chiffre 1 puis 2 fois le chiffre 2 et enfin 1 fois le chiffre 1"


    ce qui donne

    "3 fois le chiffre 1 puis 2 fois le chiffre 2 et enfin 1 fois le chiffre 1"


    et enfin, en extrayant les nombres :

    312211


    d'où la ligne 6.

    Une liste des 25 premiers nombres de la suite est disponible ICI sur le site de Sloane.

    Vous devez écrire un programme Python qui génère la ligne n de la suite de conway. Le coeur du programme devrait être une fonction qui calcule la ligne suivante à partir d'une ligne donnée.

    Votre programme doit être capable de traiter en quelques secondes des valeurs comme n=50 mais attention, les nombres obtenus ont assez rapidement des millions de chiffres. Pour que votre console ne soit pas envahi par un affichage énorme, redirigez l'affichage vers un fichier (ça marche sous Windows comme sous Linux), genre si je veux placer l'affichage dans un fichier toto.txt :

    $ python conway.py > toto.txt



    Le code Python ne contient aucune astuce, ça fait juste travailler les listes, les boucles, les conditions, les fonctions.

    EDIT La suite est répertoriée sur le site de Sloane.
    • Partager sur Facebook
    • Partager sur Twitter
      14 juillet 2010 à 15:27:56

      Pour ça, un code très moche mais terriblement rapide, en python 3, que je mets en secret, parce que ce n'est surtout pas ce qui est demandé :

      import time
      def conway(nbr=25):
          n = "1"
          for i in range(nbr):
              n=n.replace('111','ca').replace('222','cb').replace('11','ba').replace('22','bb').replace('33','bc').replace('1','aa').replace('2','ab').replace('3','ac').replace('aaa','111').replace('bbb','222').replace('ccc','333').replace('aa','11').replace('bb','22').replace('cc','33').replace('a','1').replace('b','2').replace('c','3')
      #cette mocheté a pour but de changer les "111" en "31" (et autres), en mettant des lettres ;
      #les lettres seront ensuite regroupées pour donner des chiffres.
      
              yield n #c'est un générateur qui calculera les valeurs au fur et à mesure, c'est plus pratique à mon goût
      
      suite = conway(50)
      
      for i in suite:
          pass #pour éviter d'afficher les résultats ; ils seront néanmoins calculés
      
      print(time.clock()) #prend 1,21 seconde en temps cpu chez moi, pour calculer les 50 premières occurences de la suite
      


      Édit : j'ai oublié 2-3 trucs, je le modifie(rai), à ce propos :

      Citation : candide

      Le coeur du programme devrait être une fonction qui calcule la ligne suivante à partir d'une ligne donnée.




      • Partager sur Facebook
      • Partager sur Twitter
        14 juillet 2010 à 15:39:36

        En effet, c'est osé, car c'est très moche :D
        • Partager sur Facebook
        • Partager sur Twitter
          14 juillet 2010 à 15:55:50

          "Et pourtant, il est très rapide."
          • Partager sur Facebook
          • Partager sur Twitter
            14 juillet 2010 à 16:08:06

            Citation : ordiclic

            Pour ça, un code très moche mais terriblement rapide, en python 3, que je mets en secret, parce que ce n'est surtout pas ce qui est demandé :



            Tu n'es pas obligé de suivre l'énoncé au pied de la lettre surtout si tu fais quelque chose d'équivalent ou supérieur ;)


            Tes sorties semblent correctes et ton code est de l'ordre 3 fois plus rapide que le mien, bien ! À vrai dire je n'ai cherché absolument aucune optimisation.
            • Partager sur Facebook
            • Partager sur Twitter
              14 juillet 2010 à 16:13:10

              Mon code est tout simple et correspond certainement à ce que tout le monde fera (peut-être modulo le passage par des listes) :
              def ligne_suivante(ligne):
                  suivante = []
                  chiffre_actuel = ligne[0]
                  compteur = 0
                  for chiffre in ligne:
                      if chiffre == chiffre_actuel:
                          compteur += 1
                      else:
                          suivante.append(str(compteur))
                          suivante.append(chiffre_actuel)
                          chiffre_actuel = chiffre
                          compteur = 1
                  suivante.append(str(compteur))
                  suivante.append(chiffre_actuel)
                  return "".join(suivante)
              
              
              def conway(n):
                  "Iterateur sur les n premiers termes de la suite"
                  ligne = "1"
                  for i in xrange(n):
                      yield ligne
                      ligne = ligne_suivante(ligne)
              



              edit : une version qui utilise les générateurs takewhile et dropwhile, probablement plus performante (mais sans être sûr, si quelqu'un veut vérifier) :
              from itertools import dropwhile, takewhile
              
              def ligne_suivante(ligne):
                  suivante = []
                  while ligne != "":
                      gauche = takewhile(lambda i : i == ligne[0], ligne)
                      droite = dropwhile(lambda i : i == ligne[0], ligne)
                      suivante.append(str(len(list(gauche))))
                      suivante.append(str(ligne[0]))
                      ligne = "".join(droite)
                  return "".join(suivante)
              
              
              def conway(n):
                  "Iterateur sur les n premiers termes de la suite"
                  ligne = "1"
                  for i in xrange(n):
                      yield ligne
                      ligne = ligne_suivante(ligne)
              
              • Partager sur Facebook
              • Partager sur Twitter
                14 juillet 2010 à 16:54:51

                Merci de ta participation Maxibolt ;)

                Citation : Maxibolt

                Mon code est tout simple et correspond certainement à ce que tout le monde fera



                Tu parles de l'algorithme ? parce qu'un débutant ne va pas écrire du code Python comme le tien vu que tu connais Python depuis pas mal de temps, non ?(*) Même pour l'algorithme, un débutant complet pourrait avoir du mal.

                (*) EDIT débutant il y a 4 ans disais-tu sur ce forum.

                Je pense que pour la compréhension du débutant, il serait bien d'accompagner tout code au moins d'une exécution. Je me rappelle qu'à une époque, je ne comprenais rien aux itérateurs et j'aurais été incapable d'utiliser ton code pour afficher la ligne n°x. Donc je complète ton code avec l'exemple que j'ai utilisé :

                def ligne_suivante(ligne):
                    suivante = []
                    chiffre_actuel = ligne[0]
                    compteur = 0
                    for chiffre in ligne:
                        if chiffre == chiffre_actuel:
                            compteur += 1
                        else:
                            suivante.append(str(compteur))
                            suivante.append(chiffre_actuel)
                            chiffre_actuel = chiffre
                            compteur = 1
                    suivante.append(str(compteur))
                    suivante.append(chiffre_actuel)
                    return "".join(suivante)
                
                
                def conway(n):
                    "Iterateur sur les n premiers termes de la suite"
                    ligne = "1"
                    for i in xrange(n):
                        yield ligne
                        ligne = ligne_suivante(ligne)
                        
                n=10
                for i,z in enumerate(conway(n)):
                    if i==n-1:
                        print (z)
                

                13211311123113112211



                Au passage, ton code ne passe pas en Python 3 :

                NameError: global name 'xrange' is not defined



                Alors effectivement mon code est assez proche du tien, aux listes près effectivement, et à l'itérateur aussi, que je considère comme trop compliqué pour le débutant lambda et d'une façon générale, assez artificiel, mais bon, c'est un avis personnel et susceptible d'évoluer. Les temps de nos codes sont très comparables. Mon code est ici :

                def lookAndSay(n):
                    def conway(z):
                        zz, old, c =[], z[0], 0 # c = compteur
                        for i in range(len(z)):
                            if z[i]!=old:
                                zz+=[c]+[old]
                                old, c = z[i], 1
                            else :
                                c+=1
                        return zz+[c]+[old]
                    z=[1]
                    for _ in range(n-1):
                        z=conway(z)
                    return ''.join(str(i) for i in z)
                
                n=10
                
                print (lookAndSay(n))
                



                13211311123113112211





                Citation : Maxibolt


                edit : une version qui utilise les générateurs takewhile et dropwhile, probablement plus performante (mais sans être sûr, si quelqu'un veut vérifier) :



                Ah non, c'est très lent chez moi.

                • Partager sur Facebook
                • Partager sur Twitter
                  14 juillet 2010 à 23:55:57

                  Oui, pour Python 3, il faut remplacer xrange par range (au passage, ça marcherait aussi sous 2.6, mais j'ai préféré utiliser xrange pour marquer que c'était le générateur qui m'intéressait).

                  Pour "tout le monde fera ça", je pensais surtout à la première fonction. Les débutants complets auront peut-être du mal, mais sinon l'algorithme est assez basique et le code n'utilise pas de fonctionnalités très avancées de Python (à part le passage à une liste que j'ai choisi de faire, qui n'est pas très compliqué mais pas forcément évident, et qu'on pourrait facilement retirer).
                  • Partager sur Facebook
                  • Partager sur Twitter
                    15 juillet 2010 à 1:18:24

                    Par souci de complétude, voici deux codes Python trouvés sur le site de Sloane et résolvant le problème mais ayant des performances bien moins bonnes que tous les codes que nous avons donnés ici (traduction : on est des zéros mais pas absolus ;) ) :

                    def A005150(n):
                        p = "1"
                        seq = [1]
                        while (n > 1):
                            q = ''
                            idx = 0 # Index
                            l = len(p) # Length
                            while idx < l:
                                start = idx
                                idx = idx + 1
                                while idx < l and p[idx] == p[start]:
                                    idx = idx + 1
                                q = q + str(idx-start) + p[start]
                            n, p = n - 1, q
                            seq.append(int(p))
                        return seq[-1]
                    
                    print A005150(10)
                    



                    def A005150(n):
                        seq = [1] + [None] * (n - 1) # allocate entire array space
                        def say(s):
                            acc = '' # initialize accumulator
                            while len(s) > 0:
                                i = 0
                                c = s[0] # char of first run
                                while (i < len(s) and s[i] == c): # scan first digit run
                                    i += 1
                                acc += str(i) + c # append description of first run
                                if i == len(s):
                                    break # done
                                else:
                                    s = s[i:] # trim leading run of digits
                            return acc
                        for i in xrange(1, n):
                            seq[i] = int(say(str(seq[i-1])))
                        return seq[-1] 
                    
                    print A005150(45)
                    
                    • Partager sur Facebook
                    • Partager sur Twitter
                      15 juillet 2010 à 1:21:12

                      La première fonction de maxibolt est très claire je trouve.

                      Ca ressemble à du pseudo-code!(Si on oublie le générateur)
                      J'ai déjà codé la suite de conway en C :-° Et c'était un(gros)poil moins clair.

                      Tout de même, en python(et avec des noms de variables explicites), c'est très lisible!

                      J'en profite pour une question :D
                      Je lis souvent qu'en python il n'y a qu'une seule manière de faire correctement.
                      Bizarrement, on voit toujours plusieurs solutions.
                      La manière considérée correcte, c'est le second code de maxibolt, ou son premier code?


                      Citation : candide


                      (traduction : on est des zéros mais pas absolus ;) ) :


                      J'ai déjà fait l'expérience!
                      ;)
                      • Partager sur Facebook
                      • Partager sur Twitter
                      Zeste de Savoir, le site qui en a dans le citron !
                        15 juillet 2010 à 16:26:36

                        Bonjour,

                        C'est toujours un plaisir ces petits exercices candide :)
                        Voici ce à quoi j'arrive de façon terre à terre:
                        from time import clock
                        import sys
                        
                        def conway(n):
                            line = [1]
                            for i in xrange(0,n-1):
                                newline = []
                                j = 0
                                while j < len(line):
                                    k = 0
                                    while j+k < len(line) and line[j+k] == line[j]:
                                            k += 1
                                    newline += [k] + [line[j]]
                                    j += k
                                line = newline
                            return str().join([str(x) for x in line])
                        
                        start = clock()
                        line = conway(int(sys.argv[1]))
                        stop = clock()
                        print line
                        print >> sys.stderr, 'Execution time :', stop-start, 's'
                        


                        Python26\>conway.py 50 > conway50.txt
                        Execution time : 6.80582901916 s


                        Edit: petite correction du code, maintenant l'algorithme ne manipule plus que des listes (et non des listes et des chaines comme c'était le cas). Ca fait gagner plus de 2 secondes chez moi.
                        • Partager sur Facebook
                        • Partager sur Twitter
                        Inkamath on GitHub - Interpréteur d'expressions mathématiques. Reprise du développement en cours.
                          15 juillet 2010 à 17:16:17

                          Citation : iNaKoll


                          C'est toujours un plaisir ces petits exercices candide :)



                          Merci de ce commentaire qui me fait fait bien plaisir.


                          Citation : iNaKoll


                          Edit: petite correction du code, maintenant l'algorithme ne manipule plus que des listes (et non des listes et des chaines comme c'était le cas). Ca fait gagner plus de 2 secondes chez moi.




                          C'est marrant parce que je venais de modifier ton code exactement dans ce sens et effectivement le temps s'améliore notablement. Petit détail supplémentaire : si je change ta partie de code :

                          while j < len(line):
                                      k = 0
                                      while j+k < len(line) and line[j+k] == line[j]:
                                              k += 1
                                      newline += [k] + [line[j]]
                          



                          par celle-ci :

                          z=line[j]
                                      while j+k < len(line) and line[j+k] == z:
                                              k += 1
                                      newline += [k,z]
                          


                          je gagne 25% de temps d'exécution.

                          Par ailleurs,

                          *) je ne connaissais pas print >>
                          *) j'ai trouvé ton code très lisible.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            15 juillet 2010 à 17:34:45

                            Bonjour

                            Voici ma version :
                            import sys
                            import time
                            
                            def conway(nbrLine):
                            	def nextLine(n):
                            		nbr, lastC, re = 0, '', ''
                            		
                            		for c in str(n):
                            			if not c == lastC:
                            				re += str(nbr) + lastC
                            				nbr, lastC = 0, c
                            			
                            			nbr += 1
                            		
                            		return re[1:] + str(nbr) + lastC
                            	
                            	line = 1
                            	for _ in xrange(1, nbrLine + 1):
                            		yield line
                            		line = nextLine(line)
                            
                            t = time.clock()
                            for i in conway(50):
                            	print i
                            print >> sys.stderr, 'Execution time :', time.clock() - t
                            


                            Execution time : 2.07836470836


                            En tout cas j'ai été intrigué par la version de ordiclic. Je n'avais jamais vu cette implémentation pour cet algo.
                            • Partager sur Facebook
                            • Partager sur Twitter
                              15 juillet 2010 à 19:03:41

                              Les spécificités de cette implémentation, c'est plusieurs choses : la première, c'est l'enchainement des replace(), qui se font les uns après les autres, et c'est arrangeant :D , la deuxième, c'est que ça ne fonctionne -que- avec 1 comme premier terme, car ça remplace des groupes au cas par cas ; la troisième chose à dire dessus, c'est comment ça fonctionne.

                              En fait, ça prend d'abord tous les groupes de "111", "222" et "333", et ça réalise exactement ce qu'on veut : ça transforme le "111" en "31". Dans ce cas, pourquoi passer par des lettres, genre "111" en "ca" ?
                              C'est parce qu'on est obligé de traiter d'abord les gros groupes. En effet, si on fait un replace("1","11"), ça prendra le "111" et le remplacera par un "111111"... Et ça pose un problème en plus ! car une fois que tous les gros groupes ont été transformés, il reste les petits groupes et les lettres isolées... Qui seront bien prises par les replace() suivants, mais ces replace prendront aussi ce qui vient d'être transformé depuis les gros groupes : ça ferait, "111" => "31" => "1311" à cause de l'ordre des replace.
                              La solution est de transformer ces groupes de chiffres en lettres : ainsi "222" devient "cb", puis de mettre des chiffres à la place des lettres, ainsi "cb" devient "32" : problème résolu. :)

                              D'ailleurs, j'ai un peu allégé mon code, j'avais mis 6 replace en trop, ça devrait être mieux maintenant :

                              import time
                              def conway(nbr=25):
                                  n = "1"
                                  for i in range(nbr):
                                      n=n.replace('111','ca').replace('222','cb').replace('11','ba').replace('22','bb').replace('33','bc').replace('1','aa').replace('2','ab').replace('3','ac').replace('a','1').replace('b','2').replace('c','3')
                                      yield n
                              
                              suite = conway(50)
                              
                              for i in suite:
                                  print(i)
                              
                              print(time.clock()) #prend 0.78 seconde en temps cpu chez moi, pour calculer les 50 premières occurences de la suite
                              


                              Édit : ça fait gagner entre 30% et 40% de rapidité, le fait d'enlever des replace inutiles ; désolé si les explications sont confuses, je n'ai jamais été bon pédagogue. :-°
                              • Partager sur Facebook
                              • Partager sur Twitter
                                15 juillet 2010 à 21:56:35

                                Bon une petite résolution avec un algorithme classique je pense (je n'ai pas redirigée vers un fichier, j'ai stockée dans une liste) :
                                from time import clock
                                
                                def convert(a):
                                	c = a[0]
                                	cmp = 0
                                	res = ''
                                	for i in a:
                                		if i == c:
                                			cmp += 1
                                		else:
                                			res += str(cmp)
                                			res += c
                                			cmp = 1
                                			c = i
                                	res += str(cmp)
                                	res += c
                                	return res
                                
                                def conway(n):
                                	res = ['1']
                                	for i in range(n):
                                		res.append(convert(res[i]))
                                	return res
                                
                                debut = clock()
                                resultat = conway(50)
                                fin = clock()
                                print('Temps :',fin-debut)
                                

                                Je trouve 4.5 secondes en temps pour trouver les 50 premiers termes mais bon, à tester sur une autre machine :p
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  15 juillet 2010 à 22:13:42

                                  Ton programme prend 4"57 chez moi, c'est assez rapide. Mais ça ne bat pas encore les 0"78 de mon programme :-°
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    15 juillet 2010 à 22:49:24

                                    Un petit comparatif des solutions de Holt, ordiclic et de deux variantes de celle d'ordiclic.

                                    from time import clock
                                    
                                    def convert(a):
                                    	c = a[0]
                                    	cmp = 0
                                    	res = ''
                                    	for i in a:
                                    		if i == c:
                                    			cmp += 1
                                    		else:
                                    			res += str(cmp)
                                    			res += c
                                    			cmp = 1
                                    			c = i
                                    	res += str(cmp)
                                    	res += c
                                    	return res
                                    
                                    def conway(n): #Holt
                                    	res = ['1']
                                    	for i in range(n):
                                    		res.append(convert(res[i]))
                                    	return res
                                    
                                    def conway2(nbr=50): #ordiclic
                                        n = "1"
                                        for i in range(nbr):
                                            n=n.replace('111','ca').replace('222','cb').replace('11','ba').replace('22','bb').replace('33','bc').replace('1','aa').replace('2','ab').replace('3','ac').replace('a','1').replace('b','2').replace('c','3')
                                        return n
                                    
                                    def conway3(nbr=50): #Variante 1 de conway2
                                        n = "1"
                                        l=[('111','ca'),('222','cb'),('11','ba'),('22','bb'),('33','bc'),('1','aa'),('2','ab'),('3','ac')]
                                        l2=[('aaa','31'),('bbb','32'),('aa','21'),('bb','22'),('cc','23'),('a','11'),('b','12'),('c','13')]
                                        for i in xrange(nbr):
                                            for old,new in (l if not i%2 else l2):
                                                n=n.replace(old,new)
                                        if nbr%2:
                                            return n.replace("a","1").replace("b","2").replace("c","3")
                                        return n
                                    
                                    def conway4(nbr=50): #Variante 2 de conway2
                                        n="1"
                                        dic={"1":"11","2":"12","3":"13","11":"21","22":"22","33":"23","111":"31","222":"32","333":"33"}
                                        for i in xrange(nbr):
                                            act,newn=n[0],""
                                            for e in n[1:]:
                                                if e!=act[0]:
                                                    newn+=dic[act]
                                                    act=e
                                                else:
                                                    act+=e
                                            n=newn+dic[act]
                                        return n    
                                    
                                    for cnw in (conway,conway2,conway3,conway4):
                                        t=clock()
                                        cnw(50)
                                        print cnw.func_name," : ",clock()-t
                                    


                                    Résultat chez moi:

                                    conway  :  2.26874709333
                                    conway2  :  0.386976426666
                                    conway3  :  0.29351552
                                    conway4  :  1.43114410667
                                    


                                    Edit : corrections

                                    J'ajoute que je ne cautionne pas spécialement ces courses à la vitesse d'exécution en Python.
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      15 juillet 2010 à 23:20:56

                                      Ouch, violent pour le conway3.
                                      Faire 8 remplacements par boucle au lieu de 11 de cette manière, c'est bien pensé !
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        15 juillet 2010 à 23:35:09

                                        Citation : Holt

                                        Bon une petite résolution avec un algorithme classique je pense



                                        Temps plus qu'honorable (mieux que le mien et code très lisible).

                                        Citation : ordiclic

                                        Mais ça ne bat pas encore les 0"78 de mon programme :-°



                                        En effet, ton temps est remarquable grâce à ta jolie petite astuce, encore mieux avec la variante d'EMC1. J'en conclus en particulier qu'il faut laisser le plus possible aux fonctions de Python le soin de faire le travail plutôt que tout refaire nous-mêmes.

                                        Note quand même que ton algorithme utilise de façon essentielle que les chiffres seront toujours parmi 1, 2 ou 3 et donc qu'on n'a jamais une succession de plus de trois chiffres identiques (qui en a une preuve ?) et aussi que la suite 333 n'est jamais présente.
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          15 juillet 2010 à 23:52:21

                                          Effectivement :

                                          def rep2(n,old,new):
                                              newn,lo,ln="",len(old),len(n)
                                              c=0
                                              while c<ln:
                                                  if n[c:c+lo]==old:
                                                      newn+=new
                                                      c+=lo
                                                  else:
                                                      newn+=n[c]
                                                      c+=1
                                              return newn
                                          
                                          #def replace(n,old,new):
                                          #    return n.replace(old,new)
                                          replace = rep2
                                          
                                          def conway5(nbr=50): #conway2
                                              n = "1"
                                              for i in range(nbr):
                                                  n=replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(n,'111','ca'),'222','cb'),'11','ba'),'22','bb'),'33','bc'),'1','aa'),'2','ab'),'3','ac'),'a','1'),'b','2'),'c','3')
                                              return n
                                          
                                          for cnw in (conway,conway2,conway3,conway4,conway5):
                                              t=clock()
                                              cnw(50)
                                              print cnw.func_name," : ",clock()-t
                                          


                                          Résultat :

                                          conway  :  2.19650645333
                                          conway2  :  0.3873088
                                          conway3  :  0.292969386667
                                          conway4  :  1.42799402667
                                          conway5  :  20.2548253867
                                          


                                          On en conclut que la méthode replace des strings est une bif extrêmement rapide.


                                          333 est impossible car une telle succession implique qu'elle soit déjà présente avant (peut se lire X3-33 ou 33-3X, dans les deux cas on a le 33).

                                          Toute suite de plus de 3 chiffres identiques est impossible; X3333 <- 333333 ou [X*3]333YYY , ces deux configurations devraient normalement aboutir à 63 ou (43,53 ou 63) respectivement. Même chose avec 2 ou 1, on se rend compte que la configuration dont l'actuelle est sensée être issue aboutit normalement à une autre.

                                          Edit:
                                          Et c'est encore pire avec la version plus élégante qui m'aurait évité de de recoder la fonction d'ordiclic:


                                          from time import clock
                                          class Nstr(str):
                                              def __add__(self,ch):
                                                  return Nstr(str.__add__(self,ch))
                                              def replace(self,old,new):
                                                  newn,lo,ln=Nstr(""),len(old),len(self)
                                                  c=0
                                                  while c<ln:
                                                      if self[c:c+lo]==old:
                                                          newn+=new
                                                          c+=lo
                                                      else:
                                                          newn+=self[c]
                                                          c+=1        
                                                  return newn
                                          
                                          def conway(nbr=50): #ordiclic
                                              n = Nstr("1")
                                              for i in range(nbr):
                                                  n=n.replace('111','ca').replace('222','cb').replace('11','ba').replace('22','bb').replace('33','bc').replace('1','aa').replace('2','ab').replace('3','ac').replace('a','1').replace('b','2').replace('c','3')
                                              return n
                                          
                                          for cnw in [conway]:
                                              t=clock()
                                              cnw(40)
                                              print cnw.func_name," : ",clock()-t
                                          
                                          #conway  :  29.6537642667 (pour seulement 40...)
                                          


                                          Comme quoi...
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            15 juillet 2010 à 23:57:00

                                            Moi j'en ai une preuve.

                                            Citation

                                            J'en conclus en particulier qu'il faut laisser le plus possible aux fonctions de Python le soin de faire le travail plutôt que tout refaire nous-mêmes.


                                            Clairement : ces fonctions compilées iront bien plus vite qu'une boucle écrite en Python. C'est d'ailleurs pour ça que je pensais que mon second code irait plus vite.
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              16 juillet 2010 à 1:23:28

                                              Citation : EMC1


                                              333 est impossible car une telle succession implique qu'elle soit déjà présente avant (peut se lire X3-33 ou 33-3X, dans les deux cas on a le 33).



                                              Je trouve pas cette explication très claire : j'ai envie de t'objecter que je pourrais imaginer qu'à l'étape précédente de 333, j'aie, (au hasard) 123332 un successifs (cad une suite 111111111...11111 contenant 123332 fois le chiffre 1 et encadrée par d'autre nombres que 1) et donc à l'étape qui nous intéresse, on aurait la séquence <math>\(12\fbox{333}21\)</math>

                                              Pour écarter l'éventualité d'un 333, il faut déjà avoir montré qu'on n'a jamais 4 répétitions et à ce moment c'est immédiat à voir (un peu délicat à écrire pour qu'un interlocuteur comprenne bien).

                                              Citation : EMC1


                                              Toute suite de plus de 3 chiffres identiques est impossible; X3333 <- 333333 ou [X*3]333YYY , ces deux configurations devraient normalement aboutir à 63 ou (43,53 ou 63) respectivement. Même chose avec 2 ou 1, on se rend compte que la configuration dont l'actuelle est sensée être issue aboutit normalement à une autre.



                                              Excuse-moi, mais je trouve vraiment pas ton raisonnement clair ni convaincant (oui pour les maths aussi je suis chiant :( )


                                              Citation : EMC1


                                              from time import clock
                                              class Nstr(str):
                                                  def __add__(self,ch):
                                                      return Nstr(str.__add__(self,ch))
                                                  def replace(self,old,new):
                                                      newn,lo,ln=Nstr(""),len(old),len(self)
                                                      c=0
                                                      while c<ln:
                                                          if self[c:c+lo]==old:
                                                              newn+=new
                                                              c+=lo
                                                          else:
                                                              newn+=self[c]
                                                              c+=1        
                                                      return newn
                                              
                                              def conway(nbr=50): #ordiclic
                                                  n = Nstr("1")
                                                  for i in range(nbr):
                                                      n=n.replace('111','ca').replace('222','cb').replace('11','ba').replace('22','bb').replace('33','bc').replace('1','aa').replace('2','ab').replace('3','ac').replace('a','1').replace('b','2').replace('c','3')
                                                  return n
                                              
                                              for cnw in [conway]:
                                                  t=clock()
                                                  cnw(40)
                                                  print cnw.func_name," : ",clock()-t
                                              
                                              #conway  :  29.6537642667 (pour seulement 40...)
                                              



                                              Comme quoi...




                                              Abominablement lent (dès que n est un peu grand), et finalement assez instructif, ça donne pas envie de surcharger des méthodes builtin :(




                                              Citation : Maxibolt

                                              Moi j'en ai une preuve.



                                              Moi aussi je pense, c'est assez facile à voir mais chiant à expliquer :

                                              d'abord, par définition de la suite de Conway, toute ligne L est une succession de <math>\(\overline{n_i}c_i\)</math> où <math>\(\overline{n_i}\)</math> est la représentation en base 10 de <math>\(n_i\)</math> et où <math>\(c_{i-1}\neq c_i\)</math> avec <math>\(c_i\)</math> désignant le chiffre répété et <math>\(n_i\)</math> le nombre d'occurrences successives de <math>\(c_i\)</math> à la ligne précédente de L. Maintenant, supposons qu'apparaisse une ligne contenant une séquence cccc de 4 chiffres identiques valant c et soit L la 1ère ligne où apparait une telle succession. En essayant d'identifier notre séquence cccc à une succession de <math>\(\overline{n_i}c_i\)</math> dans L, vu l'hypothèse que <math>\(n_i\leq 3\)</math> et donc que <math>\(n_i\)</math> ne prend qu'une seule position puisque c'est un nombre à un seul chiffre, on aurait <math>\(c_i=c_{i+1}\)</math> : contradiction.




                                              Citation : Maxibolt


                                              Clairement : ces fonctions compilées iront bien plus vite qu'une boucle écrite en Python.



                                              Et je me dis alors : C (ou C++) + Python, c'est l'arme fatale (efficacité + simplicité) !!

                                              EDIT néanmoins le C pur reste quand même beaucoup plus rapide que Python, par exemple ici l'équivalent de l'algo naïf en C tourne 5 à 8 fois plus vite.
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                16 juillet 2010 à 10:47:49

                                                Citation : candide


                                                Par ailleurs, je ne connaissais pas print >>


                                                Moi non plus je ne connaissais pas avant cet exercice. Il a fallu creuser un peu pour obtenir le comportement que je voulais.

                                                Sinon pour retenir une morale de tout cela : les performances de replace sont uniquement liées au fait que cette fonction soit compilée? L'algorithme utilisé par cette fonction n'est à priori et si on le mettait sur un pied d'égalité, pas plus rapide que ce que l'on a pu proposer d'autre ?
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                Inkamath on GitHub - Interpréteur d'expressions mathématiques. Reprise du développement en cours.
                                                  16 juillet 2010 à 12:41:39

                                                  Citation : iNaKoll

                                                  les performances de replace sont uniquement liées au fait que cette fonction soit compilée?



                                                  C'est ce que je dirais.


                                                  Citation : iNaKoll

                                                  L'algorithme utilisé par cette fonction n'est à priori et si on le mettait sur un pied d'égalité, pas plus rapide que ce que l'on a pu proposer d'autre ?



                                                  Je viens de regarder dans le code source de Cpython, le coeur de la méthode replace est implémenté par la fonction replace_substring qui bien que technique dans son code ne semble avoir rien de spécialement sophistiquée.
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    16 juillet 2010 à 22:23:04

                                                    Bon allez je me lance !

                                                    C'est sans doute loin d'être parfait mais ça a l'air de fonctionner pas mal (je n'ai pas testé les temps de calcul)

                                                    #!/usr/bin/python
                                                    # -*- coding: utf-8 -*-
                                                    
                                                    # SuiteConvey.py
                                                    
                                                    """
                                                    Ce programme génére la suite de Convey par itérations.
                                                    On commence le programme à l'itération n=1
                                                    """
                                                    
                                                    ## A FAIRE : Créer une fonction qui compare le résultat à celui présent dans 1 fichier "étalon"
                                                    
                                                    ## Imports
                                                    import sys
                                                    from itertools import groupby
                                                    
                                                    ## Fonctions utilitaires
                                                    def iter_convey(boucle_iter1):
                                                        """
                                                        itération de la suite de Convey :
                                                        Prend le paramétre d'entrée boucle_iter1 (itération n) et renvoie iter 2 l'itération n+1
                                                        """
                                                        if boucle_iter1 == '0': # Si n = 0:
                                                            iter2 = '1' # Valeur pour n = 1
                                                        else: # Si n > 0:
                                                            iter1 = list(boucle_iter1) # On transforme boucle_iter1 en une liste de ses propres chiffres
                                                            # On en tire chaque groupe de chiffres répétés grace à groupby
                                                            # Puis On ajoute à iter2 : 1) le nombre de fois où le chiffre est répété et 2) le chiffre
                                                            iter2 = ''.join([str(len(list(groupe))) + chiffre for chiffre, groupe in groupby(iter1)])
                                                        return iter2 # Et on renvoie iter2
                                                    
                                                    def test_option_entier(test_str, test_max , test_min = 0):
                                                        """
                                                        Teste si la valeur test_str (string) est bien un entier compris entre les limites test_min (par défaut 0) et test_max
                                                        On retourne la valeur True si c'est le cas et False sinon
                                                        """
                                                        result = True
                                                        try : # on vérifie que la valeur saisie est un entier
                                                            if int(test_str) not in range(test_min, test_max + 1): # Si la saisie n'est pas comprise dans la liste proposée: MAJ du résultat
                                                                result = False
                                                        except : # si erreur, MAJ du résultat
                                                            result = False
                                                        return result
                                                    
                                                    
                                                    ## Corps du programme
                                                    
                                                    # Définition des variables
                                                    nmax = 0 # nombre d'itérations à effectuer à partir de la valeur 1 incluse. Si nmax = 0 alors le programme ne fera pas d'itération
                                                    n = 0 # compteur d'itérations effectuées (initialisé à 0)
                                                    main_iter1 = '0' # valeur de la suite à l'itération n (initialisé à '0' pour n = 0)
                                                    consencode = sys.stdout.encoding # Encodage des sorties texte basé sur la console
                                                    options = [u"Afficher la valeur de la suite pour l'itération n", u"Afficher toutes les valeurs pour les itérations de 1 à n",\
                                                               u"Sauver toutes les valeurs pour les itérations de 1 à n", u"Charger un fichier de résultat", u"Quitter"]
                                                    
                                                    # Demande des options de l'utilisateur
                                                    essais = 10 # nombre d'essais auxquels l'utilisateur a droit pour saisir les options
                                                    while essais > 0: # si il reste des essais possibles:
                                                        essais = essais - 1 # MAJ essais restants
                                                        print u'\nQue voulez-vous faire ?'.encode(consencode, 'replace')
                                                        for index, option in enumerate(options):
                                                            print (u'%s : %s' % (unicode(index + 1), option)).encode(consencode, 'replace')
                                                        saisie = raw_input(u'Choix + [Entrée]: '.encode(consencode, 'replace'))
                                                        if test_option_entier(test_str = saisie, test_max = len(options), test_min = 1) == False: #si saisie incorrecte :
                                                            print u'Saisie incorrecte, rentrez un entier compris dans les valeurs proposées!'.encode(consencode, 'replace')
                                                            continue
                                                        else: # Si la saisie est correcte :
                                                            saisie = int(saisie)
                                                            if saisie == 5 : # Si dernière option (=quitter) : sortie de boucle et quitter
                                                                print u'Sortie du programme'.encode(consencode, 'replace')
                                                                break
                                                            elif saisie < 4: # Sinon :
                                                                # gestion du nombre d'itérations (pour les options 1, 2 et 3)
                                                                essais = 10
                                                                saisie1 = raw_input(u"\nChoix du nombre d'itérations (entre 1 et 100) + [Entrée]: ".encode(consencode, 'replace'))
                                                                if test_option_entier(test_str = saisie1, test_max = 100, test_min = 1) == False: #si saisie incorrecte :
                                                                    print u'Saisie incorrecte, rentrez un entier compris dans les valeurs proposées!'.encode(consencode, 'replace')
                                                                    continue
                                                                else: # Si la saisie est correcte :
                                                                    nmax = int(saisie1)
                                                            # Confirmation chargement /  sauvegarde (pour les options 3 et 4)
                                                            if saisie == 3:
                                                                saisie1 = raw_input(u"\nLes résultats seront sauvegardés dans le fichier 'ConvResult.txt' dans le dossier du présent programme\nConfirmez (o) ou annulez (n'importe-quelle autre touche) :".encode(consencode, 'replace'))
                                                                if saisie1 != 'o': #si annulation :
                                                                    continue
                                                                else: # Si confirmation :
                                                                    break
                                                            elif saisie == 4:
                                                                saisie1 = raw_input(u"\nLe fichier 'ConvResult.txt' sera chargé dans le dossier du présent programme\nConfirmez (o) ou annulez (n'importe-quelle autre touche) :".encode(consencode, 'replace'))
                                                                if saisie1 != 'o':
                                                                    continue
                                                                else:
                                                                    break
                                                            else:
                                                                break
                                                                
                                                    # Gestion de la sortie de boucle d'options
                                                    if essais == 0: # Si sortie et nombre d'essais dépassé :
                                                        print u"\nNombre d'essais dépassé, sortie du programme".encode(consencode, 'replace')
                                                    elif saisie < 4: # Si sortie et itérations demandées (options 1 à 3):
                                                        print (u"\nIterons %s fois:" % nmax).encode(consencode, 'replace')
                                                        fichier_sauv = (open('ConvResult.txt', 'w') if saisie == 3 else None) # Ouverture du fichier si option 3
                                                        try: # Essai d'itération + affichage et sauvegarde des résultats
                                                            for n in range(nmax): # Boucle d'itérations
                                                                main_iter1 = iter_convey(boucle_iter1 = main_iter1) # Itération à n+1
                                                                if saisie == 2 or (saisie == 1 and n == nmax - 1): # Si l'option 2 ou fin de boucle et option 1: on affiche le résultat à n+1
                                                                    print "%s %s" % (n + 1, main_iter1)
                                                                elif saisie == 3: # Si option 3 : sauvegarde du résultat n+1
                                                                    fichier_sauv.write("%s %s\n" % (n + 1, main_iter1))
                                                            print u'Fin'.encode(consencode, 'replace') # Fin de boucle
                                                        except:
                                                            print u'Il y a eu une erreur pendant les itérations !'.encode(consencode, 'replace')
                                                        finally:
                                                            if saisie == 3 : fichier_sauv.close()
                                                    elif saisie == 4: # Si chargement fichier demandé :
                                                        print (u"\nChargement du fichier ").encode(consencode, 'replace')
                                                        try: # Essai de chargement et d'affichage des résultats:
                                                            fichier_charg = open('ConvResult.txt', 'r')
                                                            print (u"\nAffichage des résultats :").encode(consencode, 'replace')
                                                            for result in fichier_charg : print result.rstrip("\n")
                                                            print u'Fin'.encode(consencode, 'replace')
                                                        except:
                                                            print u"Erreur de chargement, le fichier n'existe pas dans le présent dossier ou est corrompu !".encode(consencode, 'replace')
                                                        finally:
                                                            fichier_charg.close()
                                                    
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      17 juillet 2010 à 12:23:23

                                                      Voilà ce que j'ai trouvé sur Wikipédia

                                                      import itertools
                                                       
                                                        def conway():
                                                            x = '1'
                                                            while True:
                                                                yield x
                                                                nx = ''
                                                                for item, grouper in itertools.groupby(x):
                                                                    nx += '%d%s' % (len(list(grouper)), item)
                                                                x = nx
                                                       
                                                        suite = conway()
                                                        for i in range(10):
                                                            print suite.next()
                                                      
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        17 juillet 2010 à 12:37:01

                                                        Citation : Yohan88

                                                        Voilà ce que j'ai trouvé sur Wikipédia




                                                        Plus précisément, le code est ICI.

                                                        Merci, tout à fait intéressant car code très compact et bien lisible.
                                                        Toutefois, les performances sont assez moyennes.
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          17 juillet 2010 à 12:41:31

                                                          Rien ne vaut le Perl :D

                                                          for ( $_=1;; ) { say; s/((.)\2*)/length($1).$2/xeg; }
                                                          


                                                          (Source : Wikipédia, même article)
                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            17 juillet 2010 à 12:46:37

                                                            Citation : Yohan88

                                                            Rien ne vaut le Perl :D

                                                            for ( $_=1;; ) { say; s/((.)\2*)/length($1).$2/xeg; }
                                                            





                                                            Citation : zen de Python

                                                            Readability counts


                                                            ;)
                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              17 juillet 2010 à 12:48:07

                                                              Depuis le shell, selon Wikipédia toujours

                                                              perl -E 'for ( $_=1;; ) { say; s/((.)\2*)/length($1).$2/xeg; }'
                                                              
                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              [exercice] La suite de Conway

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown