2009-07-24 5 views
0

Je construis une liste de hachages qui représentent les chemins de racine à noeud dans un arbre. Mes fonctions fonctionnent mais elles sont incroyablement lentes sur de grandes structures d'arbres - y a-t-il un meilleur moyen? J'ai essayé de construire la liste dans une fonction mais j'ai des hachages uniques où je ne les veux pas.Liste de chemins de construction lente

public ArrayList<Integer> makePathList(AbstractTree<String> tree){ 
    StringBuilder buffer = new StringBuilder(); 
    ArrayList<Integer> pl = new ArrayList<Integer>(); 
    ArrayList<StringBuilder> paths = getPaths(tree, buffer); 
    for(StringBuilder sb : paths){ 
     pl.add(sb.toString().hashCode()); 
    } 

    return pl; 
} 

public ArrayList<StringBuilder> getPaths(AbstractTree<String> tree, StringBuilder parent){ 
     ArrayList<StringBuilder> list = new ArrayList<StringBuilder>(); 
     parent.append("/"); 
     parent.append(tree.getNodeName()); 
     list.add(new StringBuilder(parent)); 

     if (!tree.isLeaf()){  
      int i = 0; 
      Iterator<AbstractTree<String>> child = tree.getChildren().iterator(); 
      while (i < tree.getChildren().size()){ 
       list.addAll(getPaths(child.next(), new StringBuilder(parent))); 
       i++; 
      } 
     } 
     return list; 
} 

MISE À JOUR:

suggestion de Marcin pour faire le hachage lors de la traversée de l'arbre donne la mauvaise réponse, mais peut-être est la façon dont je l'ai fait?

public ArrayList<Integer> getPaths(AbstractTree<String> tree, StringBuilder parent){ 
    ArrayList<Integer> list = new ArrayList<Integer>(); 

    parent.append("/"); 
    parent.append(tree.getNodeName()); 
    list.add(new StringBuilder(parent).toString().hashCode()); 

    if (!tree.isLeaf()){  
     int i = 0; 
     Iterator<AbstractTree<String>> child = tree.getChildren().iterator(); 
     while (i < tree.getChildren().size()){ 

      list.addAll(getPaths(child.next(), new StringBuilder(parent))); 
      i++; 
     } 
    } 
    return list; 
} 

Répondre

1

Je pense que votre problème principal est la quantité de données en double que vous produisez: pour chaque feuille de l'arbre, vous allez faire une copie du chemin complet menant à cette feuille et calculer le hachage pour ce chemin. c'est-à-dire que si vous avez 50 000 feuilles sous un nœud de niveau supérieur, le nom de chemin de ce nœud sera copié 50 000 fois et son hachage calculé 50 000 fois.

Si vous pouviez organiser vos données afin que les préfixes de chemins partagés soient réutilisés en tant que références entre les feuilles et les calculs de hachage de ces préfixes sont mis en cache et réutilisés, vous pourriez réduire considérablement la quantité de travail à effectuer.

+0

Cela ressemble à une solution intéressante - avez-vous un exemple d'une telle méthode? – Robert

+0

Je n'ai pas le temps de fournir le code de travail, mais fondamentalement, au lieu de construire le chemin dans les instances de StringBuilder, représente un chemin comme une liste d'éléments de chemin, chacun avec un nom et un hash partiel vers cet élément. –

0

Où jvisualvm indique-t-il que le goulot d'étranglement de performance est?

+0

Je ne sais pas comment utiliser jvisualvm, mais j'ai chronométré les méthodes, en utilisant un arbre XML de 100 Mo. chemins ... faisant \t [Terminé] 3614ms créer des codes de hachage ... \t [Terminé] 962ms \t total Done [4576ms] – Robert

+0

Il ne nommerai pas le problème de base dans ce cas, mais vous devriez vraiment apprendre comment utilisez un profileur tel que visualvm. C'est le seul moyen professionnel d'attaquer les problèmes de performance. –

+0

Je recommanderai fortement d'apprendre à utiliser un profileur. Le fruit le plus bas est jvisualvm. –

0

Vous créez d'abord une liste de tous les chemins, puis une fois que vous les avez tous, vous calculez des hachages. La taille de la liste de tous ces chemins est O (n^3) (il y a O (n^2) chemins, chaque O (n) étant long) Pourquoi? Pourquoi ne pas simplement calculer les hachages lorsque vous traversez l'arbre? De cette façon, vous prendrez un n hors de votre temps de complexité.

Le code de solution propre (résultat se retrouve dans passé dans la liste des nombres entiers):

public void getPaths(AbstractTree<String> tree, StringBuilder parentPath, 
    List<Integer> list) 
    StringBuilder newPath = parentPath.clone(); 
    newPath.append("/"); 
    newPath.append(tree.getNodeName()); 
    list.add(newPath.toString().hashCode()); 
    if (!tree.isLeaf()){  
    Iterator<AbstractTree<String>> child = tree.getChildren().iterator(); 
    for (AbstractTree<String> child : tree.getChildren()){ 
     getPaths(child, newPath, list) 
    } 
    } 
} 

Ceci est encore O (n^2). C'est à cause du hachage de O (n^2) valeurs de chaînes (chaque nœud a une longueur de chemin proportionnelle à sa profondeur) et vous pouvez l'abaisser même à O (N) si vous aviez un hachage que pour un nœud donné prend seulement un hacher du chemin de ses parents et le modifie d'une certaine manière.

optimisations furhter comprennent: - arbre parallèle traversal - en utilisant le hachage plus intelligent (à savoir de hachage d'un enfant est une fonction de l'enfant et hachage du chemin parent, pas tout le chemin parent).

+0

essayé de calculer des hachages au cours de l'arbre traveral, mais il donne la mauvaise réponse - peut-être vous pouvez voir pourquoi? (Voir la question originale pour le code) – Robert

+0

J'ai amélioré la solution. Ça devrait être mieux maintenant. – Marcin

+0

Je suis un peu confus par cette solution. Premièrement, comment obtenez-vous le résultat? passer une liste en paramètre fait une copie de la liste et ne modifie pas la liste d'origine. Deuxièmement, la méthode clone n'est pas visible pour parentPath. – Robert

0

Je pense que la complexité est toujours la même. Peu importe si vous utilisez une création en ligne de hachages (O (n^2)) ou si vous le faites après récursivité (O (n^2 + n) = O (n^2)). La seule occasion de trouver un moyen rapide est de faire une partie du travail à un autre endroit. par exemple. vous pouvez hacher le chemin tout en insérant un nœud et ne rassembler tous les hashs qu'à un autre point.

Questions connexes