Trouver un élément dans un tableau, facile pour qui s'y connait !

Trouver un élément dans un tableau, facile pour qui s'y connait ! - VB/VBA/VBS - Programmation

Marsh Posté le 23-09-2007 à 16:44:11    

Salut à tous !  
 
Voilà, j'ai un tableau TA(1 To Max), qui contient une  
liste de mots (sans doublons), et je souhaite obtenir
l'indice (le numéro de ligne) d'un mot M donné.
 
Je bosse en VBA pour Excel, et j'ai le code suivant :
 

Code :
  1. Trouve = 0
  2. For i = 1 To Max
  3.     If TA(i) = M Then Trouve = i: Exit For
  4. Next i


 
En sortie de boucle, je récupère une variable Trouve
qui vaut 0 si le mot n'est pas dans la table, et son  
numéro de ligne s'il y est.
 
Je trouve ça assez peu optimisé (et je suis gentil),  
et du coup, ça me ralenti pas mal mon programme
(j'ai Max qui vaut 50.000 actuellement, et ce nombre
 va croitre jusqu'à 200.000 environ... Et cette boucle
 va tourner plus de 30 millions de fois !!!).
 
Mais je ne vois pas d'autre méthode !
 
Est-il possible de faire ça plus rapidement ?
(sans boucle, se serait top)
 
Merci d'avance ! :D


Message édité par Profil supprimé le 23-09-2007 à 18:34:54
Reply

Marsh Posté le 23-09-2007 à 16:44:11   

Reply

Marsh Posté le 23-09-2007 à 20:29:59    

tu as ces valeurs là sous excel ? c'est pas mieux une BDD Access ? :/

Reply

Marsh Posté le 23-09-2007 à 20:45:41    

Si tu tiens à ton Tableau et si ce dernier est trié alors http://www.asp-php.net/tutorial/divers/dichotomie.php
sinon une bête feuille XL 256 x 65536 cellules et un find


Message édité par kiki29 le 23-09-2007 à 20:58:09
Reply

Marsh Posté le 23-09-2007 à 21:02:03    

Hum... Non, Access n'est pas mieux, parce qu'il y a plus de fonctions Excel pour les traitements parallèles que j'ai à faire.
Et non, mon tableau n'est pas trié, donc le dichotomie, ça marche pas !
Et un find implique que "j'écrives" mon tableau sur une feuille Excel, mais cette simple opération prend plus de temps que la boucle que je cherche à optimiser ! :)

Reply

Marsh Posté le 23-09-2007 à 21:25:24    

ton tableau tu le sors d'où au fait ? ca va piocher des infos où ?
sinon en passant par un fichier txt, ca devrait aller plus vite non ?

Reply

Marsh Posté le 24-09-2007 à 11:43:39    

Bon, j'explique plus en détail... En fait, le but est de soumettre
un texte (stocké dans un fichier txt) au programme. Le but sera
alors de lister les différents mots présents dans le texte et, pour
chaque mot, de connaitre le nombre d'occurences.

 

Mon programme fonctionne comme suit :
1. Lecture du fichier : on stocke le contenu du
    fichier texte dans une variable (ou on fait ça
    en plusieurs fois selon la taille du fichier).
2. On met tout en minuscule et on vire tout ce qui
    n'est pas une lettre ou un espace.
3. On vire les éventuels double-espaces.
4. On "sépare" les mots, donc pour ça, je cherche
    le premier espace (avec un InStr) et ce qui est
    à gauche, c'est mon mot, ce qui est à droite, c'est
    ce qu'il reste à traiter. Et je boucle là-dessus.
    Et donc, ce mot en question que je récupère, il y a
    deux possibilités.
    En fait, j'ai deux tableaux : TA et TB. TB(n) sera le
    nombre d'occurences du mot TA(n).
    La boucle fameuse boucle dont je parle cherche le petit
    n pour lequel TA(n) = mot qu'on cherche.
    Si n <> 0 alors TB(n) = TB(n) + 1
    Par contre, si n = 0, alors on incrémente la variable MAX
    (nombre total de mots), et on créé TA(MAX) et TB(MAX)
    passe à 1.

 

Voilà !

 

Au final je n'ai plus qu'à écrire sur ma feuille Excel les
tableaux TA et TB.

 

Dans ce programme, ce qui met du temps, c'est la
boucle de découpage des mots. Le reste, c'est quasi
instantané.
Si vous voulez, voici la boucle en question :

 
Code :
  1. Do
  2.     P = InStr(T, " " )
  3.     If P <> 0 Then
  4.         M = Left(T, P - 1)
  5.         T = Right(T, Len(T) - P)
  6.         Trouve = 0
  7.         For i = 1 To TN
  8.             If TA(i) = M Then Trouve = i: Exit For
  9.         Next i
  10.         If Trouve = 0 Then
  11.             TN = TN + 1
  12.             TA(TN) = M
  13.             TB(TN) = 1
  14.         Else
  15.             TB(Trouve) = TB(Trouve) + 1
  16.         End If
  17.     End If
  18. Loop
 

Note : j'ai zappé le if P = 0 qui gère le dernier mot et
         qui fait sortir de la boucle. C'est pas important.

 

Merci de vous pencher là-dessus ! ;)


Message édité par Profil supprimé le 24-09-2007 à 11:48:41
Reply

Marsh Posté le 24-09-2007 à 11:48:12    

oki,
bon alors moi je propose un truc peut être plus simple : commande batch  :pt1cable:  
je ne maitrise pas assez le code pour te filer une ligne, mais à mon avis tu dois pouvoir faire un find du mot que tu cherches, et un compteur peux te refiler le nombre d'occurences. Le tout s'écrit alors dans un fichier fichier txt simple avec nom : nb_d'occurences.
 
Ca te donne une idée ?

Reply

Marsh Posté le 24-09-2007 à 13:10:38    

Bah ça m'emballe pas trop, parce que si je bosse
sur un fichier texte, ok, je pourrai utiliser un Find
ou quelque chose dans ce gout-là, cependant,  
les temps d'écritures sont plus lents en TXT qu'en
RAM, forcément. Donc au final, c'est même pas  
dit que je sois gagnant.

Reply

Marsh Posté le 24-09-2007 à 13:26:19    

Pour un tableau VB non trié, seule la recherche exhaustive peut marcher.
Il n'y a pas d'optimisation.
 
Toute solution passe par l'utilisation d'un tableau trié, d'un objet autre qu'un tableau (Collection, classe dédiée, expressions régulières via "Microsoft VBScript Regular Expressions 5.5" ) ou d'une autre solution (batch, composant externe, ...).

Reply

Marsh Posté le 24-09-2007 à 13:45:17    

Hum... Bon bah tant pis, ça restera lent...

Reply

Marsh Posté le 24-09-2007 à 13:45:17   

Reply

Marsh Posté le 24-09-2007 à 13:46:50    

euh, tu peux juste nous indiquer le temps de traitement qui est "lent" stp ?

Reply

Marsh Posté le 24-09-2007 à 17:21:24    

Sans problème. Prenons par exemple "Le petit chose", d'Alphonse Daudet.
 
1. Lecture du fichier et stockage dans une variable texte.
    29 secondes
 
2. Formatage du texte (suppression des caractères autres que les lettres)
    5 secondes
 
3. Suppression des double-espaces
    12 secondes
 
4. Séparation des mots et calcul des occurences (code plus haut)
    114 secondes
 
5. Affichage des données
    4 secondes
 
Et conclusion (pour ceux que ça intéresse) :
88.008 mots, dont 8.590 différents.

Reply

Marsh Posté le 24-09-2007 à 17:25:36    

et ca doit être réduit à combien tout ca ?

Reply

Marsh Posté le 24-09-2007 à 17:35:32    

J'ai pas de contraite particulière. :)
C'est juste que je trouve ça long, et que
j'aimerai abaisser ce temps le plus possible,  
sans que ça m'oblige à faire des trucs trop
compliqués ! :D

Reply

Marsh Posté le 25-09-2007 à 01:01:07    

Qqs remarques
1. Lecture du fichier : on stocke le contenu du
    fichier texte dans une variable (ou on fait ça  
    en plusieurs fois selon la taille du fichier).
 
    Normalement tu peux lire séquentiellement un fichier texte
    et le traiter ligne par ligne sans le "tronçonner"
    et faire du LCase , Replace et autre dans la foulée
 
C'est vrai je n'ai pas de dispo "le petit chose" en texte pour faire une comparaison


Message édité par kiki29 le 25-09-2007 à 05:21:21
Reply

Marsh Posté le 25-09-2007 à 09:17:59    

Oui, bien sûr, je sais qu'on peut faire ça
ligne par ligne, mais bon, entre faire tourner
une fois le programme avec 2000 lignes ou  
2000 fois avec une ligne, je ne sais pas si  
ça fait une différence. Surtout que ça me  
fait réitérer certaines opérations d'affichage
et ça, c'est très lent...

Reply

Marsh Posté le 25-09-2007 à 10:04:30    

J'ai déjà fait des tris en VB/VBA, et j'avais de bien meilleur temps que toi.
Je m'étais aidé de ce site


---------------
J'suis timide - Prêt à mourir, mais pas à vivre - Je suis vraiement très fatigué ... - more than meets the eye
Reply

Marsh Posté le 25-09-2007 à 10:05:37    

tiens d'ailleurs, si tu virais les affichages intermédiaires (si inutiles hein), ou que tu freezais l'écran ca accélérerait les étapes non ?

Reply

Marsh Posté le 25-09-2007 à 11:11:20    

Bonjour,
Une suggestion qui vaut peut être pas grand chose ... (je connais rien au problème d'optimisation)
Pour le découpage de mots pourquoi ne pas utiliser Split et pour le comptage du nombre d'occurences de chaque mot le même Split avec Ubound.
 
Ca pourrait faire
 
TabResA = Split(MonTexte, " " )
    Do While UBound(TabResA) > 0
        TabResA = Split(MonTexte, " " )
        nbocc = UBound(Split(MonTexte, TabResA(0)))
         
        MonTexte = Replace(MonTexte, TabResA(0) & " ", "" )
        msg = msg & vbCrLf & TabResA(0) & "=>" & nbocc
    Loop
    MsgBox msg

Reply

Marsh Posté le 25-09-2007 à 11:44:28    

To thejulienlepage : Ta réponse est tout simplement stupide, la linéarité de ce genre de probleme est un leurre
Si en plus tu n'as pas utilisé Application.ScreenUpdating = False....Application.ScreenUpdating = True, ainsi que Application.EnableEvents


Message édité par kiki29 le 25-09-2007 à 11:48:19
Reply

Marsh Posté le 25-09-2007 à 12:06:32    

Merci bien les gars !  
Le Split() va sûrement bien m'aider, et pareil
pour le Application.ScreenUpdating.
Je connaissais pas du tout ! :)
 
M'enfin kiki29, de là à dire que ma réponse est "stupide",
c'est pas la peine ! Je connais pas, c'est vrai. C'est
pas être stupide ! ;)
C'est comme si je te disais "quoi ?! Tu parles pas  
le bulgare ? Mais t'es stupide, c'est pourtant simple !" :D
 
Donc traite moi d'ignorant tant que tu veux, et honni
j'accepterai. En revanche stupide, évite ! ;)
 
Bon, je vais faire les changements appropriés et je vous
tiens au courant des résultats !

Reply

Marsh Posté le 25-09-2007 à 12:57:36    

Bon, bah j'y pige que pouic, comme on dit.
La fonction Split() refuse mystérieusement
de fonctionner et me donne le message  
d'erreur suivant :
"Erreur de compilation :
 Nombre d'arguments incorrect ou affectation de propriété incorrecte"
 
J'ai marqué juste ça :
TAt = Split(T, " " )
TAt() et T étant de type String.
D'ailleurs, même si je marque TAt = Split("Texte bidon", " " ),  
ça me fait la même chose.
 
Mais il y a un autre truc étrange : quand je tape  
"Split(", la p'tite infobulle qui donne d'habitude les
arguments à mettre n'apparait pas, alors que le mot
clé "Split" est reconnu puisque quand je ne met pas
la majuscule sur le "S", il me la met.
 
Bref, faut-il faire quelque chose de spécial pour que
le Split fonctionne sous Excel ?

Reply

Marsh Posté le 25-09-2007 à 13:12:22    

Ne pas être sous Excel97

Reply

Marsh Posté le 25-09-2007 à 13:14:00    

:) Oui, ça ça va. Je suis sous Excel 2007.

Reply

Marsh Posté le 25-09-2007 à 13:23:13    

Es-tu sûr que c'est bien la syntaxe du Split qui pose problème ?
Si tu as une autre erreur de syntaxe, cela justifierait l'erreur et l'absence d'info-bulle, même pour le Split.

 

edit: après test, le split fonctionne parfaitement sous Excel 2007. Le problème est ailleurs.


Message édité par tegu le 25-09-2007 à 13:30:42
Reply

Marsh Posté le 25-09-2007 à 13:36:26    

Non, je n'ai pas d'autre erreur.
Quand je met le Split en commentaire, la
macro s'exécute.  
En fait, quand je lance
le programme (qui est constitué de deux
macros), la première tourne, puis au moment
de lancer la seconde, il va directement sur la
ligne du Split et me met le message d'erreur.
Il refuse tout simplement d'entrer dans la macro.
Je ne sais pas si je suis très clair là... :D
 
En résumé, quand je lance le programme, il  
ne commence même pas à exécuter la macro
et place le curseur sur la ligne du Split en me
mettant le message d'erreur.

Reply

Marsh Posté le 25-09-2007 à 13:45:53    

1/ Si tu fais Debogage / Compiler est-ce que l'erreur est la même ?
2/ Fais voir le code concerné, y compris les déclarations des variables, sinon on ne pourra pas t'aider.

 

edit:
Tu peux aussi faire le test suivant : tu crées une nouvelle feuille Excel, tu actives l'éditeur VBA et, dans la fenêtre de debogage tu tapes
« ? split("bnefuih reuivh ireugb"," " )(1) » ou un truc du genre et tu fais ENTREE pour voir.


Message édité par tegu le 25-09-2007 à 13:49:47
Reply

Marsh Posté le 25-09-2007 à 14:03:15    

Ton TAt doit etre de type tableau
Tu peux donc mettre
Dim TAt as variant
Ca devrait fonctionner
 
en plus comme dirait Ki.. et quelque  :hello:  tu fais F1 (Aide en ligne)


Message édité par Paul Hood le 25-09-2007 à 14:06:03
Reply

Marsh Posté le 25-09-2007 à 14:14:23    

Bah l'aide elle m'apprend rien de plus.
Et en ce qui concerne le type de TAt () (que j'ai renommé
Test d'ailleurs, mais on s'en fout), que je mette String,  
Variant ou même rien du tout, ça ne change rien ! :(
 
Voici mon code, si ça peut aider :
 

Code :
  1. Option Explicit
  2. Dim TA(1 To 150000, 1 To 1) As String   ' Tableau contenant les différents mots du texte
  3. Dim TB(1 To 150000, 1 To 1) As Double   ' Tableau contenant le nombre d'occurences de chaque mot
  4.                                         ' Pour un mot M donné, si TA(i) = M alors TB(i) est le
  5.                                         ' nombre d'occurences du mot M.
  6. Dim TN                      As Double   ' Nombre total de mots différents
  7. Dim T                       As String   ' Variable stockant l'intégralité du fichier
  8. Dim S                       As String   ' Une ligne du fichier texte
  9. Dim L                       As String   ' Caractères possibles dans un mot
  10. Dim i                       As Double   ' Variable multiusage (pour les boucles)
  11. Dim Max                     As Integer  ' Nombre maximum d'espaces à la suite
  12. Dim O                       As Double   ' Taille du fichier texte ouvert en octets
  13. Dim K                       As Integer  ' Taille du fichier en ko
  14. Dim M                       As Integer  ' Taille du fichier en Mo
  15. Dim Test()                   As String
  16. '---------------- Fonction principale, appelée par le bouton d'exécution du programme
  17. Sub Split()
  18.    
  19.     '-- On ferme les fichiers ouverts, au cas où.
  20.     Close
  21.    
  22.     '-- Initialisation...
  23.     Range("O8" ) = "Initialisation...": Range("P8" ) = ""
  24.         Range("E4:J150000" ).ClearContents
  25.         T = ""                                                 ' Contenu du texte
  26.         L = "azertyuiopqsdfghjklmwxcvbnäëüïöÿâêûîôéèçùàñãõœæ " ' Caractères possibles dans un mot
  27.         TN = 0                                                 ' Nombre de mots différents
  28.    
  29.     '-- On stocke le fichier dans la variable T
  30.     Range("O8" ) = "Lecture du fichier...": Range("P8" ) = "En cours..."
  31.         Open Range("O5" ) For Input As #1                       ' Range("O5" ) contient le chemin d'accès du fichier
  32.             O = 0                                              ' Taille ouverte jusqu'à présent
  33.             Do                                                 ' Boucle de lecture
  34.                 Input #1, S                                    ' On lit une ligne...
  35.                 O = O + Len(S)                                 ' La taille du fichier est mise à jour
  36.                 T = T & LCase(S) & " "                         ' On incrémente T
  37.                 If O / 1024 = Int(O / 1024) Then               ' Tous les 1024 octets (pour pas que ça rame trop)
  38.                     M = Int(O / 1048576)                       ' On affiche la taille ouverte
  39.                     K = Int(O / 1024)
  40.                     If K > 1024 Then                           ' Si ça fait plus d'1 Mo, on met la taille en Mo...
  41.                         Range("P8" ) = CStr(M) & "," & CStr(Int(Int((K - M * 1024)) / 1024 * 100)) & " Mo"
  42.                     Else
  43.                         Range("P8" ) = CStr(K) & " ko"
  44.                     End If
  45.                 End If
  46.                 If Range("M12" ) = "OUI" Then                   ' Permet de traiter les gros fichiers par blocs de
  47.                     If Len(T) > 500000 Then                    ' 500 ko via une option sur la feuille Excel.
  48.                         Traite T                               ' Si les 500 ko sont atteints, on traite T.
  49.                         T = ""                                 ' ...et on réinitialise sa valeur.
  50.                     End If
  51.                 End If
  52.             Loop Until EOF(1)                                  ' On boucle.
  53.             Close #1                                           ' On peut maintenant fermet le fichier.
  54.             Traite T                                           ' On traite T, qui vaudra tout le texte pour un petit
  55.                                                                ' fichier, ou la fin d'un gros.
  56. Fin:
  57.     Range("O8" ) = "Opérations terminées !": Range("P8" ) = ""   ' Le programme est fini !
  58. End Sub
  59. '--------------- Procédure de traitement de la variable T.
  60. Sub Traite(T)
  61.    
  62.     '----------- On sépare tous les mots par un espace.
  63.     Range("O8" ) = "Formatage du texte..."
  64.         Range("P8" ) = "0 %"                     ' Permet de connaitre l'état d'avancement
  65.         For i = 1 To Len(T)
  66.             ' On actualise tous les 10.000 caractères, pour pas que ça rame trop.
  67.             If i / 10000 = Int(i / 10000) Then Range("P8" ) = CStr(Int(i / Len(T) * 1000)) & " ‰"
  68.             ' Si le caractère i n'est pas un caractère valide (appartenant à la chaîne L), on
  69.             ' le remplace par un espace.
  70.             If InStr(L, Mid(T, i, 1)) = 0 Then Mid(T, i, 1) = " "
  71.         Next i
  72.    
  73.     '------------ On vire les doubles espaces
  74.     Range("O8" ) = "Suppression des espaces..."
  75.         Range("P8" ) = "En cours..."
  76.         ' On calcule le nombre maximal d'espaces à la suite
  77.         Max = 1
  78.         Do
  79.             If InStr(T, String(Max + 1, " " )) <> 0 Then Max = Max + 1 Else Exit Do
  80.         Loop
  81.         ' ...et on les remplace par un espace simple.
  82.         For i = Max To 2 Step -1
  83.             T = Replace(T, String(i, " " ), " " )
  84.         Next i
  85.    
  86.     '------------ Le plus gros du boulot : la séparation des mots !
  87.     Range("O8" ) = "Séparation des mots..."
  88.         Range("P8" ) = ""
  89.         ' La fonction qui ne marche pas !
  90.         Test = Split(T, " " )
  91. End Sub


 
J'ai pas mis toute la partie que j'avais faite
pour le traitement (que j'espère pouvoir modifier
avec le fameux Split), mais ça n'a pas d'incidence
sur le programme, car j'ai tout mis en commentaires.
À part ça, le code est le même chez moi et bug  
toujours sur le Split.

Reply

Marsh Posté le 25-09-2007 à 14:26:18    

En ligne 21 tu redéfinis la fonction Split...
Quand tu l'appel en ligne 97...c'est la tienne qu'il tente d'executer...
Je pense qu'en changeant le nom de ta fonction Split ca devrait aller mieux...

Reply

Marsh Posté le 25-09-2007 à 14:28:08    

:D Argh !!! Qu'est-ce que je suis naze !
J'avais même pas fait gaffe au fait que  
ma fonction s'appelait Split !
 
Merci Paul Hood ! (de la famille de Robin ?)

Reply

Marsh Posté le 25-09-2007 à 14:29:43    

pas tout à fait !!
Plus près de Jack Ryan  :whistle:  que de Robin ...


Message édité par Paul Hood le 25-09-2007 à 14:40:05
Reply

Marsh Posté le 25-09-2007 à 15:28:16    

Bon, bah mauvaise nouvelle : en utilisant la fonction suivante :

Code :
  1. Test = Split(T, " " )
  2. For i = 0 To UBound(Test)
  3.     Test = Split(T, " " )
  4.     TA(i + 1, 1) = Test(0)
  5.     T = " " & T
  6.     TB(i + 1, 1) = UBound(Split(T, " " & Test(0) & " " ))
  7.     T = Replace(T, " " & Test(0) & " ", " " )
  8.     T = Right(T, Len(T) - 1)
  9. Next i


 
...j'obtiens un temps de calcul nettement plus long
qu'avec mon ancienne méthode... :(

Reply

Marsh Posté le 25-09-2007 à 16:03:46    

Tu n'as pas besoins de la ligne 8 : Right etc...
   Test = Split(T, " " )
    i=0
    Do While UBound(Test) > 0
        Test = Split(T, " " )
        TA(i,0)=Test(0)
        TA(i,1)=UBound(Split(T, Test(0)))
        T = Replace(T, Test(0) & " ", "" )
        i=i+1
    Loop
en fait tu remplaces le mot sélectionné et le blanc suvant par rien, ce qui diminue la string au fur et a mesure.
Dans ton tableau TA à 2 dimensions, la premiere c'est pour stocker le mot et la deuxième c'est pour le nombre d'occurences.


Message édité par Paul Hood le 25-09-2007 à 16:04:49
Reply

Marsh Posté le 25-09-2007 à 16:24:00    

Bah oui, j'avais fait comme ça au début, mais...
0: "alphonse daudet le petit chose table des matières"
1: "daudet le petit chose table des matières"
2: "petit chose tab des matières"
...

 

Eh oui, c'est un peu embêtant ! :D

 

Mais j'ai trouvé quelque chose qui fonctionne
pas mal et me baisse le temps d'exécution du
découpage des mots de 114 à 30 secondes en
faisant un mix entre mon ancienne méthode et
la tienne :

 
Code :
  1. Do
  2.     P = InStr(T, " " )
  3.     If P = 0 Then
  4.         If T <> "" Then
  5.             TN = TN + 1
  6.             TA(TN, 1) = T
  7.             TB(TN, 1) = 1
  8.         End If
  9.         Exit Do
  10.     Else
  11.         Mot = Left(T, P - 1)
  12.         T = Right(T, Len(T) - P)
  13.         TN = TN + 1
  14.         TA(TN, 1) = Mot
  15.         TB(TN, 1) = UBound(Split(T, " " & Mot & " " )) + 1
  16.         T = Replace(T, " " & Mot & " ", " " )
  17.     End If
  18. Loop


Message édité par Profil supprimé le 25-09-2007 à 16:27:28
Reply

Marsh Posté le 25-09-2007 à 16:35:48    

Si l'optimisation dans le traitement de chaînes VB6 vous intéresse, voici de quoi lire.
http://msdn2.microsoft.com/en-us/l [...] e.10).aspx
http://msdn.microsoft.com/library/ [...] topic1.asp

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed