|
Publicité ' | ||||||||||||||||||||||||
|
|
#1 |
![]() ![]() Inscription : juin 2006 Messages : 6 935 ![]() |
Bonjour,
Pour ce sixième défi proposé par SpiceGuid, l'équipe de developpez.com vous propose un challenge assez court qui peut laisser place à l'optimisation. Challenge : Il s'agit d'écrire une fonction (s: string) → string qui renvoie un palindrome p tel que:
On rappelle qu'un palindrome est une chaîne de caractère dont l'ordre de lettre est identique, selon que la lise par la gauche ou par la droite. Formellement, une chaîne s est un palindrome si et seulement si : Pour tout i de 0 à taille(s)-1, s[i] = s[taille(s)-1-i] Les règles Il n'y a pas de règle particulière (évidemment, il faut que la solution proposée fonctionne). Vous pouvez proposer des solutions aussi bien dans des langages fonctionnels (caml, haskell, scheme, lisp...) qu'impératif. Le public pourra ainsi juger du code suivant divers critères :
Le public pourra également juger les différences entre une solution fonctionnelle et une solution impérative. Il lui sera ainsi plus facile de voir, pour un même problème, les différences entre divers paradigmes. Pour répondre, il vous suffit de poster à la suite. A vos claviers de votre participation.__________________________ Sujet proposé par SpiceGuid
__________________
Je ne répondrai à aucune question technique en privé |
|
|
00
|
|
|
#2 | ||||
|
Expert Confirmé Sénior
![]() ![]() |
J'avais une petite solution en Haskell, qui utilise un zipper de liste. Je me suis concocté mon propre zipper mais on peut aussi trouver des implémentations sur Hackage :
MyListZipper.hs : Code Haskell :
Et le code principal : Code Haskell :
L'idée est simple : on commence par isoler les séquences de lettres identiques dans s, puis on essaie de faire "grossir" ces séquences par les deux côtés tant que cela reste un palindrome, enfin on récupère le palindrome le plus long. -- Jedaï |
||||
|
|
00
|
|
|
#3 | ||
|
Expert Confirmé Sénior
![]() ![]() Inscription : octobre 2004 Messages : 4 678 ![]() |
Salut,
Une solution impérative (code en Java) : L'idée est parcourir les lettres de la chaine, en recherchant la lettre courante dans le reste de la chaine. Si on en trouve, on teste si la sous-chaine qui commence à la lettre courante jusqu'à la lettre trouvée est palindrome. Code java :
|
||
|
|
00
|
|
|
#4 | ||||
|
Expert Confirmé
![]() ![]() Inscription : avril 2009 Messages : 2 633 ![]() |
J'espère avoir bien compris qu'il faut réaliser une fonction qui retourne le plus long palindrome d'une chaine de caractère.
La fonction est réalisée en perl, et est basée sur les expressions régulières : Code :
Code :
L'extraction des palindromes est réalisée grâce à la regexp : /((.+).?(??{ reverse "$2" }))/gx à savoir un certain nombre de caractère (le plus possible), suivi éventuellement d'un caractère quelconque, suivi de la première partie déjà trouvée, mais à l'envers. On extrait de cet expression deux chaines : le palindrome et la première partie du palindrome. La palindrome est toujours plus long que la première partie. Ensuite, on trie le résultat par longue décroissante, et on prends le premier élément de cette liste.
__________________
Plus j'apprends, et plus je mesure mon ignorance (philou67430) Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book) Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé Using strict and warnings is good for you. |
||||
|
|
00
|
|
|
#5 | ||
|
Expert Confirmé Sénior
![]() ![]() |
La solution que j'ai donné en premier lieu a une très bonne complexité/efficacité, mais ce n'est évidemment pas le plus simple qu'on puisse faire en Haskell, dans la même optique que la solution Perl, on peut avoir :
Code Haskell :
Complexité absolument horrible bien sûr... (ça reste plus rapide que la solution Perl, même en interprété) La solution de djo.mos est meilleure de ce point de vue mais tout de même plus complexe que ma première solution Haskell. -- Jedaï |
||
|
|
00
|
|
|
#6 |
|
Expert Confirmé
![]() ![]() Inscription : avril 2009 Messages : 2 633 ![]() |
Sauf erreur, Jedaï, je crois que ta fonction isPalindrome ne récupère pas les palindromes de taille impaire.
__________________
Plus j'apprends, et plus je mesure mon ignorance (philou67430) Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book) Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé Using strict and warnings is good for you. |
|
|
00
|
|
|
#7 | |
|
Expert Confirmé Sénior
![]() ![]() |
Citation:
En fait dans ce second code, j'ai favorisé à fond la lisibilité et la simplicité du code, il n'y a aucune astuce, la fonction finale : Code :
longest = maximumBy (comparing length) . filter isPalindrome . allSubstrings -- Jedaï |
|
|
|
00
|
|
|
#8 |
|
Expert Confirmé
![]() ![]() Inscription : avril 2009 Messages : 2 633 ![]() |
Parce que j'ai mal lu le code
__________________
Plus j'apprends, et plus je mesure mon ignorance (philou67430) Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book) Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé Using strict and warnings is good for you. |
|
|
00
|
|
|
#9 | ||
|
Expert Confirmé Sénior
![]() ![]() |
Une solution identique à ma première mais sur un type de donnée différent, spécifiquement sur une chaîne de caractère disposant d'un accès aléatoire en O(1) (String en Haskell est un synonyme pour [Char] autrement dit une simple liste chaînée de caractères) :
Code Haskell :
Cette solution reste complètement fonctionnelle : il n'y a pas le moindre soupçon de mutation ou de code impur dans le tas, simplement il utilise un tableau fonctionnel (immutable) à la place d'une liste chaînée. Je doute qu'on fasse beaucoup mieux que ce code par la suite (du point de vue rapidité). -- Jedaï |
||
|
|
00
|
|
|
#10 |
|
Expert Confirmé Sénior
![]() ![]() |
Il est intéressant de noter qu'encore une fois les critères algorithmiques priment sur la question du langage ou de l'efficacité de la structure de donnée choisie : sur un fichier de taille raisonnable (1,5 Mo, un dictionnaire des mots français), ma première version mettait 0.6s environ et ma troisième 0.06s... Je ne sais pas combien de temps mettent les versions Perl et Java : je les ai arrêté après 3/4 d'heure d'exécution !
La différence tient simplement à la complexité : mes versions 1 et 3 sont en O(np) où p est la longueur du plus grand palindrome), la version Perl est en O(n²) et la version Java également, bien que chacune ait une petite optimisation par rapport à ma version 2 (en O(n²p) pur jus). -- Jedaï |
|
|
00
|
|
|
#11 | ||||
![]() ![]() Xavier PhilippeauArchitecte système Inscription : décembre 2006 Messages : 9 815 ![]() |
Une version impérative (java que j'ai essayé de faire ressemble a du C). Le principe est basé sur l'aspect "miroir" des palindromes: pour chaque caractère de la chaine, on explore simultanément a gauche + a droite jusqu'a rencontrer une différence.
Code java :
EDIT (jeudi à 14h00): Une réecriture de l'algo ci-dessus dans une seule fonction pour avoir une meilleure "maintenabilité" et "simplicité" comme demandé dans l'énoncé. Vive les commentaires. Code java :
__________________
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple. |
||||
|
00
|
|
|
#12 | |
|
Expert Confirmé Sénior
![]() ![]() |
Citation:
-- Jedaï |
|
|
|
00
|
|
|
#13 |
![]() ![]() Xavier PhilippeauArchitecte système Inscription : décembre 2006 Messages : 9 815 ![]() |
heu... oui. Si tu le dis, je veux bien te croire, vu mon incompétence a déchiffrer du haskell.
![]() PS: le worst-case pour cet algo c'est lorsqu'on a des grandes séquences de lettres identiques. Cela peut se régler en faisant une première passe pour "compresser" les sequences.
__________________
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple. |
|
00
|
|
|
#14 | |||
|
Expert Confirmé Sénior
![]() ![]() |
Citation:
Si je faisais exactement comme toi dans ma 3ème version, j'écrirais : Code Haskell :
-- Jedaï |
|||
|
|
00
|
|
|
#15 | ||
|
Invité de passage
![]() Inscription : octobre 2007 Messages : 2 ![]() |
Il me semble que ton algo est O( n * p ) , mais ou p se serait la moyenne de la taille des palindromes et non que celle du plus grand ( ce qui est la meme chose qu'en pire cas ... )
Enfin ... je vous propose un algo qui n'est pas mieux que les votre dans le cas moyen parce qu'il a une constante un peu plus grande que les votre et qu'il n'est qu'en O( n * log(Pmax) ) (en pire cas comme en moyenne ) or je crois que Pmoyen ~= log(Pmax) sur une distribution bien aleatoire donc pas bien mieux ... sauf sur le pire cas ! Le principe de cet algo repose sur le fait que si il n'existe pas de palindrome de taille n ou n+1 (eh oui obliger de distinguer les cas pair et impair ) c'est qu'alors il n'y en a pas de plus grand que n. De la on peut faire une dichotomie. La fonction qui regarde si il n'y a pas de palindrome fonctionne avec un hachage ce qui permet ( globalement ) de considerer que la comparaison des cotes gauches et droites se fait en O(1) si elle echoue et O(P) si elle reussit comme elle ne reussit que une fois par appel on a bien du O(N) pour cette fonction donc un total de O( N ln P ). Code C++ :
Voila! Louis |
||
|
|
00
|
|
|
#16 | ||
|
Expert Confirmé
![]() ![]() Inscription : avril 2009 Messages : 2 633 ![]() |
Jedaï, quand j'utilise ton algorithme 3, avec GHC 6.10.2, sur WinXP Pro, j'obtiens une erreur :
Code :
Je suis dans une fenêtre cygwin pour lancer la commande. J'ai la même erreur depuis une fenêtre de commande windows (en appelant palindrome.exe <french). Je vais tenter sous Ubuntu (virtualisé sur mon XP)...
__________________
Plus j'apprends, et plus je mesure mon ignorance (philou67430) Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book) Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé Using strict and warnings is good for you. |
||
|
|
00
|
|
|
#17 | ||
|
Invité(e)
![]() Messages : n/a ![]() |
Voici une version Caml pas mal bidouillesque :
Code :
|
||
00
|
|
|
#18 |
|
Invité(e)
![]() Messages : n/a ![]() |
Sur un fichier avec de *nombreuses* et *longues* répétition, la 3ème version de Jedai me rattrape, mais la première devient 50 fois plus lente.
|
00
|
|
|
#19 |
|
Expert Confirmé Sénior
![]() ![]() |
Je précise que je compile avec optimisation (-O2 -funbox-strict-fields) rien de plus compliqué.
@Philou : Tu as bien compilé avec optimisation ? Pour ma part je n'ai pas eu de stack overflow et je ne pense pas que tu ais le problème avec les optimisations (ça pourrait venir du maximumBy, mais avec optimisation, il est normalement strict et donc non sujet à ce problème). @AlexPi : Il est relativement normal que la première version soit bien plus lente sur de gros fichiers vu qu'elle utilise String, qui n'est autre qu'un synonyme pour [Char]... Ta version comme ma 3ème utilise des tableaux de caractères pour les chaînes et est donc nettement plus efficace. Par ailleurs si la seule différence entre ma 3ème version et ton code est un facteur 2, je m'avoue plutôt content puisqu'a priori tu as employé une technique relativement plus bas niveau que la mienne. |
|
|
00
|
|
|
#20 |
|
Invité(e)
![]() Messages : n/a ![]() |
|
00
|
Copyright © 2000-2013 - www.developpez.com