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

Macros et VBA Excel Discussion :

Macro: sélection aléatoire dans liste sans redondance


Sujet :

Macros et VBA Excel

  1. #1
    Membre à l'essai
    Homme Profil pro
    Chimiste
    Inscrit en
    Décembre 2012
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chimiste

    Informations forums :
    Inscription : Décembre 2012
    Messages : 24
    Points : 21
    Points
    21
    Par défaut Macro: sélection aléatoire dans liste sans redondance
    Bonjour,

    Je suis en train de réaliser ma première macro qui me sert à m’entraîner au langage VBA excel.
    Dans cette macro, J'ai besoin, notamment, de choisir un nombre indéfini d'éléments dans une liste prédéfinie. Cette liste de noms est associé un par un à une valeur.
    Chaque fois qu'un élément est extrait (copie ailleurs) dans la liste, la valeur est totalisée avec les autres valeurs déjà prises. Quand la valeur totale atteint (et ne dépasse pas) un plafond défini, la macro arrête son travail.

    en image
    Noms - Valeurs
    A - 1
    B - 3
    C - 6
    D - 4
    E - 2
    F - 1
    etc

    Valeur plafond : exemple: 10

    Liste générée aléatoirement

    E
    B
    F
    D


    Et pour compliquer:
    - il ne faut pas prendre deux fois le meme nom.
    (La liste doit rester intact car utilisée plusieurs fois. Un tri est donc possible, mais supprimer des données, non)

    En tant que novice, je m'arrache un peu les cheveux.
    Mon idée initiale est de trier la liste par ordre de valeur, ensuite d'utiliser la fonction random multipliée par une variable qui décroit de manière à sortir les valeurs trop grandes quand on se rapproche du plafond. Pas facile.

    Et pour ne pas choisir le meme nom, à part utiliser une variable pour chaque valeur sortie, je m'y perd.

    Est-ce que quelqu'un aurait une idée, ne fut-ce qu'un début de piste pour m'orienter?

    Merci d'avance
    Olivier

  2. #2
    Membre émérite
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Novembre 2011
    Messages
    1 503
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Irlande

    Informations professionnelles :
    Activité : Chef de projet en SSII

    Informations forums :
    Inscription : Novembre 2011
    Messages : 1 503
    Points : 2 657
    Points
    2 657
    Par défaut
    Salut à toi !

    Voici le code que je te propose :
    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
    Sub Rdm_lim()
    Dim lim As Integer
    Dim test As Range
    Dim tot As Integer
    Dim cell_des As Range
    Dim flg As Boolean
     
    Dim upperbound As Integer
     
    i = 0
    tot = 0
    flg = False
    lim = InputBox("Insérer la valeur plafond", "Valeur plafond", 25)
     
    With Worksheets("test")
        Set test = .Range("A1")
        Set cell_des = .Range("D1")
     
        upperbound = .Columns(1).Find("*", , , , xlByColumns, xlPrevious).Row - 1
     
        Do
            rdm_test = Int((upperbound + 1) * Rnd)
     
            If i = 0 Then
                cell_des.Offset(i, 0) = test.Offset(rdm_test, 0)
     
                tot = tot + test.Offset(rdm_test, 1)
                i = i + 1
            Else
                For j = 0 To .Columns(4).Find("*", , , , xlByColumns, xlPrevious).Row - 1
                    If cell_des.Offset(j, 0) = test.Offset(rdm_test, 0) Then
                        flg = True
                    End If
                Next j
     
                If flg = False Then
                    cell_des.Offset(i, 0) = test.Offset(rdm_test, 0)
     
                    tot = tot + test.Offset(rdm_test, 1)
                    i = i + 1
                End If
     
                flg = False
     
            End If
     
            If i > upperbound Then
                MsgBox "Vous avez atteind la limite Max : il n'y a plus de possibilité d'unique valeur dans votre sélection."
                Exit Do
            End If
        Loop While tot < lim
     
        cell_des.Offset(i, 0) = tot
     
    End With
     
    End Sub
    Puisque tu as précisé que tu étais novice, je me permet de te préciser que tu dois placer toute macro dans un module.
    Voici les précisions et conditions d'utilisation :
    1) Ton onglet doit s'appeler "test".
    2) Tu dois placer tes Noms et Valeurs...
    A - 1
    B - 3
    C - 6
    D - 4
    E - 2
    F - 1
    ... dans les colonnes A et B.
    3) Lance ta macro.
    4) Insère la valeur plafond que tu souahites
    5) Tu verras apparaitre dans la colonne D une liste aléatoire sans doublon de tes valeurs qui se trouvent dans la colonne A.
    6) En bas de cette liste, je place la valeur totale (addition des valeurs correspondantes à la liste au dessus).
    7) Si jamais tu insères une valeur plafond trop importante qui dépasse la somme de toutes les valeurs correspondantes au nom de ta colonne A, un message d'erreur s'affichera spécifiant que la recherche ne peut plus trouver de valeurs unique.

    Je crois que c'est tout ! ^^
    Tiens moi au courant !
    La logique :
    • Plus ya de gruyère, moins ya de gruyère.
    • Plus tu pédales moins vite, moins tu avances plus vite.
    Plusoyer les réponses pertinentes et n'oublier pas de résolver en fin de post !

  3. #3
    Membre à l'essai
    Homme Profil pro
    Chimiste
    Inscrit en
    Décembre 2012
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chimiste

    Informations forums :
    Inscription : Décembre 2012
    Messages : 24
    Points : 21
    Points
    21
    Par défaut
    Bonjour Kimy_Ire

    Merci de ta réponse!
    Wow... Pour être honnête, ça fait un bon bout de temps que je plonge sur ton code, sans en comprendre la totalité. Je pense que j'ai encore de nombreuses lacunes en VBA qu'il faudra remédier. D'abord, je viens de m'apercevoir qu'on peut déclarer des parties d'objets (.Range par exemple) dans une variable... Quel changement pour moi (comme ça tu vois ou en sont mes lacunes )
    Du coup, si tu as un peu de temps, j'ai quelques questions:

    1) Je ne vois pas de déclaration pour "i", "j" et "rdm_test"? Est-ce normal?
    2) Il me semble comprendre la variable upperbound, mais pour être sur: elle renvoie la valeur du nombre de rangées moins 1 contenant quelque chose dans la colonne 1. Du coup, pourquoi le -1 dans upperbound et le +1 dans rdm_test?
    3) Si j'ai bien compris, la partie for J ... next J vérifie que la variable mémorisée (pas encore écrite) rdm_test fait correspondre une donnée non existante de la colonne 1 avec chacune des valeur de la colonne 4 une par une (J incrémenté 1 à 1)? Et donc quand flg n'est pas vérifié (faux) il copie la donnée. Sinon, il en cherche une autre en recommençant?

    Merci pour ton aide, ça me donne de nouvelles perspectives pour mon code.

    Hier, je ne suis pas arrivé par mes propres moyens à faire un code fonctionnel comme le tien. J'arrivais à sortir des valeurs, mais je n'ai rien trouvé de concluent pour la non répétition des noms.

    Par contre, dans mon code j'ai observé que
    - Dans un "If... Then" l'opérateur ">" est prioritaire sur une soustraction. Est-ce exacte? J'ai du mettre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     If Variable1 > (Variable2 + Variable3) Then
    Au lieu de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     If Variable1 > Variable2 + Variable3 Then
    Qui ne fonctionnait pas comme je le voulais.

    - j'ai utilisé la fonction Cells.Copy / Activesheet.Paste. Comment faire pour que ce soit juste la valeur qui soit copiée et non la case complète (qui ruine l'encadrement prédéfini de ma case des destination).

    Merci!

  4. #4
    Membre émérite
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Novembre 2011
    Messages
    1 503
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Irlande

    Informations professionnelles :
    Activité : Chef de projet en SSII

    Informations forums :
    Inscription : Novembre 2011
    Messages : 1 503
    Points : 2 657
    Points
    2 657
    Par défaut
    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
    'Nom de la Macro
    Sub Rdm_lim()
    'Déclaration des variable
    Dim lim As Integer
    Dim test As Range
    Dim tot As Integer
    Dim cell_des As Range
    Dim flg As Boolean
     
    Dim upperbound As Integer
     
    'Set les variables prédéfinies aux valeurs souhaitées
    i = 0
    tot = 0
    flg = False
     
    'Créer une "InputBox" dans laquelle on place la valeur plafond que l'on enregistre dans "lim"
    lim = InputBox("Insérer la valeur plafond", "Valeur plafond", 25)
     
    'On défini la feuille dans laquelle on travaille
    With Worksheets("test")
        'On set les "Ranges" sur les cases que l'on veut
        'test va balayer la colonne A
        Set test = .Range("A1")
        'cell_des est la cellule de destination
        Set cell_des = .Range("D1")
     
        'On cherche la dernière cellule non vide de la collone 1 (=> .Columns(1).Find("*", , , , xlByColumns, xlPrevious) )
        'On récupère sa ligne (=> .Row )
        'On enregistre celle-ci en soustrayant 1 dans upperbound
        upperbound = .Columns(1).Find("*", , , , xlByColumns, xlPrevious).Row - 1
     
        'On boucle tant que "tot < lim" (=> voir "Loop While")
        Do
            'On créer un variable rdm comprise entre 0 et upperbound-1
            rdm_test = Int((upperbound + 1) * Rnd)
     
            'i est notre offset pour la cellule de destination
            'Il y a donc deux parties => 1) i = 0 et l'on place la permière valeur dans la collone D
            '                         => 2) i est différent de 0 (=> <> ) et il faut vérifier que la cellule que l'on va déplacer n'est pas déjà présente dans la collone D
     
            'Première partie => i = 0
            If i = 0 Then
                'On place simplement la cellule test avec un offset de ligne "rdm_test" (je rappelle : compris entre 0 et le nombre de ligne -1)
                'Je répond donc à ta question "Pourquoi -1" car si tu fais un offset du nombre de ligne exacte à la dernière cellule non vide, tu tombes sur la case d'après la dernière ligne : soit "rien"
                cell_des.Offset(i, 0) = test.Offset(rdm_test, 0)
     
                'On ajoute au total "tot", tot + la cellule à droite de la cellule déplacée
                'On peut faire également "tot = test.Offset(rdm_test, 1)" directement
                tot = tot + test.Offset(rdm_test, 1)
                'On ajoute 1 à i puisque l'on a maintenant une valeur dans D
                'Pareil : on aurait pu écrire "i = 1"
                i = i + 1
     
            'Deuxième partie => i > 0
            Else
                'Cette boucle va comparer la nouvelle cellule que l'on veut déplacer avec celles qui sont déjà présentes dans D
                'On boucle donc sur toute la colonne D soit sur "D1" avec un offset de 0 au numéro de la dernière ligne "-1"
                For j = 0 To .Columns(4).Find("*", , , , xlByColumns, xlPrevious).Row - 1
                    'On compare la valeur de la case rdm à celles sur D
                    'Si on trouve une valeur identique...
                    If cell_des.Offset(j, 0) = test.Offset(rdm_test, 0) Then
                        '... on pace notre flag (= drapeau) à "True"
                        flg = True
                    End If
                Next j
     
                'Si le flag est à "False", cela signifie que la cellule rdm que l'on veut déplacer n'est pas dans la colonne D
                'Dans ce cas, on peut se permettre de la déplacer
                If flg = False Then
                    'Ce que l'on fait ici
                    cell_des.Offset(i, 0) = test.Offset(rdm_test, 0)
     
                    'Puis de la même manière on ajoute le total de la cellule à droite de la cellule déplacée
                    tot = tot + test.Offset(rdm_test, 1)
                    'Et on incrémente i
                    i = i + 1
                End If
                '=> Si le flag était "True" on est pas rentré dans la boucle, donc rien n'a été déplacé, rien n'a été ajouté, on recommence, tout simplement
     
                'On reset le flag à "False" dans le cas où il était passé à "True"
                flg = False
     
            End If
     
            'Ceci est un complément :
            'En effet, en effectuant des tests et en ayant placé une valeur "lim" beaucoup trop grande, la somme de toutes les valeurs à droite des cellules à déplacées est inférieur à "lim"
            'Et si c'est le cas, on rentre dans un boucle infini dans laquelle le programme cherche une valeur différente de celle qu'il a déjà déplacé alors qu'elle l'on déjà toutes été
            If i > upperbound Then
                'J'affiche donc un message qui stipule qu'on a déjà déplacé toute les valeurs
                MsgBox "Vous avez atteind la limite Max : il n'y a plus de possibilité d'unique valeur dans votre sélection."
                'Et je sors de la boucle
                Exit Do
            End If
        Loop While tot < lim
     
        'Je place le total "tot" en bas de la colonne D
        cell_des.Offset(i, 0) = tot
     
    End With
     
    End Sub
    J'espère que ceci te permettra déjà de comprendre d'avantage.
    Ensuite pour répondre à tes questions :
    0) "qu'on peut déclarer des parties d'objets" => je ne comprends pas ce que tu veux dire
    1) Le VBA est fantastique. Si une variable n'est pas déclarée initialement, le programme la déclare implicitement en tant que "Variant". Cela signife qu'on peut placer à peu près n'importe quoi dedant. Lors de son initialisation dans le programme, ce variant change.
    Si tu veux absolument déclarer toutes tes variables, tu peux utiliser l'option "Explicit" tout en haut de ton programme. Cela t'oblige à tout déclarer. Mais pour répondre à de nombreuses questions sur le Forum, je ne vous le place pas pour vous éviter de vous arracher les cheveux en cas de modification. Pour autant cette option est parfois importante et évite des erreurs de compilation (mais généralement pour des Macro beaucoup plus longues et surtout qui font appellent à plusieurs fonctions les unes dans les autres).
    2) Je pense avoir répondu au fait que je métais "-1" à upperbound. Comme je te l'ai dis, un offset décale d'une cellule. Si je me place sur A1 pour rester en A1, je met "A1.offset(0, 0)" (=> 0 ligne et 0 colonne). Si je veux aller en A2, je place "A1.offset(1, 0)". Tu auras donc compris si je ne met pas de "-1" et si j'applique "A1.offset(nb_max, 0)" je tomberai non pas sur la dernière ligne mais sur celle d'après. D'où le "-1" pour rester dans la bonne range.
    3) Oui, avec mes explications c'est peut-être plus clair. Dis moi si tu n'as toujours pas compris au terme de ta lecture.
    4) Je n'utilise JAMAIS "Copy/Paste". J'ai horreur de ça. Idem pour "Select". Je préfère savoir en permanence où je suis avec mes offsets. Question de goût personnel. Cependant, ces options sont parfois obligatoire pour faire ce que l'on veut. Mais je tente de les éviter au maximum, personnellement.

    Bref, je tape du code, donc je ne suis pas fort en explications. J'espère pour autant que tu auras tout compris et que cela t'aidera. Mais des personnes plus spécialisées pourront peut-être t'aider d'avantage que moi sur ce point ! ^^

    5) Résolu si tu as tout ce que tu souhaites !
    La logique :
    • Plus ya de gruyère, moins ya de gruyère.
    • Plus tu pédales moins vite, moins tu avances plus vite.
    Plusoyer les réponses pertinentes et n'oublier pas de résolver en fin de post !

  5. #5
    Membre à l'essai
    Homme Profil pro
    Chimiste
    Inscrit en
    Décembre 2012
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chimiste

    Informations forums :
    Inscription : Décembre 2012
    Messages : 24
    Points : 21
    Points
    21
    Par défaut
    Merci pour ta réponse rapide!

    0) Je m'exprime surement avec les mauvais termes. Ce que je voulais dire, c'est que je ne savais pas que des variables pouvaient contenir des bouts de code comme ".Range("A1")"; jusqu'ici, je n'utilisais que des opérations mathématiques ou des messages (string)... Ca change pas mal ma vision du VBA
    1) Ok! J'utilise Explicit (c'est comme ça que j'ai appris) et donc je m'inquiétais de ne pas les voir déclarées. Si c'est une bonne habitude, je vais donc la garder!
    2) Ok! Je n'avais donc pas compris que j'allais tomber sur la première ligne vide. Mauvais calcul. Je comprends mieux le -1. Merci
    3) Très clair, parfait!
    4) Ok

    Merci pour tes réponses. Elles sont vraiment claires et je suis heureux d'être passé ici, ça me sauve un temps dingue en compréhension de la macro.

    Un tout grand merci!

    5) Ok, je clique


    A bientot!

  6. #6
    Rédacteur
    Avatar de Philippe Tulliez
    Homme Profil pro
    Formateur, développeur et consultant Excel, Access, Word et VBA
    Inscrit en
    Janvier 2010
    Messages
    12 755
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Formateur, développeur et consultant Excel, Access, Word et VBA

    Informations forums :
    Inscription : Janvier 2010
    Messages : 12 755
    Points : 28 606
    Points
    28 606
    Billets dans le blog
    53
    Par défaut
    Bonjour Michaël,
    1) Le VBA est fantastique. Si une variable n'est pas déclarée initialement, le programme la déclare implicitement en tant que "Variant". Cela signife qu'on peut placer à peu près n'importe quoi dedant. Lors de son initialisation dans le programme, ce variant change.
    Si tu veux absolument déclarer toutes tes variables, tu peux utiliser l'option "Explicit" tout en haut de ton programme. Cela t'oblige à tout déclarer. Mais pour répondre à de nombreuses questions sur le Forum, je ne vous le place pas pour vous éviter de vous arracher les cheveux en cas de modification. Pour autant cette option est parfois importante et évite des erreurs de compilation (mais généralement pour des Macro beaucoup plus longues et surtout qui font appellent à plusieurs fonctions les unes dans les autres).
    Je ne partage pas du tout, ce que tu conseilles là.
    Pour moi c'est justement la faiblesse du Basic de ne pas rendre la déclaration des variables obligatoires.
    C'est le premier conseil que l'on donne au programmeur dans les formations VBA, c'est de placer l'Option Explicit en début de programme pour s'obliger à les déclarer.
    Si l'on ne veut pas passer des heures à déboguer un programme parce-que tout simplement une variable a été mal orthographiée, il est préférable de déclarer ses variables et de préférence les déclarer en début de procédure.
    De plus avoir une variable de type Variant prends plus d'espace en mémoire que si elle est bien typée.
    Philippe Tulliez
    Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément. (Nicolas Boileau)
    Lorsque vous avez la réponse à votre question, n'oubliez pas de cliquer sur et si celle-ci est pertinente pensez à voter
    Mes tutoriels : Utilisation de l'assistant « Insertion de fonction », Les filtres avancés ou élaborés dans Excel
    Mon dernier billet : Utilisation de la fonction Dir en VBA pour vérifier l'existence d'un fichier

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

Discussions similaires

  1. sélection aléatoire dans liste sans redondance
    Par nrf0415a dans le forum Macros et VBA Excel
    Réponses: 0
    Dernier message: 07/11/2014, 16h37
  2. [XL-2007] Macro de recherche dans liste de contact + introductionde quelques données
    Par Tomtomaso dans le forum Macros et VBA Excel
    Réponses: 11
    Dernier message: 25/06/2010, 21h34
  3. [E-03] Macro pour créer une liste sans les vides
    Par PYJ59 dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 18/12/2008, 10h19
  4. Sélection aléatoire dans une BD
    Par djileuk dans le forum VBA Access
    Réponses: 2
    Dernier message: 03/10/2008, 10h41
  5. Macro sélection aléatoire de lignes
    Par Pascalou2008 dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 12/03/2008, 23h54

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