Trier des données d'un arraylist [Résolu]

Messages postés
197
Date d'inscription
dimanche 5 août 2012
Statut
Membre
Dernière intervention
15 juillet 2019
- - Dernière réponse : Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
- 15 juil. 2019 à 19:58
Bonjour,
je suis à la recherche d'une méthode ou d'une idée d'algorithme permettant de trier une série de nombre dans un arraylist, en Visual Basic.

Pour développer, il s'agit en fait d'une série de coordonnées cartésiennes notées sous la formes "x;y" qui correspondent à des cases. Je voudrais, dans toutes ces coordonnées, isoler et partager en plusieurs groupes toutes celles correspondant à des cases qui se touchent.

Pour mieux comprendre, voici un schéma représentant le problème à l'aide d'un exemple :



Merci d'avance pour votre aide :)
matousso
Afficher la suite 

5 réponses

Meilleure réponse
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288
1
Merci
Alors, je t'avoue que je me suis amusé un peu.

Je me suis dit, que j'allais te montrer plusieurs facettes d'un objet, tel que pourrait l'être une de tes cases.
J'ai appelé la classe MaCase


Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text.RegularExpressions


Class MaCase
#Region "Propriétés de classe"
    ''' <summary>
    ''' Valeur en X mini de la grille
    ''' </summary>
    Public Shared Property Xmin As Integer = 1

    ''' <summary>
    ''' Valeur en X maxi de la grille
    ''' </summary>
    Public Shared Property Xmax As Integer = 10

    ''' <summary>
    ''' Valeur en Y mini de la grille
    ''' </summary>
    Public Shared Property Ymin As Integer = 1

    ''' <summary>
    ''' Valeur en Y maxi de la grille
    ''' </summary>
    Public Shared Property Ymax As Integer = 7
#End Region

#Region "Méthode de classe"
    ''' <summary>
    ''' Crée une instance de Case à partir d'un texte, si le texte n'est pas compatible, retourne null
    ''' </summary>
    ''' <param name="Coordonnees"></param>
    ''' <returns></returns>
    Public Shared Function NouvelleCase(Coordonnees As String) As MaCase
        Dim m As Match = Regex.Match(Coordonnees, "^(?<x>\d+);(?<y>\d+)$")

        If m.Success Then
            'si le texte correspond au modèle
            'on extrait x et y
            Dim x As Integer = Convert.ToInt32(m.Groups("x").Value)
            Dim y As Integer = Convert.ToInt32(m.Groups("y").Value)
            'On vérifie qu'il sont compatibles de la grille
            If x >= Xmin AndAlso x <= Xmax AndAlso y >= Ymin AndAlso y <= Ymax Then
                Return New MaCase(x, y) 'on retourne une instance de Case, grâce au constructeur privé avec paramètres
            End If
        End If

        'sinon, on retourne null
        Return Nothing
    End Function

#End Region

#Region "Constructeurs"
    ''' <summary>
    ''' Constructeur nécessaire pour la création d'une instance voisine
    ''' </summary>
    Private Sub New()
    End Sub

    ''' <summary>
    ''' Constructeur nécessaire pour la création d'une instance depuis un string au format "x;y"
    ''' </summary>
    ''' <param name="LeX"></param>
    ''' <param name="LeY"></param>
    Private Sub New(LeX As Integer, LeY As Integer)
        X = LeX
        Y = LeY

        'crée la liste des voisins non null de la case en cours,
        'cette fois on se sert du constructeur sans paramètres pour ne pas calculer indéfiniment des voisins
        VoisinsPotentiels = ({New MaCase With {.X = LeX, .Y = LeY + 1}, New MaCase With {.X = LeX, .Y = LeY - 1}, New MaCase With {.X = LeX + 1, .Y = LeY}, New MaCase With {.X = LeX - 1, .Y = LeY}}).Where(Function(c) c IsNot Nothing).ToList()
    End Sub
#End Region

#Region "Propriétés d'instance"
    ''' <summary>
    ''' Abscisse de la case
    ''' </summary>
    Public Property X As Integer

    ''' <summary>
    ''' Ordonnées de la case
    ''' </summary>
    Public Property Y As Integer

    ''' <summary>
    ''' Liste des voisins possibles de la case
    ''' </summary>
    Public Property VoisinsPotentiels As List(Of MaCase)
#End Region

    Public Overrides Function ToString() As String
        Return String.Format("{0};{1}", X, Y)
    End Function

#Region "Opérateurs"
    ''' <summary>
    ''' Opérateur d'égalité
    ''' </summary>
    ''' <param name="Un"></param>
    ''' <param name="Autre"></param>
    ''' <returns></returns>
    Public Shared Operator =(ByVal Un As MaCase, ByVal Autre As MaCase) As Boolean
        'si Un ou Autre vaut null
        If Object.ReferenceEquals(Un, Nothing) Then
            Return Object.ReferenceEquals(Autre, Nothing)
        ElseIf Object.ReferenceEquals(Autre, Nothing) Then
            Return False
        End If

        'Sinon on compare les coordonnées
        Return Un.X = Autre.X AndAlso Un.Y = Autre.Y
    End Operator


    ''' <summary>
    ''' Opérateur d'inégalité
    ''' </summary>
    ''' <param name="Un"></param>
    ''' <param name="Autre"></param>
    ''' <returns></returns>
    Public Shared Operator <>(ByVal Un As MaCase, ByVal Autre As MaCase) As Boolean
        Return Not (Un = Autre)
    End Operator

#End Region
End Class


Elle dispose de son ordonnée et de son abscisse en Integer (c'est mieux pour les calculs qu'une string).
Elle sait s'initialiser à partir d'un texte, en vérifiant au préalable que le format est correct (via une Regex) et que les coordonnées sont comprises dans la grille (les valeurs min et max étant contenues dans des propriétés de classe).
Elle se génère toute seule la liste de ses voisins potentiels.
Elle dispose d'un opérateur d'égalité (et d'inégalité l'un ne va pas sans l'autre) pour chercher dans les cases noires si l'un d'elle est égale à un voisin potentiel.
Enfin elle dispose d'un ToString personnalisé, qui permet, entre autre, de voir de quelle case il s'agit dans la capture d'écran.

Et ensuite, j'ai écris une méthode qui à partir d'un ArrayList contenant les cases noires de ton schéma, génère une liste de MaCase, puis la trie.
le tri est fait par 2 boucles imbriquées.
La première crée un groupe à partir d'une case noire disponible.
La seconde, remplit le groupe, elle cherche et ajoute les voisin de la case, puis les voisins des voisins et ainsi de suite tant que c'est possible.

A la fin j'ai mis un point d'arrêt pour la capture d'écran.
Pour le fun, avant la création de la liste de cases noires, je modifie la taille de la grille.
    Private Sub TestMatousso()
        'initialisation d'un arrayList pour l'exemple
        Dim caseNoires As New ArrayList()
        caseNoires.Add("6;2")
        caseNoires.Add("4;3")
        caseNoires.Add("5;3")
        caseNoires.Add("6;3")
        caseNoires.Add("5;4")
        caseNoires.Add("4;5")
        caseNoires.Add("5;5")
        caseNoires.Add("8;7")
        caseNoires.Add("9;7")


        'si finalement on décidait que la grille a une colonne de plus
        MaCase.Xmax = 11

        Dim lesCasesNoires As New List(Of MaCase)()
        'Création de la liste de travail
        For Each s As String In caseNoires 'on ne peut pas faire de Linq sur un ArrayList, alors on fait un foreach
            Dim c As MaCase = MaCase.NouvelleCase(s)
            If c IsNot Nothing Then
                lesCasesNoires.Add(c)
            End If
        Next s

        Dim lesGroupes As New List(Of List(Of MaCase))()
        Do While lesCasesNoires.Count > 0 'la recherche ci dessous va enlever les cases au fur et à mesure et s'arreter quand il n'y en aura plus
            'Création d'un nouveau groupe qui commence avec la première case, que l'on enlève de la liste d'origine
            Dim laCase As MaCase = lesCasesNoires(0)
            lesCasesNoires.Remove(lesCasesNoires(0))
            Dim g As New List(Of MaCase)() From {laCase}
            lesGroupes.Add(g)

            Do
                Dim voisinsPotentiels As List(Of MaCase) = g.SelectMany(Function(c) c.VoisinsPotentiels).ToList() 'collection de tous les voisins possibles des cases déjà dans le groupe
                Dim voisins As List(Of MaCase) = (
                    From p In voisinsPotentiels, c In lesCasesNoires
                    Where p = c
                    Select c).ToList() 'collection des cases se trouvant dans les voisins potentiels et les cases noires

                If voisins.Count = 0 Then
                    Exit Do 'il n'y a plus de voisins on sort de la création de ce groupe
                End If

                'On ajoute les voisins au groupe
                g.AddRange(voisins)
                'on les enlève de la liste de cases noires
                For Each c As MaCase In voisins
                    lesCasesNoires.Remove(c)
                Next c

                If lesCasesNoires.Count = 0 Then
                    Exit Do 's'il n'y a plus de case à classer, pas la peine de faire une boucle de plus
                End If

                'on recommence la même recherche à partir du groupe nouvellement constitué
            Loop

        Loop
    End Sub

Dire « Merci » 1

Heureux de vous avoir aidé ! Vous nous appréciez ? Donnez votre avis sur nous ! Evaluez CodeS-SourceS

Codes Sources 130 internautes nous ont dit merci ce mois-ci

Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288 -
Ha zut, le truc c'est que c'est pas facile à vérifier sur 20000 case.
Je regarde quand j'ai un moment
matousso
Messages postés
197
Date d'inscription
dimanche 5 août 2012
Statut
Membre
Dernière intervention
15 juillet 2019
-
oui je comprends c'est un grand casse tête. Merci vraiment de ton aide !
Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288 -
En modifiant la mise à jour des x & y min & max, comme suit
							If c.X - 2 < xmin Then
								xmin = c.X - 2
							ElseIf c.X + 2 > xmax Then
								xmax = c.X + 2
							End If

							If c.Y - 2 < ymin Then
								ymin = c.Y - 2
							ElseIf c.Y + 2 > ymax Then
								ymax = c.Y + 2
							End If

Je trouve 15791 groupes au lieu de 16354, sur le fichier de test.

Je n'ai pas vérifié à grande échelle mais j'ai une idée pour le faire, mais je ne la mettrais pas en oeuvre ce soir.

Si de ton coté tu peux regarder ce que ça donne sur ton image.
matousso
Messages postés
197
Date d'inscription
dimanche 5 août 2012
Statut
Membre
Dernière intervention
15 juillet 2019
-
Avec ces modifications, ça fonctionne enfin parfaitement ! Le temps est légèrement plus long mais reste tout à fait convenable (< 1 min en mode release), donc je n'ai pas besoin de plus d'optimisations !
Merci beaucoup pour toute cette aide et cette implication et bonne continuation !
matousso
Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288 -
Tant mieux et de rien
Commenter la réponse de Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288
0
Merci
Bonjour

Ton schéma ne correspond pas à
Je voudrais, dans toutes ces coordonnées, isoler et partager en plusieurs groupes toutes celles correspondant à des cases qui se touchent.

Si tu choisis une case de départ au milieu, alors les 8 cases (si tu considères que se toucher par les angles compte) ou les 4 autours devraient être sélectionnées.
Hors le groupe 1 invalide ces 2 options.
Peux tu expliquer mieux?

Quel est le contenu d’une case?
  • un texte x;y
  • un objet avec une propriété x et une propriété y'a
  • autre


Enfin pourquoi t’embêter avec un tableau ou un arraylist ces 2 collections sont plus contraignantes et moins performantes que n’importe quelle autre?
Le seul cas où un tableau est indispensable c’est pour communiquer avec une dll ou un programme ayant un tableau en paramètre
Quand j'étais petit, la mer Morte n'était que malade.
George Burns
matousso
Messages postés
197
Date d'inscription
dimanche 5 août 2012
Statut
Membre
Dernière intervention
15 juillet 2019
-
Bonjour,
en fait il s'agit de cases noires disposées de manière aléatoire que je récupère via un arraylist avec leurs coordonnées. Leur disposition n'est pas contrôlée et une case ne contient rien, elle est juste caractérisée par sa coordonnée.
Et c'est seulement de ces cases noires imposées que je parle dans ce problème, les cases blanches ne m'intéressent pas.
Je me retrouve donc avec un arraylist contenant une série de coordonnées correspondant aux cases noires, que je souhaite trier et individualiser en plusieurs groupes de cases noires.
Et j'utilise un arraylist tout simplement parce que je ne connais pas à l'avance le nombre de cases noires au départ. Ces cases peuvent être disposées de n'importe quelle manière n'importe où, et en nombre indéterminé.
Commenter la réponse de Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288
0
Merci
une case ne contient rien, elle est juste caractérisée par sa coordonnée.

Je me retrouve donc avec un arraylist contenant une série de coordonnées correspondant aux cases noires,

Je me suis mal exprimé. Comment sont stockées ces coordonnées?
  • un texte x;y
  • un objet avec une propriété x et une propriété y'a
  • autre


Et j'utilise un arraylist tout simplement parce que je ne connais pas à l'avance le nombre de cases noires au départ
Ok, mais une list(of ), est plus simple et souple à utiliser, et c’est aussi une liste chaînée

matousso
Messages postés
197
Date d'inscription
dimanche 5 août 2012
Statut
Membre
Dernière intervention
15 juillet 2019
-
les coordonnées sont justes stockées dans l'arraylist sous forme de string "x;y"
Sinon, pour le list(of), si ça permet de simplifier et d'optimiser, je peux effectivement l'utiliser. Je n'y avais pas forcément pensé.
Mais ma problématique de trier toutes ces coordonnées comme expliqué est toujours là.
Commenter la réponse de Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288
0
Merci
En string, c’est pas le plus simple, mais bon.
Est-ce 2 cellules en diagonales sont dans le même groupe?
matousso
Messages postés
197
Date d'inscription
dimanche 5 août 2012
Statut
Membre
Dernière intervention
15 juillet 2019
-
Non, il faut juste que 2 cases noires se touchent verticalement ou horizontalement pour qu'elles soient dans le même groupe.
Commenter la réponse de Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288
0
Merci
Ok, je tache de regarder ça ce soir
Whismeril
Messages postés
13504
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
15 juillet 2019
288 -
Question bonus, tu es en
  • mode console
  • winform
  • wpf
  • asp
  • autre

ET quelle Framework ?
Que je te propose un truc avec des outils auxquels tu as accès.
matousso
Messages postés
197
Date d'inscription
dimanche 5 août 2012
Statut
Membre
Dernière intervention
15 juillet 2019
-
Merci beaucoup !
Pour les informations complémentaires, je suis en Winform avec le .NET Framework 4.6.1
Commenter la réponse de Whismeril