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 :

Quel est le plus petit nombre que Python puisse utiliser? [Python 3.X]


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de ryan
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2003
    Messages
    956
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

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

    Informations forums :
    Inscription : Juin 2003
    Messages : 956
    Billets dans le blog
    1
    Par défaut Quel est le plus petit nombre que Python puisse utiliser?
    Hello!

    Je m'amuse un peu avec l'ensemble de Mandelbrot. Le but est de zoomer le plus loin possible en diminuant le pas d'incrémentation (la différence de valeur d'abscisse et d'ordonnée entre deux positions contigües symbolisées par deux pixels contigus)

    Je vois dans la doc que la limite inférieure pour les float est de 10e-308.

    Jusqu'à un pas d'incrémentation de 1e-16, mon code affiche bien des pixels distincts à chaque incrémentation, j'en conclus que les calculs se passent bien.

    Par contre, dès que je passe à un pas d'incrémentation de 1e-17, le code commence à afficher des blocs de pixels, comme s'il n'arrivait plus à incrémenter correctement les valeurs d'abscisse et d'ordonnée.

    Voici le code de la fonction qui calcule et affiche.

    step est le fameux pas d'incrémentation.
    x_start et y_start sont les valeurs d'abscisse et d'ordonnée du coin supérieur gauche de la zone à afficher.
    rows_number et lines_number sont les nombres de pixels qui déterminent la taille du canevas à afficher.

    Les autres paramètres se rapportent à la gestion des couleurs et à la façon dont les boucles sont incrémentées (pour permettre des pré-visualisation rapide avant de lancer les calculs complets)

    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
     
    def draw_selection(x_start, y_start, step, iteration_and_color_offset, loop_increment):
     
        for x_pixel in range(1, rows_number, loop_increment):
     
            for y_pixel in range(1, lines_number, loop_increment):
     
                x_value = x_start + (x_pixel * step)
                y_value = y_start + (y_pixel * step)
                x = 0
                y = 0
                iterations = 0
     
                while x * x + y * y <= 4:
     
                    if iterations <= nb_colors + iteration_and_color_offset:
     
                        x_temp = x * x - y * y + x_value
                        y = 2 * x * y + y_value
                        x = x_temp
                        array_index = iterations - iteration_and_color_offset
     
                        pixel_color = color_array[array_index]
     
                        iterations = iterations + 1
     
                    else:
     
                        break
     
                cnv.create_line(x_pixel + 1, y_pixel, x_pixel + 2, y_pixel + 1, fill=pixel_color)
     
            cnv.update()
    Je me demande si le problème vient d'une limitation dans les valeurs qu'un float peut contenir, ou bien s'il vient d'une faiblesse de mon code...

    Merci d'avance pour vos avis éclairés.

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 754
    Par défaut
    Citation Envoyé par ryan Voir le message
    Je me demande si le problème vient d'une limitation dans les valeurs qu'un float peut contenir, ou bien s'il vient d'une faiblesse de mon code...
    Les valeurs côté float se testent en les visualisant avec "print" (par exemple).

    Après ce que vous constatez est côté affichage de... par le Canvas de pixels qui ont eux une dimension fixe.

    Cela étant montrer du code sans qu'on puisse reproduire ce que vous constatez ne permet pas de voir ce que vous constatez... et limite quelque peu le champ des possibles à vos réflexions.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    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 838
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par ryan Voir le message
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    iterarion=0
    while x * x + y * y <= 4:
    	if iterations <= nb_colors + iteration_and_color_offset:
    		x_temp = x * x - y * y + x_value
    		y = 2 * x * y + y_value
    		x = x_temp
    		array_index = iterations - iteration_and_color_offset
    		pixel_color = color_array[array_index]
    		iterations = iterations + 1
    	else:
    		break
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    iterarion=0
    while x * x + y * y <= 4:
    	if iterations > nb_colors + iteration_and_color_offset: break
     
    	x_temp = x * x - y * y + x_value
    	y = 2 * x * y + y_value
    	x = x_temp
    	array_index = iterations - iteration_and_color_offset
    	pixel_color = color_array[array_index]
    	iterations += 1
    Et voilà comment on évite de perdre un niveau d'indentation.

    Après, on peut se baser sur l'itération comme support de boucle (ça me semble un peu plus adapté) ce qui permet de supprimer l'initialisation et l'incrément...
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    for iteration in range(nb_colors + iteration_and_color_offset + 1):
    	if x * x + y * y > 4: break
     
    	x_temp = x * x - y * y + x_value
    	y = 2 * x * y + y_value
    	x = x_temp
    	array_index = iterations - iteration_and_color_offset
    	pixel_color = color_array[array_index]
    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. #4
    Membre Expert
    Avatar de ryan
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2003
    Messages
    956
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

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

    Informations forums :
    Inscription : Juin 2003
    Messages : 956
    Billets dans le blog
    1
    Par défaut
    Hello!

    @Sve@r
    Merci pour l'optimisation, je n'en étais pas encore à ce stade. Dans un premier temps, je préfère garder un code très lisible, vu que je débute en python et que je ne suis pas encore habitué à la syntaxe. Quand j'aurai résolu tous les problème, je me pencherai sur l'optimisation.

    Sinon, j'ai isolé la partie calcul de la partie interface. J'ai initialisé les paramètres pour faire pointer la fenêtre d'affichage sur un endroit de l'ensemble où il y a qqchose à voir, c'est plus pratique

    Résumé du problème: la variable xy_step contient ce que j'appelle le pas d'incrémentation. C'est ce qui est ajouté à l'abscisse et/ou à l'ordonnée chaque fois qu'on passe au pixel suivant dans la ligne ou qu'on passe à la ligne de pixels suivante. Pour zoomer dans l'ensemble de Mandelbrot, on réduit ce pas d'incrémentation.

    Jusqu'à 1e-15, tout va bien, chaque pixel est bien distinct. Ensuite, quand je passe à 1e-16, je commence à voir des pixels qui se groupent, ce qui ne devrait pas arriver. Plus on réduit la pas d'incrémentation, plus le problème s'accentue.

    C'est comme si on arrivait à la limite de la précision dont est capable Python, bien que dans la doc, je lis que les float peuvent descendre jusqu'à 10e-308. Je croyais avoir encore de la marge...

    Dans le code ci-dessous, j'ai commenté les lignes qui initialisent différentes valeurs de la variable xy_step. Il suffit donc de les dé-commenter pour zoomer et voir apparaître le problème.

    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
    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
     
    from tkinter import *
     
     
    def draw_selection(x_start, y_start, step, iteration_and_color_offset, loop_increment):
     
        global drawing_done
     
        drawing_done = False
     
        root.title("Calculs en cours...")
        root.update()
     
        cnv.delete("all")
     
        for x_pixel in range(1, rows_number, loop_increment):
     
            for y_pixel in range(1, lines_number, loop_increment):
     
                x_value = x_start + (x_pixel * step)
                y_value = y_start + (y_pixel * step)
                x = 0
                y = 0
                iterations = 0
                pixel_color = "white"
     
                while x * x + y * y <= 4:
     
                    if iterations <= nb_colors + iteration_and_color_offset:
     
                        x_temp = x * x - y * y + x_value
                        y = 2 * x * y + y_value
                        x = x_temp
                        array_index = iterations - iteration_and_color_offset
     
                        if array_index <= 1:
                            array_index = 1
     
                        pixel_color = color_array[array_index]
     
                        iterations = iterations + 1
     
                    else:
     
                        break
     
                #cnv.create_line(x_pixel + 1, y_pixel, x_pixel + 2, y_pixel + 1, fill=pixel_color)
                cnv.create_line(x_pixel, y_pixel, x_pixel + 1, y_pixel + 1, fill=pixel_color)
     
            cnv.update()
     
        drawing_done = True
     
        root.title("Calculs terminés")
        root.update()
     
     
    color_array = ("#000000", "#000102", "#000205", "#000409", "#000511",
                   "#010615", "#010617", "#01081c", "#010a23", "#020c28",
                   "#030e30", "#031037", "#04133f", "#051646", "#07194e",
                   "#081c55", "#0a1f5d", "#0f2267", "#132670", "#182979",
                   "#1c2d83", "#21318c", "#243594", "#243c97", "#23439b",
                   "#234a9e", "#2251a1", "#2257a5", "#215ea8", "#2166ab",
                   "#206eaf", "#1f76b3", "#1e7eb7", "#1e86bb", "#1d8ebe",
                   "#2094c0", "#269ac1", "#2ca0c1", "#32a7c2", "#2fa4c2",
                   '#31a7c1', '#34a7c2', '#35a9c2', '#36aac3', '#38acc3',
                   '#39adc4', '#39aec2', '#3aafc3', '#3bb0c2', '#3db0c3',
                   '#3eb3c4', '#3fb5c3', '#40b6c4', '#40b6c2', '#43b7c4',
                   '#45b7c2', '#47b7c3', '#49bac2', '#4ab9c2', '#4dbac1',
                   '#4fbac0', '#51bcc2', '#53bcc0', '#55bcc1', '#56bebf',
                   '#59bfc1', '#5bbfbf', '#5dbfbe', '#5ec0bf', '#61c1bf',
                   '#64c3bf', '#65c4be', '#68c4bf', '#6ac5bc', '#6cc6bd',
                   '#6fc6bd', '#70c7be', '#72c8bd', '#75c9bc', '#77c9bd',
                   '#78cbbb', '#7acbbc', '#7bccbb', '#7eccbc', '#80cdb9',
                   '#83ceba', '#84cfba', '#87d1ba', '#89d1b9', '#8bd1b9',
                   '#8ed2b9', '#91d3b8', '#92d4b9', '#94d5b7', '#97d6b9',
                   '#9ad7b8', '#9bd8b7', '#9dd8b8', '#a0d9b8', '#a2dab7',
                   '#a3dbb6', '#a6dcb8', '#a9ddb6', '#abddb7', '#acdfb6',
                   '#afdfb7', '#b1e0b6', '#b4e1b6', '#b7e2b5', '#b8e3b5',
                   '#bae4b4', '#bde5b3', '#c0e6b5', '#c1e7b4', '#c5e7b4',
                   '#c6e8b5', '#c7eab4', '#c8e9b2', '#c9eab3', '#cbe9b3',
                   '#ccebb2', '#ceebb3', '#cfecb2', '#d0ecb2', '#d1edb3',
                   '#d2eeb3', '#d5efb2', '#d6f0b3', '#d7efb3', '#d7efb1',
                   '#daf0b2', '#ddf1b2', '#def3b2', '#dff2b2', '#e0f3b1',
                   '#e4f5b1', '#e6f5b2', '#e9f6b1', '#ebf6b1', '#edf8b3',
                   '#eef8b3', '#eff8b5', '#f0f9b8', '#f0f9ba', '#f2fabe',
                   '#f5fac2', '#f3fbc2', '#f4fbc5', '#f7fbc8', '#f8fcc9',
                   '#f9fccd', '#fbfcd0', "#fbfdd3",  "#fdfed6", "#fefed8")
     
    nb_colors = len(color_array) - 1
     
    drawing_done = False
     
    root_horizontal_margin = 0
    root_vertical_margin = 74
    interface_width = 150
     
    root = Tk()
    root.resizable(False, False)
    root.title("")
     
    rows_number = root.winfo_screenwidth() - root_horizontal_margin - interface_width
    lines_number = root.winfo_screenheight() - root_vertical_margin
     
    x_center = -0.175390781769355
    y_center = -1.024562467958025
     
    # 1e-15
    xy_step = 0.000000000000001
     
    # 1e-16
    # xy_step = 0.0000000000000001
     
    # 1e-17
    # xy_step = 0.00000000000000001
     
    # 1e-18
    # xy_step = 0.000000000000000001
     
    print(xy_step)
     
    loop_step = 1
    color_offset = 220
     
    x_min = x_center - (rows_number * xy_step / 2)
    y_min = y_center - (lines_number * xy_step / 2)
     
    x_pointer_coord = int(rows_number / 2)
    y_pointer_coord = int(lines_number / 2)
     
    root.geometry(str(rows_number + interface_width) + "x" + str(lines_number) + "+0+0")
     
    left_frame = Frame(root)
    left_frame.pack(side=LEFT)
     
    right_frame = Frame(root)
    right_frame.pack(side=TOP, fill='both', expand=True)
     
    cnv = Canvas(left_frame, width=rows_number, height=lines_number, bg='black', highlightthickness=0)
    cnv.pack(side=LEFT)
     
    draw_selection(x_min, y_min, xy_step, color_offset, loop_step)
     
    root.mainloop()
    Je suis preneur de toute piste de réflexion qui me permettrait de passer cette barrière des 1e-15.

    Edit: Je viens de visionner une vidéo où on peut voir le même problème que celui qui m'occupe. D'après le commentaire de la vidéo, il semblerait que ce soit un problème de mémoire.
    Voir à la 43ème seconde de cette vidéo:

    Venant du développement web, ces potentiels problèmes de mémoire me sont totalement étrangers. Va falloir que je creuse dans cette direction...

  5. #5
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    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 838
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ryan Voir le message
    Merci pour l'optimisation, je n'en étais pas encore à ce stade. Dans un premier temps, je préfère garder un code très lisible, vu que je débute en python et que je ne suis pas encore habitué à la syntaxe. Quand j'aurai résolu tous les problème, je me pencherai sur l'optimisation.
    C'est pas de l'optimisation, c'est justement de la lisibilité. Typiquement un else: break c'est une verrue. Dire "si X alors je fais ceci sinon je sors" c'est de la logique à l'envers car en réalité c'est "si pas X je sors ; et ensuite puisqu'à priori je ne suis pas sorti alors je peux faire ceci". Et (accessoirement) on gagne un niveau d'indentation (pour ceux qui respectent l'esthétique du code).
    En tout cas ton truc est magnifique !!!

    Citation Envoyé par ryan Voir le message
    Je suis preneur de toute piste de réflexion qui me permettrait de passer cette barrière des 1e-15.
    Le module decimal...
    Si le souci vient de la précision (ou plus précisément du manque de précision) ce module est fait pour toi.
    Pour résumer, l'imprécision des flottants est un souci archi-connu. il ne vient pas de Python mais de la façon qu'à l'ordinateur de coder un flottant en binaire: par une somme de fractions de 2. Sauf que la majorité des flottants ne peuvent pas être réduits en une somme de fractions de 2 (ex 0.6 sera un tout petit peu plus grand que 1/2^1 + 1/2^4 + 1/2^5 mais un tout petit peu plus petit que 1/2^1 + 1/2^4 + 1/2^5 + 1/2^6 ; et un tout petit peu plus grand que 1/2^1 + 1/2^4 + 1/2^5 + 1/2^8 + 1/2^9 mais un tout petit peu plus petit que 1/2^1 + 1/2^4 + 1/2^5 + 1/2^8+ 1/2^9+ 1/2^10 ; et etc. Et aucune somme de fractions de 1/2^n (n entier) ne pourra tomber exactement sur 0.6.
    D'où le module decimal qui lui travaille en précision absolue. Cette librairie code chaque digit de façon séparée. Ex 0.6 c'est '0' + '.' + '6'. Et comme au primaire, les calculs se font digit par digit. Précision parfaite mais calculs plus longs (évidemment)

    Voici par exemple ton code réécrit en utilisant cet outil
    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
    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
    #!/usr/bin/env python3
    # coding: utf-8
     
     
    from tkinter import *
    import decimal
     
     
    def draw_selection(x_start, y_start, step, iteration_and_color_offset, loop_increment):
     
    	global drawing_done
     
    	drawing_done = False
     
    	root.title("Calculs en cours...(%s)" % step)
    	root.update()
     
    	cnv.delete("all")
     
    	for x_pixel in range(1, rows_number, loop_increment):
     		for y_pixel in range(1, lines_number, loop_increment):
     			x_value = x_start + (x_pixel * step)
    			y_value = y_start + (y_pixel * step)
    			x = decimal.Decimal(0)
    			y = decimal.Decimal(0)
    			iterations = 0
    			pixel_color = "white"
     
    			while x * x + y * y <= 4:
     				if iterations > nb_colors + iteration_and_color_offset: break
     
    				x_temp = x * x - y * y + x_value
    				y = 2 * x * y + y_value
    				x = x_temp
    				array_index = iterations - iteration_and_color_offset
     
    				if array_index <= 1: array_index = 1
     
    				pixel_color = color_array[array_index]
     
    				iterations += 1
    			# while
     
    			#cnv.create_line(x_pixel + 1, y_pixel, x_pixel + 2, y_pixel + 1, fill=pixel_color)
    			cnv.create_line(x_pixel, y_pixel, x_pixel + 1, y_pixel + 1, fill=pixel_color)
    		# for
     
    		cnv.update()
    	# for
     
    	drawing_done = True
     
    	root.title("Calculs terminés (%s)" % step)
    	root.update()
    # draw_selection()
     
    color_array = ("#000000", "#000102", "#000205", "#000409", "#000511",
    			   "#010615", "#010617", "#01081c", "#010a23", "#020c28",
    			   "#030e30", "#031037", "#04133f", "#051646", "#07194e",
    			   "#081c55", "#0a1f5d", "#0f2267", "#132670", "#182979",
    			   "#1c2d83", "#21318c", "#243594", "#243c97", "#23439b",
    			   "#234a9e", "#2251a1", "#2257a5", "#215ea8", "#2166ab",
    			   "#206eaf", "#1f76b3", "#1e7eb7", "#1e86bb", "#1d8ebe",
    			   "#2094c0", "#269ac1", "#2ca0c1", "#32a7c2", "#2fa4c2",
    			   '#31a7c1', '#34a7c2', '#35a9c2', '#36aac3', '#38acc3',
    			   '#39adc4', '#39aec2', '#3aafc3', '#3bb0c2', '#3db0c3',
    			   '#3eb3c4', '#3fb5c3', '#40b6c4', '#40b6c2', '#43b7c4',
    			   '#45b7c2', '#47b7c3', '#49bac2', '#4ab9c2', '#4dbac1',
    			   '#4fbac0', '#51bcc2', '#53bcc0', '#55bcc1', '#56bebf',
    			   '#59bfc1', '#5bbfbf', '#5dbfbe', '#5ec0bf', '#61c1bf',
    			   '#64c3bf', '#65c4be', '#68c4bf', '#6ac5bc', '#6cc6bd',
    			   '#6fc6bd', '#70c7be', '#72c8bd', '#75c9bc', '#77c9bd',
    			   '#78cbbb', '#7acbbc', '#7bccbb', '#7eccbc', '#80cdb9',
    			   '#83ceba', '#84cfba', '#87d1ba', '#89d1b9', '#8bd1b9',
    			   '#8ed2b9', '#91d3b8', '#92d4b9', '#94d5b7', '#97d6b9',
    			   '#9ad7b8', '#9bd8b7', '#9dd8b8', '#a0d9b8', '#a2dab7',
    			   '#a3dbb6', '#a6dcb8', '#a9ddb6', '#abddb7', '#acdfb6',
    			   '#afdfb7', '#b1e0b6', '#b4e1b6', '#b7e2b5', '#b8e3b5',
    			   '#bae4b4', '#bde5b3', '#c0e6b5', '#c1e7b4', '#c5e7b4',
    			   '#c6e8b5', '#c7eab4', '#c8e9b2', '#c9eab3', '#cbe9b3',
    			   '#ccebb2', '#ceebb3', '#cfecb2', '#d0ecb2', '#d1edb3',
    			   '#d2eeb3', '#d5efb2', '#d6f0b3', '#d7efb3', '#d7efb1',
    			   '#daf0b2', '#ddf1b2', '#def3b2', '#dff2b2', '#e0f3b1',
    			   '#e4f5b1', '#e6f5b2', '#e9f6b1', '#ebf6b1', '#edf8b3',
    			   '#eef8b3', '#eff8b5', '#f0f9b8', '#f0f9ba', '#f2fabe',
    			   '#f5fac2', '#f3fbc2', '#f4fbc5', '#f7fbc8', '#f8fcc9',
    			   '#f9fccd', '#fbfcd0', "#fbfdd3",  "#fdfed6", "#fefed8")
     
    nb_colors = len(color_array) - 1
     
    drawing_done = False
     
    root_horizontal_margin = 0
    root_vertical_margin = 74
    interface_width = 150
     
    root = Tk()
    root.resizable(False, False)
    root.title("")
     
    rows_number = root.winfo_screenwidth() - root_horizontal_margin - interface_width
    lines_number = root.winfo_screenheight() - root_vertical_margin
     
    x_center = decimal.Decimal("-0.175390781769355")
    y_center = decimal.Decimal("-1.024562467958025")
     
    # Profondeur
    import sys
    xy_step = decimal.Decimal("1e-%s" % (sys.argv[1] if len(sys.argv) > 1 else "15"))
    print(xy_step)
     
    loop_step = 1
    color_offset = 220
     
    x_min = x_center - (rows_number * xy_step / 2)
    y_min = y_center - (lines_number * xy_step / 2)
     
    x_pointer_coord = int(rows_number / 2)
    y_pointer_coord = int(lines_number / 2)
     
    root.geometry(str(rows_number + interface_width) + "x" + str(lines_number) + "+0+0")
     
    left_frame = Frame(root)
    left_frame.pack(side=LEFT)
     
    right_frame = Frame(root)
    right_frame.pack(side=TOP, fill='both', expand=True)
     
    cnv = Canvas(left_frame, width=rows_number, height=lines_number, bg='black', highlightthickness=0)
    cnv.pack(side=LEFT)
     
    draw_selection(x_min, y_min, xy_step, color_offset, loop_step)
     
    root.mainloop()
    Tu le lances en lui passant la profondeur désirée (16, 17, 18) en paramètre sinon c'est 15 par défaut.
    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]

  6. #6
    Membre Expert
    Avatar de ryan
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2003
    Messages
    956
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

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

    Informations forums :
    Inscription : Juin 2003
    Messages : 956
    Billets dans le blog
    1
    Par défaut
    Hello!

    Merci à Sve@r pour la solution à mon problème. J'ai testé jusqu'à 1e-22 et tout fonctionne comme attendu!

    Les temps de calcul, par contre, deviennent très longs: à ce niveau de zoom, on est à presque 1000 itérations par point, donc rien de vraiment étonnant. Va y avoir un sacré boulot d'optimisation, mais c'est une autre histoire...

  7. #7
    Membre chevronné
    Homme Profil pro
    Développeur banc de test
    Inscrit en
    Mai 2014
    Messages
    199
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur banc de test
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Mai 2014
    Messages : 199
    Par défaut
    Bonjour,
    Très ludique comme sujet.

    Je pense qu'il est possible d'optimiser au niveau de Tkinter, plutôt que de générer pixel par pixel, tkinter peut afficher une image avec tk.PhotoImage par exemple.

    voici une solution rapide pour passer en multiprocessing la génération des lignes.

    La boucle de génération "for y_pixel" a été déplacée dans la fonction generate_line qui sera appelée par les processus pour générer chaque ligne.

    multiprocessing.Pool facilite le partage du calcul automatiquement entre les processus. Dès qu'un calcul se termine sur un des processus, le pool attribue un nouveau calcul à réaliser.

    Vu que Tkinter n'est pas Thread-safe la génération dans le canvas est géré par le main, les autres processus se chargent de calculer les valeurs des lignes.

    Durant mes essais j'ai constaté que cnv.create_line consomme énormément, du coup en configurant nb_processes -1 on laisse un CPU de libre pour le main qui réalise l'affichage des lignes, et dans mon cas je gagne 30% de temps d’exécution.

    Avec un vieux I-2600 (32 nm) @ 3.4 GHz (4 CPU) j'arrive à générer 1024x1024 pixels en 195 secondes.

    Nom : 2021-11-07 16_09_08-Calculs terminés (1E-15).png
Affichages : 139
Taille : 232,4 Ko


    Voici le code :
    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
    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
    #!/usr/bin/env python3
    # coding: utf-8
     
    import decimal # Decimal fixed point and floating point arithmetic
    import multiprocessing # Process-based parallelism
    import sys # System-specific parameters and functions
    import time # Time access and conversions
    import tkinter as tk # Python interface to Tcl/Tk
     
    color_array = ("#000000", "#000102", "#000205", "#000409", "#000511",
                   "#010615", "#010617", "#01081c", "#010a23", "#020c28",
                   "#030e30", "#031037", "#04133f", "#051646", "#07194e",
                   "#081c55", "#0a1f5d", "#0f2267", "#132670", "#182979",
                   "#1c2d83", "#21318c", "#243594", "#243c97", "#23439b",
                   "#234a9e", "#2251a1", "#2257a5", "#215ea8", "#2166ab",
                   "#206eaf", "#1f76b3", "#1e7eb7", "#1e86bb", "#1d8ebe",
                   "#2094c0", "#269ac1", "#2ca0c1", "#32a7c2", "#2fa4c2",
                   '#31a7c1', '#34a7c2', '#35a9c2', '#36aac3', '#38acc3',
                   '#39adc4', '#39aec2', '#3aafc3', '#3bb0c2', '#3db0c3',
                   '#3eb3c4', '#3fb5c3', '#40b6c4', '#40b6c2', '#43b7c4',
                   '#45b7c2', '#47b7c3', '#49bac2', '#4ab9c2', '#4dbac1',
                   '#4fbac0', '#51bcc2', '#53bcc0', '#55bcc1', '#56bebf',
                   '#59bfc1', '#5bbfbf', '#5dbfbe', '#5ec0bf', '#61c1bf',
                   '#64c3bf', '#65c4be', '#68c4bf', '#6ac5bc', '#6cc6bd',
                   '#6fc6bd', '#70c7be', '#72c8bd', '#75c9bc', '#77c9bd',
                   '#78cbbb', '#7acbbc', '#7bccbb', '#7eccbc', '#80cdb9',
                   '#83ceba', '#84cfba', '#87d1ba', '#89d1b9', '#8bd1b9',
                   '#8ed2b9', '#91d3b8', '#92d4b9', '#94d5b7', '#97d6b9',
                   '#9ad7b8', '#9bd8b7', '#9dd8b8', '#a0d9b8', '#a2dab7',
                   '#a3dbb6', '#a6dcb8', '#a9ddb6', '#abddb7', '#acdfb6',
                   '#afdfb7', '#b1e0b6', '#b4e1b6', '#b7e2b5', '#b8e3b5',
                   '#bae4b4', '#bde5b3', '#c0e6b5', '#c1e7b4', '#c5e7b4',
                   '#c6e8b5', '#c7eab4', '#c8e9b2', '#c9eab3', '#cbe9b3',
                   '#ccebb2', '#ceebb3', '#cfecb2', '#d0ecb2', '#d1edb3',
                   '#d2eeb3', '#d5efb2', '#d6f0b3', '#d7efb3', '#d7efb1',
                   '#daf0b2', '#ddf1b2', '#def3b2', '#dff2b2', '#e0f3b1',
                   '#e4f5b1', '#e6f5b2', '#e9f6b1', '#ebf6b1', '#edf8b3',
                   '#eef8b3', '#eff8b5', '#f0f9b8', '#f0f9ba', '#f2fabe',
                   '#f5fac2', '#f3fbc2', '#f4fbc5', '#f7fbc8', '#f8fcc9',
                   '#f9fccd', '#fbfcd0', "#fbfdd3",  "#fdfed6", "#fefed8")
     
    nb_colors = len(color_array) - 1
     
    def generate_line(
        x_start, y_start, step, iteration_and_color_offset, loop_increment,
        x_pixel, lines_number
    ): # {
        global color_array, nb_colors
        line = list()
        for y_pixel in range(1, lines_number, loop_increment):
            x_value = x_start + (x_pixel * step)
            y_value = y_start + (y_pixel * step)
            x = decimal.Decimal(0)
            y = decimal.Decimal(0)
            iterations = 0
            pixel_color = "white"
     
            while x * x + y * y <= 4:
                if iterations > nb_colors + iteration_and_color_offset: break
     
                x_temp = x * x - y * y + x_value
                y = 2 * x * y + y_value
                x = x_temp
                array_index = iterations - iteration_and_color_offset
     
                if array_index <= 1: array_index = 1
     
                pixel_color = color_array[array_index]
     
                iterations += 1
            # while
            line.append(pixel_color)
        # for
        return x_pixel, line
    # } generate_line()
     
    def draw_selection(x_start, y_start, step, iteration_and_color_offset, loop_increment):
     
        global drawing_done, lines_number, rows_number, root, cnv
     
        draw_time_m_ref = time.monotonic()
        drawing_done = False
     
        root.title("Calculs en cours...(%s)" % step)
        root.update()
     
        cnv.delete("all")
     
        try:
            nb_processes = multiprocessing.cpu_count() # Nombre de processus/CPU utilisés pour le pool de calcul des portions
            if nb_processes > 1: # Conserve 1 CPU pour le main qui gère la création du canvas
                nb_processes -= 1
            pool_processes = multiprocessing.Pool( # Distribution des tâches automatiquement sur les processus / CPU
                processes=nb_processes, # Nombre de processus/CPU
                # maxtasksperchild=1, # To free memory after each processing
            )
            lines_processes = list()
            for x_pixel in range(1, rows_number, loop_increment):
                # Génération des lignes
                lines_processes.append(
                    pool_processes.apply_async(
                        generate_line,
                        (x_start, y_start, step, iteration_and_color_offset, loop_increment, x_pixel, lines_number),
                    )
                )
            # for
     
            while len(lines_processes):
                for i_comp, line_process in enumerate(lines_processes):
                    if line_process.ready(): # Calcul de la ligne terminée
                        x_pixel, line = line_process.get(None)
                        for y_pixel, pixel_color in enumerate(line):
                            cnv.create_line(x_pixel, y_pixel, x_pixel + 1, y_pixel + 1, fill=pixel_color)
                        cnv.update()
                        lines_processes.pop(i_comp) # Retire le processus terminé
                        break # Pour reprendre dans l'ordre les colonnes
                    # if
                # for
                time.sleep(0.01)
            # while
        # try
        finally:
            pool_processes.close() # Libère la mémoire des pool/processus
            pool_processes.join() # Attend que tous les processus se terminent
     
        drawing_done = True
     
        root.title("Calculs terminés (%s)" % step)
        root.update()
        print("Fin du calcul, temps écoulé: {:.02f} s\n".format(time.monotonic() - draw_time_m_ref))
    # draw_selection()
     
    if __name__ == '__main__':
        drawing_done = False
     
        root_horizontal_margin = 0
        root_vertical_margin = 74
        interface_width = 0
     
        root = tk.Tk()
        root.resizable(False, False)
        root.title("")
     
        rows_number = 1024 # root.winfo_screenwidth() - root_horizontal_margin - interface_width
        lines_number = 1024 # root.winfo_screenheight() - root_vertical_margin
     
        x_center = decimal.Decimal("-0.175390781769355")
        y_center = decimal.Decimal("-1.024562467958025")
     
        # Profondeur
        xy_step = decimal.Decimal("1e-%s" % (sys.argv[1] if len(sys.argv) > 1 else "15"))
        print(xy_step)
     
        loop_step = 1
        color_offset = 220
     
        x_min = x_center - (rows_number * xy_step / 2)
        y_min = y_center - (lines_number * xy_step / 2)
     
        x_pointer_coord = int(rows_number / 2)
        y_pointer_coord = int(lines_number / 2)
     
        root.geometry(str(rows_number + interface_width) + "x" + str(lines_number) + "+0+0")
     
        left_frame = tk.Frame(root)
        left_frame.pack(side='left')
     
        right_frame = tk.Frame(root)
        right_frame.pack(side='top', fill='both', expand=True)
     
        cnv = tk.Canvas(left_frame, width=rows_number, height=lines_number, bg='black', highlightthickness=0)
        cnv.pack(side='left')
        #img = tk.PhotoImage(width=rows_number, height=lines_number)
        #cnv.create_image((rows_number, lines_number), image=img, state='normal')
     
        draw_selection(x_min, y_min, xy_step, color_offset, loop_step)
     
        root.mainloop()
    # if __main__
    Edit: Ajout d'un time.sleep(0.01) dans la boucle d'attente du calcul des lignes pour éviter de tourner en boucle pour rien

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 07/12/2006, 19h03
  2. Réponses: 3
    Dernier message: 23/09/2006, 21h24
  3. Réponses: 9
    Dernier message: 19/06/2006, 15h29
  4. Quel est le plus performant?
    Par trotters213 dans le forum Langage SQL
    Réponses: 5
    Dernier message: 31/03/2005, 14h23

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