Le Crible d'Eratosthène - Langages fonctionnels - Programmation
Marsh Posté le 02-05-2007 à 22:48:18
Juste une question, car je n'ai rien compris à vos temps d'exécution sous Linux/Unix (je sais je suis nul).
J'ai fait un programme Prolog sous Vista qui génère les nombres premiers par la méthode du crible, il a mis 0.73 s pour obtenir les nombres premiers inférieurs à 10 000, c'est concurrentiel avec vos programmes ou je ferais mieux d'aller me coucher ?
Au passage, je génère tous les nombres de 2 à 10 000.
De toute façon, c'est inutilisable car Prolog explose à partir de 50 000 à cause de l'utilisation de listes.
Marsh Posté le 02-05-2007 à 23:38:22
Trap D a écrit : Juste une question, car je n'ai rien compris à vos temps d'exécution sous Linux/Unix |
gni ? quels temps d'execution ?
Marsh Posté le 03-05-2007 à 02:05:42
L'autre topic sans la section C je pense
http://forum.hardware.fr/forum2.ph [...] w=0&nojs=0
Je dirais (en réponse à l'autre topic), que comparé au truc de Masklinn (qu'il explique bien, mais j'ai toujours rien pigé ) c'est nul à chier.
Par rapport à mon programme en C#, sous Vista aussi, tu arrives à un résultat légèrement inférieur, mais rien de follichon. Reste à savoir ensuite quel est ton processeur et autres paramètres pouvant influer sur le temps de traîtement (moi j'ai un pure processeur, mais sur un portable *sic* et surtout, je tourne en mode débug, ce qui n'est pas là pour arranger les perfs).
Bon, ensuite, l'approche de Masklinn pour cet algo précis est un plus évolué que mon algo, ce qui explique (minablement, je sais) un peu la différence (mais bon, de là à passer de 24 secondes à 0,15 secondes, ça fait mal quand même )
Notamment, Masklinn utilise la récursivité dans on algo, alors que j'utilise une approche séquentielle. Il passeapparement par des listes là où je passe par un bon gros array des familles de plusieurs Mo... Forcément ça fausse un peu la donne
Par contre, je serais intéressé de savoir ce que donne "mon" algo avec Haskell plutôt que le crible machin (qui me semble une pure usine à gaz, à cause de l'énumération inutile de tous les nombres entiers au départ, puis une réévaluation, successive en plus, par la suite).
Marsh Posté le 03-05-2007 à 09:01:46
Ça y'est, j'ai les yeux en face des trous et je comprends les temps d'exécution. Effectivement, mon prog est archi lent, normal c'est de l'interprété et la manipulation des listes est lente et gourmande en mémoire.
J'adore ce que je viens de voir ici : http://www.scriptol.org/langages-populaires.php
"Haskell Programmation fonctionnelle. Lent et gros consommateur de mémoire. pour essayer de programmer autrement. "
Marsh Posté le 03-05-2007 à 09:59:54
Trap D a écrit : Lent et gros consommateur de mémoire. pour essayer de programmer autrement. " |
Ca dépend de l'implémentation (il y a des interpréteurs Haskell et des compilos, GHC est un compilateur), et il est effectivement lent par rapport à d'autres langages (C++ ou C, mais aussi D ou OCaml, OCaml par exemple a un très très bon compilateur)
Marsh Posté le 03-05-2007 à 10:03:05
masklinn a écrit :
|
pourquoi 27 n'est pas dégagé lors du filtrage "multiple de 3"? Un simple oubli?
Marsh Posté le 03-05-2007 à 10:08:10
rufo a écrit : pourquoi 27 n'est pas dégagé lors du filtrage "multiple de 3"? Un simple oubli? |
Ouais, j'ai tapé le truc à la main j'ai laissé passer ça
C'est corrigaid
Marsh Posté le 03-05-2007 à 12:22:11
pour rire, (presque) le meme programme en erlang: (presque, parce que j'utilise un accumulateur )
Code :
|
ou sinon, le programme complet (a sauver dans un fichier erat.erl):
Code :
|
pour l'executer, d'abord le compiler avec
|
puis on le lance (ici pour des entiers jusqu'a 10000):
|
Marsh Posté le 03-05-2007 à 15:23:21
/*
En cours d'édition, grosse connerie dans l'algo qui plombe effectivement les perfs
-- T'ain la grosse tanche, cf.2 posts plus loin...
*/
Masklinn, je pige pas comment t'arrive à trouver les nombres premiers jusqu'à 100 000 000 en 15 secondes...
J'ai beau tortiller le truc dans tous les sens, même en C pur, y'a pas moyen de moyenner, je tourne autour de 50 secondes...
Il aurait pas du mal avec les gros array et oublierait de le dire ton bidule ?
Code :
|
|
Marsh Posté le 03-05-2007 à 15:28:48
Même en collant le timer après le calloc et avant le parcours inutile (le printf en commentaire) je suis à des années lumières de tes 15 secondes... Je vois pourtant pas comment faire plus rapide que du C, et je vois pas ce qui peut clocher avec mon code... unsigned, type de base, pas un seul calcul... pige pas
Et vient pas me dire que t'as un PC récent, j'ai un pur portable de folie (Core 2 Duo T7200 // 2 Go DDR2)
=> Ca donne un lamentable 48605 ms...
Marsh Posté le 03-05-2007 à 15:32:15
Ouais bon, ok, laisse béton, faut que je prenne 3 ans de vacances, ou alors l'euthanasie directe, je sais pas...
Code :
|
Je passe un peu à 7 secondes...
Marsh Posté le 03-05-2007 à 16:08:46
Ah oui mais non la perf initiale est fausse hein, yavait un bug dans le code je le mentionne au début du post, faut l'oublier, les du code qui fonctionne sont plus proches de ce que sort souk
Marsh Posté le 03-05-2007 à 16:11:01
tu veux dire que depuis 24 heures je suis vexé comme un poux d'avoir mon prog C# explosé par ton truc, et que j'ai passé la journée à l'optimiser pour... finalement n'exploser qu'un faux record ?
Marsh Posté le 03-05-2007 à 16:34:23
Ouaip
Marsh Posté le 02-05-2007 à 20:23:12
Implémentation en Haskell, histoire de pouvoir détailler le fonctionnement dans un thread dédié et non dans la sous-cat C
edit: je me suis rendu compte en écrivant le post que j'avais oublié une clause, le programme originel était buggé chuis trop nul![[:sisicaivrai] [:sisicaivrai]](https://forum-images.hardware.fr/images/perso/sisicaivrai.gif)

ca m'apprendra à faire le kéké
Code principal:
Commençons par les helpers (qui ne sont là que pour rendre le code plus lisible)
Utilise le currying pour créer une fonction comparant un Integer (un entier sans limites) à 0: appliquer isNull à 0 renvoie True, l'appliquer à n'importe quoi d'autre renvoie False
Teste si "a" est un multiple de "b":
isMultiple renvoie donc True si a est un multiple de b, False s'il ne l'est pas
Passons maintenant à l'implémentation de l'algo.
Commençons par la définition de la fonction "sieve" (qui prend un unique argument "limit", la limite supérieure des tests de primalité)
sieve :: Integer -> [Integer]
est la signature de "sieve": "sieve" prend un entier en paramètre et renvoie une liste d'entiers
[3,5..limit]
crée une liste de tous les entiers impairs entre 3 et la limite donnée (3, 5, 7, 9, 11, etc...). À noter qu'Haskell a une sémantique dite "lazy evaluation", la liste n'est donc pas générée en mémoire, n'est générée que sa potentialité (on peut trivialement définir une liste infinite en Haskell, on pourrait d'ailleurs trivialement définir le Crible sous forme d'une liste infinie de nombres premiers)
2 : (sieveHelper [3,5..limit])
crée une liste dont le premier élément est "2" et la suite (tail) est le résultat de l'application de "sieveHelper" sur la liste générée au dessus.
sieveHelper prend une liste d'entiers (non premiers) et renvoie une liste d'entiers (premiers)
On arrive maintenant à l'implémentation réelle du crible.
(x:xs) est la syntaxe permettant de "déstructurer" une liste Haskell: le premier élément de la liste ira dans "x" et la queue de la liste ira dans "xs"
Exemple: si on appelle sieveHelper en lui donnant la liste [3, 5, 7, 9] alors on aura x = 3 et xs = [5, 7, 9]
Le premier élément de la liste étant par définition un entier, on le renvoie directement, en lui attachant rest que l'on crée en dessous (on remarquera que cette syntaxe de structuration est exactement la même que celle de déstructuration, utilisée dans un contexte différent)
Génération de rest:
not . (`isMultiple` x)
crée une fonction par currying et composition (avec une définition de closure en bonus), cette fonction (anonyme) teste si l'entier qu'on lui donne en paramètre est un multiple de x (elle renvoie False si le nombre est un multiple de x, et True s'il n'en est pas un)
L'équivalent en Python serait:
filter
est une fonction appliquant son premier argument (une autre fonction) à tous les éléments d'une liste (son 2e argument). Elle renvoie une liste de tous les éléments pour lesquels la fonction a renvoyé True.
Par exemple si on définit une fonction isPair renvoyant True si son paramètre est pair, filter isPair [1, 2, 3, 4, 5] va renvoyer 2, 4.
Ici, on applique donc à notre liste un filtre enlevant tous les multiples du nombre actuel
Puis on réapplique sieveHelper récursivement sur la liste obtenue.
À l'exécution, si on donne le paramètre "30" on a donc:
Appel de sieve
on génère la liste [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]
on appelle sieveHelper [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]
on renvoie 3
on filtre tous les multiples de 3
on appelle sieveHelper sur la liste filtrée [5, 7, 11, 13, 17, 19, 23, 25, 29]
on renvoie 5
on filtre sur tous les multiples de 5
on appelle sieveHelper sur la liste filtrée [7, 11, 13, 17, 19, 23, 29]
on renvoie 7
etc...
Utilisabilité en prod:
nulle, la fonction sieveHelper n'est pas tail-récursive et va donc prendre O(n) en taille de stack au lieu de O(1)
Message édité par masklinn le 03-05-2007 à 10:09:11
---------------
I mean, true, a cancer will probably destroy its host organism. But what about the cells whose mutations allow them to think outside the box by throwing away the limits imposed by overbearing genetic regulations? Isn't that a good thing?