Bonjur,
Je viens vers vous pour un petit problème d’ordre algorithmique que je cherche à implémenter en Python.
Pour une suite de tests, je chercher à générer une structure arborescente de N éléments, et que ces éléments soient répartis sur D niveaux (quand cela est possible). Par « répartis » j’entends que l’arbre soit équilibré. Pour un N et un D donnés, tous les parents devront avoir le même nombre d’enfants à un près (hors nœuds du dernier niveau qui n’auront pas d’enfant, et à l’exception du « dernier » nœud créé). Pas de nœud avec 4 enfants si plusieurs autres dans l’arbre n’en ont que 2.
Un élément est simplement identifié par un identifiant/index et un parent, ou plus simplement par un chemin.
L’idée est par exemple d’avoir, pour N=10
et D=4
la structure suivante :
0
/ \
1 8
/ \ \
2 5 9
/ \ / \
3 4 6 7
pour N=20
et D=4
:
0 _
/ \
___ 1 8
/ | \ / \
2 5 J 9 C
/|\ /|\ /|\ /|\
3 4 F 6 7 G A B H D E I
L’ordre de création des nœuds dans l’arbre importe peu au final. Ici j’ai juste utilisé un algorithme qui récupère comme parent le nœud avec le moins d’enfants, qui est le plus bas dans l’arbre et le plus à gauche.
Le seul point important est d’avoir la structure suivante pour N=4
et D=4
:
0
|
1
|
2
|
3
C’est-à-dire de toujours avoir le nombre de niveaux de profondeur demandé. Dans la mesure du possible bien sûr (N≥D).
Actuellement j’utilise l’algorithme suivant, implémenté en Python.
class Node:
def __init__(self, n, code, parent=None):
self.code = code
self.parent = parent
self.nb_children = 0
if parent is None:
self.depth = 0
self.left = 0
else:
self.depth = parent.depth + 1
self.left = parent.left * n + parent.nb_children
parent.nb_children += 1
@property
def path(self):
if self.parent is None:
return (self.code,)
else:
return self.parent.path + (self.code,)
def generate_tree(nb_nodes, depth):
root = Node(nb_nodes, 0)
yield root.path
parents = {root}
def key(node):
return node.nb_children, -node.depth, node.left
for i in range(1, nb_nodes):
parent = min(parents, key=key)
node = Node(nb_nodes, i, parent)
yield node.path
if node.depth < depth - 1:
parents.add(node)
Ça fonctionne bien, pas de soucis avec ça, mais ce qui me gêne c’est l’empreinte mémoire et la recherche du parent. J’aurais bien aimé pouvoir calculer l’index du parent juste à partir de l’index du nœud courant, avec une formule type heapq, sans avoir besoin de stocker les parents et de les parcourir pour trouver le bon.
J’ai pensé à une formule qui me donnerait le nombre maximum d’enfants par nœud en fonction de N et D.
Je sais avec D=4
que ça va être 1 enfant pour N jusque 4, puis 2 enfants jusque 15, 3 enfants jusque 40, etc.
Et que plus généralement pour N et D on cherche à avoir x enfants par nœud avec x tel que i=0∑D−1xi<N≤i=0∑D−1xi
Il semble que i=0∑D−1xi=⌈x−1xD⌉−1
Mais je ne sais pas si je peux en faire grand chose.
Enfin voilà, j’aurais voulu savoir si vous aviez des pistes pour résoudre mon problème de façon optimale.
Merci.