[UNIT] ROMANUTILS, TOUT POUR CONVERTIR LES CHIFFRES ROMAINS VERS LES ENTIERS ET

florenth - 1 nov. 2005 à 10:46
dje_jay Messages postés 58 Date d'inscription mercredi 17 décembre 2003 Statut Membre Dernière intervention 16 février 2011 - 30 oct. 2007 à 16:54
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/34428-unit-romanutils-tout-pour-convertir-les-chiffres-romains-vers-les-entiers-et-inversement

dje_jay Messages postés 58 Date d'inscription mercredi 17 décembre 2003 Statut Membre Dernière intervention 16 février 2011 2
30 oct. 2007 à 16:54
Bon travail!

Je l'ai d'ailleur porté en java : http://www.javafr.com/codes/VERSION-JAVA-ROMANUTILS-TOUT-CONVERTIR-CHIFFRES-ROMAINS-VERS_44548.aspx

(J'ai tenu a respecter la source d'origine autant que possible)...
JulioDelphi Messages postés 2226 Date d'inscription dimanche 5 octobre 2003 Statut Membre Dernière intervention 18 novembre 2010 14
8 nov. 2006 à 15:06
HAPPY ANNIVERSAIRE LA SOURCE !!
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
7 nov. 2005 à 21:38
HAHA!

bonne nouvelle aprés gros reflechissage :

nouvelle données des benchs :

> 5 x 100 000 RomanToInt : moyenne 56ms
(total 282ms pour 500 000 requettes, plus rapide 47ms, plus longue 63ms)
gain ~78%
dont 22ms sont due a IsRomanNumber(STR), si on supprime la verification on descend donc a 34ms pour 100 000 requettes soit un gain de ~120%

> 5 x 100 000 IntToRoman : moyenne 84ms
(total 421ms pour 500 000 requettes, plus rapide 78ms, plus longue 94ms)
gain ~25%

ce qui donne des temps de moins d'une seconde pour traiter 1 millions de requettes.
ce qui est largement raisonnable je pense.

je mets donc a jours l'unitée en Version 1.2i (i pour improved) ^^
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
7 nov. 2005 à 20:07
en effet, j'ai tester egalement de mon coté :

Athlon 1800+ 1.53Ghz, DDR PC2100 2x133Mhz,
Process load 22, CPU Charge 20-40% (firefox)

RomanBench process mode : Real Time

> 5 x 100 000 RomanToInt : moyenne 441ms
(total 2203ms pour 500 000 requettes, plus rapide 437ms, plus longue 453ms)
CPU Burn Charge : 58%
VERDICT : reglo. on pourrait encore ameliorer ... Assembleur ? ^^

> 5 x 100 000 IntToRoman : moyenne 213ms
(total 1063ms pour 500 000 requettes, plus rapide 203ms, plus longue 219ms)
CPU Burn Charge : 27%
VERDICT : vraiment reglo.

> 5 x 200 000 IsRomanNumber (str) : moyenne 22ms
(total 109ms pour 1 000 000 requettes, plus rapide 15ms, plus longue 31ms)
CPU Burn Charge : 8%
VERDICT : carrement reglo.

> 5 x 200 000 IsRomanNumber (int) : moyenne 0ms
(total 0ms pour 1 000 000 requettes, plus rapide 0ms, plus longue 0ms)
CPU Burn Charge : 0%
VERDICT : no comment.

j'avais jamais penser a tester comme ça ... les resultat parlent d'eux memes.
Excuse moi. Tu l'auras vu dans le code, les performances données sont vraies pour 100000 (cent mille) executions et non 10000 (dix mille) comme je l'avais dit
Oula, on améliore les performances ...
La concurence est rude.
Je modifie ma fonction pour la rendre plus lisible (et plus rapide en même temps):

function RomainToInt(const Romain: string): Integer;
var
Pos: Integer;

function ValeurLettre(const L: Char): Integer;
begin
case UpCase(L) of
'I': Result := 1;
'V': Result := 5;
'X': Result := 10;
'L': Result := 50;
'C': Result := 100;
'D': Result := 500;
'M': Result := 1000;
else
raise Exception.CreateFmt('Caractère incorrect dans la chaîne (%s)', [L]);
end; // case.
end;

procedure Ajout;
var
Val1, Val2: Integer;
begin
{ Récupération des deux valeurs. }
Val1 := ValeurLettre(Romain[Pos]);
Val2 := ValeurLettre(Romain[Pos + 1]);

{ Comparaison: test de soustraction. }
if Val1 < Val2 then
begin
{ Si oui, on ajoute la différence entre Val1 et Val2. }
Inc(Result, Val2 - Val1);
Inc(Pos);
end
else
{ Sinon, on ajoute juste Val1. }
Inc(Result, Val1);
end;

begin
{ Test de chaîne vide. }
Result := 0;
Assert(Romain <> '', 'La chaîne ne doit pas être vide.');

{ 1er carac: N° 1. }
Pos := 1;

{ Tant que l'on arrive pas à la fin. }
while Pos < Length(Romain) do
begin
{ On ajoute. }
Ajout;
{ On passe au carac suivant. }
Inc(Pos);
end;
Inc(Result, ValeurLettre(Romain[Pos])); // Le dernier carac.
end;

Résultats du test (cycles de 10000 executions): entre 470 et 520 ms pour ta fonction, entre 32 et 50 pour la mienne. Les chiffres sont les miens, ne te moques pas de mon ordi si tu obtiens moins.

Pour faire les tests, j'ai fait cela: en mettant un bouton sur la fiche, et y colant le code de ma fonction, et en utilisant GetTickCount().

procedure TForm1.Button3Click(Sender: TObject);
var
Tc, Tc1, Tc2, i: Cardinal;
begin
{ Test de ta fonction. }
Tc := GetTickCount;
for i := 1 to 100000 do
RomanUtils.RomanToInt('MMCDLXXXVII');
Tc1 := GetTickCount - Tc;

{ Test de la mienne. }
Tc := GetTickCount;
for i := 1 to 100000 do
RomainToInt('MMCDLXXXVII');
Tc2 := GetTickCount - Tc;

{ Affichage des résultats. }
ShowMessageFmt('RomanToInt() %d ms - RomainToInt() %d ms', [Tc1, Tc2]);
end;

Evidemment, les tests sont a effectuer au minimum une dizaine de fois, car l'utilisation du processeur par les autres programmes peuvent influencer la rapidité.

@ ++ Florent

PS: tu as drolement amélioré la vitesse de ta fonction. Si je me souvient bien des chiffres, j'obtenais environ 1000 ms pour 10000 executions (soit deux fois plus).
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
7 nov. 2005 à 01:30
bon alors, au vus de tes resultats, j'ai modifier la source de toute mes fonctions.
voir l'historique et le nouveau zip.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
6 nov. 2005 à 23:39
avec quoi verifie tu la vitesse des fonctions ?

ça a l'air plutot enorme comme difference ...
Elle n'est pas plus longue que la tienne. Voire meme plus courte.
Mais dans ton unité, tu as séparé les différentes méthodes que tu utilises dans tes différentes fonctions.
Cela parait donc moins gros.

Mais le truc, c'est que ma version est 13 fois plus rapide que la tienne ;-) et une version optimisée de ma version est jsqu'a 25 fois plus rapide ...
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
5 nov. 2005 à 18:26
En effet, pas du tout la meme approche. par contre je sais pas si c'est les commentaires que tu as mis mais elle as l'air beaucoup plus "grosse"... ^^

sinon j'ai mis a jours pas mal de chose derniere, j'ai notement ajouter un fichier d'aide pour l'utilisation de l'unité RomanUtils.

je vais regarder a ta fonction de plus prés pour voir les difference entre les deux ...
Je vois que tu as mis à jour ta source qui maintenant permet de convertit dans les deux sens et sans un seul bug connu.

Juste pour voir qu'il y a plusieurs façons de coder la même chose, je poste ici ma propre version de RomToInt() elle aussi sans bug connu.

function RomainToInt(const Romain: string): Integer;
var
Pos: Integer;

function ValeurLettre(const L: Char): Integer;
begin
case UpCase(L) of
'I': Result := 1;
'V': Result := 5;
'X': Result := 10;
'L': Result := 50;
'C': Result := 100;
'D': Result := 500;
'M': Result := 1000;
else
raise Exception.CreateFmt('Caractère incorrect dans la chaîne (%s)', [L]);
end; // case.
end;

procedure Ajout;
var
Val1, Val2: Integer;
begin
{ Si on arrive pas à la fin de la chaîne. (il reste au moins un carac) }
if Pos < Length(Romain) then
begin
{ Récupération des deux valeurs. }
Val1 := ValeurLettre(Romain[Pos]);
Val2 := ValeurLettre(Romain[Pos + 1]);

{ Comparaison: test de soustraction. }
if Val1 < Val2 then
begin
{ Si oui, on ajoute la différence entre Val1 et Val2. }
Inc(Result, Val2 - Val1);
Inc(Pos);
end
else
{ Sinon, on ajoute juste Val1. }
Inc(Result, Val1);
end
else
begin
{ Si c'est le dernier caractère, juste l'ajouter. }
Val1 := ValeurLettre(Romain[Pos]);
Inc(Result, Val1);
end;
end;

begin
{ Test de chaîne vide. }
Result := 0;
Assert(Romain <> '', 'La chaîne ne doit pas être vide.');

{ 1er carac: N° 1. }
Pos := 1;

{ Tant que l'on arrive pas à la fin. }
while Pos <= Length(Romain) do
begin
{ On ajoute. }
Ajout;
{ On passe au carac suivant. }
Inc(Pos);
end;
end;
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
5 nov. 2005 à 01:21
@florenth :

pour romstr, oui c'est effectivement vrai ...

pour result, je crois que c'est une preference de ma part d'utiliser une locale plutotque result lui meme ayant deja eu des problemes avec l'utilisation directe de result dans d'autre appli.
mais en effet ça pourrait aller avec elle.

pour les caracteres non valide, c'est juste que j'ai pas eu le temps. ^^

pour VL, en effet, c'est la ou justement tu avais raisons avec faiblesse de l'algo qui n'en est pas un au final.
en fait on devrait tout bonnement supprimer la notation exotique de l'algo et appeler la fonction StrictRomanToInt();

mais en meme temps, il faut se poser la question, ou trouve t'on encore des chiffres romain ?

dans les numerotations des paragraphes de texte ... et c'est a peut prés tout.
donc generalement n'utilise que I V et X...
au dela, les gens passe sois au vrai chiffres 1,2,3 ou aux lettres A,B,C ...

c'est pourquoi je n'ai pas chercher a ameliorer plus que ça la fonction.
mais si tu te donne la peine de l'ameliorée, a la rigeur, je corrigerais en fonction de tes resultats (en te citant dans la source bien entendus).

ps : en parlant de ça, a quand les codes sources commun ou on pourrais citer plusieurs personne en tant qu'auteur ?
C'est vrai que l'algorythme de MHI n'est pas tout à fait au point et qu'il est plus compliqué.
La notation moderne: ça dépend comment tu l'entends. Si j'écris VL (non accepté en version originale), il faudait lire 45 alors que ta fonction affiche 55.

Mais bon, c'est un peu tiré par les oreilles.

Juste trois petites suggestions cependant:
- Puisque tu ne modifie pas romstr (le parametre de la fonction), tu peux le déclarer constant (avec "const"). Il parait que ça optimise le code, surtout pour les chaines et les tableaux.

- Au lieu d'utiliser res comme variable, utilise directement Result. Ce n'est pas comme en C++ où l'utilisaiton de "return" quitte la procédure.

- Pourquoi ne pas prévoir une levée d'exception ou quelque chose comme cela si un des caractères n'est pas valide ?
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
3 nov. 2005 à 13:53
@Florenth : tu mets le doigt dessus.

a l'epoque des romains, les nombres etaient bien entendus normaliser et allant du plus grand au plus petit.

la notation 999 s'ecrit bien CMXCIX et pas IM mais a notre epoque, certain transgresse les regles etablie et le note IM. pour etre plus court.
certain meme quadruple les lettres ce qui n'etait pas le cas a l'epoque.
donc comme il semble y avoir les vieux chiffres romain et les chiffres romain "moderne" j'ai prevus le coups dans ma fonction.

mais aprés de nombreux tests, avec des valeurs "reglementaire" et "moderne" ma fonction ne semble pas commetre d'erreur. a chaque fois le resultat est bon.

mais si tu decouvre une particularitée, fais le moi savoir.

@delphiprog : aaaah! merci, justement j'avais chercher sans succés si il y avait deja quelque chose d'equivalent ici.
mais voila justement le probleme que j'eradique dans ma fonction, cad, la notation moderne.
la fonction de notre ami MHI est justement mise en echec par cette notation.
de plus il semblerais que la fonction de MHI donne des resultat erroné, sur un exemple simple :
XL (40) elle nous donne 60 (LX), idem pour XLVIII (48) elle renvoie 68...
d'ailleur un petit coups d'oeuil a son algorythme et on remarque vite qu'il n'a pas pris en compte les soustractions. sans vouloir etre medisant vis a vis de son travail.
je pense qu'il a oublier de faire quelques recherche preliminaire sur les chiffres romain et leur regles de base.

son aglorythme est assé complexe (sans parler du fait qu'il est incomplet) comparer au miens.
mais je preferais dire qu'ils ne sont pas comparable car j'utilise une methode carrement differente , il est vrai que je n'ai pas chercher midi a quatorze heure en me lançant dans des calculs et conditions complexe, aprés une nuit blanche on pardonneras mes neuronnes qui etaient deja fatigué par GLScene et sa hierarchie chaotique (je suis en train de me faire un Pong en openGL ... faut bien debuter par quelque chose).
En parlant des notation exotiques des chiffres romains,
Est ce que IM est réellement accepté ? Normalament c'est CMXCIX comme tu l'as dit.
Parce que si c'est accepté, alors il y a plusieurs façons d'écrire le même nombre (ce qui ne serait pas logique) et l'algoritme aurait alors des faiblesses
Rejoignez-nous