2009-04-15 7 views
3

Je suis en train d'écrire un algorithme de clustering agglomératif dans Java et j'ai des problèmes avec une opération de suppression. Il semble toujours échouer lorsque le nombre de grappes atteint la moitié du nombre initial. Dans l'exemple de code ci-dessous, clusters est .Supprimer d'un HashSet échouant après l'avoir parcouru

 while(clusters.size() > K){ 
      // determine smallest distance between clusters 
      Collection<Integer> minclust1 = null; 
      Collection<Integer> minclust2 = null; 
      double mindist = Double.POSITIVE_INFINITY; 

      for(Collection<Integer> cluster1 : clusters){ 
       for(Collection<Integer> cluster2 : clusters){ 
        if(cluster1 != cluster2 && getDistance(cluster1, cluster2) < mindist){ 
          minclust1 = cluster1; 
          minclust2 = cluster2; 
          mindist = getDistance(cluster1, cluster2); 
        } 
       } 
      } 

      // merge the two clusters 
      minclust1.addAll(minclust2); 
      clusters.remove(minclust2); 
     } 

Après quelques passages dans la boucle, clusters.remove(minclust2) finalement retourne faux, mais je ne comprends pas pourquoi.

J'ai testé ce code en créant d'abord 10 clusters, chacun avec un entier de 1 à 10. Les distances sont des nombres aléatoires entre 0 et 1. Voici la sortie après avoir ajouté quelques instructions println. Après le nombre de clusters, j'imprime les clusters réels, l'opération de fusion et le résultat de clusters.remove (minclust2).

Clustering: 10 clusters 
[[3], [1], [10], [5], [9], [7], [2], [4], [6], [8]] 
[5] <- [6] 
true 
Clustering: 9 clusters 
[[3], [1], [10], [5, 6], [9], [7], [2], [4], [8]] 
[7] <- [8] 
true 
Clustering: 8 clusters 
[[3], [1], [10], [5, 6], [9], [7, 8], [2], [4]] 
[10] <- [9] 
true 
Clustering: 7 clusters 
[[3], [1], [10, 9], [5, 6], [7, 8], [2], [4]] 
[5, 6] <- [4] 
true 
Clustering: 6 clusters 
[[3], [1], [10, 9], [5, 6, 4], [7, 8], [2]] 
[3] <- [2] 
true 
Clustering: 5 clusters 
[[3, 2], [1], [10, 9], [5, 6, 4], [7, 8]] 
[10, 9] <- [5, 6, 4] 
false 
Clustering: 5 clusters 
[[3, 2], [1], [10, 9, 5, 6, 4], [5, 6, 4], [7, 8]] 
[10, 9, 5, 6, 4] <- [5, 6, 4] 
false 
Clustering: 5 clusters 
[[3, 2], [1], [10, 9, 5, 6, 4, 5, 6, 4], [5, 6, 4], [7, 8]] 
[10, 9, 5, 6, 4, 5, 6, 4] <- [5, 6, 4] 
false 

L'le [10, 9, 5, 6, 4, 5, 6, 4, ...] réglé pousse simplement infiniment à partir de là.

Edit: pour clarifier, j'utilise un pour chaque cluster en grappes (un HashSet<HashSet<Integer>>)

+0

[10, 9, 5, 6, 4, 5, 6, 4, ...] n'est clairement pas un ensemble. Est-ce une liste? –

+0

Oui, bon point. Un HashSet ne doit pas être capable de contenir des objets en double. Quelque chose est bizarre ici. –

Répondre

5

Ah. Lorsque vous modifiez une valeur qui est déjà dans une Set (ou une clé Map), elle n'est pas nécessairement dans la bonne position et les codes de hachage seront mis en cache. Vous devez l'enlever, le modifier, puis le réinsérer.

+1

Oui, vous l'avez! La solution consiste à créer un nouveau cluster, à ajouter tous les éléments de minclust1 et minclust2, à supprimer minclust1 et minclust2 des clusters et à ajouter le nouveau cluster. Il semble que ce soit une mauvaise idée de modifier les objets dans un HashSet. – weiyin

+1

Excellent. Roches d'immutabilité. Techniquement, vous pouvez modifier un élément est un HashSet tant que vous ne bouleversez pas ses égaux et hashCode, mais ceux-ci devraient dépendre de tout ou aucune des données. Si vous aviez HashSet , vous pourriez modifier les composants sans crainte. –

0

Le problème évident est que il clusters.remove utilise probablement equals pour trouver l'élément à supprimer Malheureusement equals.. sur les collections en général compare si les éléments sont les mêmes, plutôt que si elle est la même collection (je crois C# fait un meilleur choix à cet égard).

une solution facile est de créer clusters comme Collections.newSetFromMap(new IdentityHashMap<Collection<Integer>, Boolean>()) (I pense).

+0

Bon point à propos d'égal, mais même si elle utilisait égal, pourquoi la suppression échouerait? – weiyin

1

Dans le test illustré, le remove échoue la première fois que vous tentez de supprimer une collection contenant plusieurs entiers. Est-ce toujours le cas?

Quel type de béton est utilisé pour la collection?

+0

Oui, vous êtes sur la bonne voie. Il échoue toujours la première fois que j'essaie de supprimer une collection avec plus d'un entier. J'utilise un HashSet pour chaque cluster dans les clusters (HashSet >). – weiyin

+0

C'est bizarre. Si vous utilisez HashSet, pourquoi obtenez-vous plusieurs valeurs dans l'ensemble. –

+0

Comme je l'ai dit plus haut, un HashSet ne devrait pas être capable de contenir des objets en double. Je pense qu'il y a un problème plus profond ici. –

Questions connexes