suche Codeoptimierung für Baumstruktur

Hallo in die Runde.
Ich suche eine Optimierungsmöglichkeit für meinen Code.
Gegeben ist eine sehr einfache Baumstruktur (Nummerierung nach Kekule). Hier gibt es einen Startdatensatz (1) sowie zwei Abzweigungen (2 und 3). In meinem Fall ist das eine Startperson sowie dessen beiden leiblichen Elternteile. Um diese nun nach Kekule zu nummerieren, rufe ich die Routine einfach rekursiv auf (gekürzt):

[code]Public Sub neuerProband(nr as integer)
if Person(nr).VerweisVater>0 then
Person(Person(nr).VerweisVater).Kekulenummer = Person(nr).Kekulenummer + Person(nr).Kekulenummer
neuerProband(Person(nr).VerweisVater)
end
if Person(nr).VerweisMutter>0 then
Person(Person(nr).VerweisMutter).Kekulenummer =P erson(nr).Kekulenummer + Person(nr).Kekulenummer +
neuerProband(Person(nr).VerweisMutter,Plausi)
end

End Sub
[/code]

Der Code enthält zudem noch eine Begrenzung auf maximal 63 Generationen und eine Prüfung auf Zirkelschlüsse (z. B. Urgroßvater = Urenkel).
Jetzt habe ich einen Anwender mit so vielen Generationen und Datensätzen, dass die Generierung bei ihm schon über 4 Minuten dauert.

Hat jemand eine Idee, wie man den Code optimieren kann?

Derzeit überlege ich, lediglich 6 bis 10 Generationen in der Art durchlaufen zu lassen und dann an einen Thread abzugeben. Damit teile ich dann die höheren Generationen in mehrere Prozesse auf und der Anwender kann schon weiter arbeiten. Allerdings hat er dann zu Anfang noch nicht alle Datensätze nummeriert und ich weiß nicht, ob der Zeitgewinn lohnt.

Wie ist eure Einschätzung?

Gruß, Stefan Mettenbrink.

Hier noch die komplette Routine:

[code]Public Sub neuerProband(nr as integer,Plausi as Boolean)
//der Parameter Plausi gibt an, ob bei Verweisschleifen eine Hinweismeldung ausgegeben wird
//oder für die Plausibilitätsprüfung die Hinweise gesammelt werden sollen
//Plaus=true -> Hinweise sammeln
dim i,j,k,l As integer
dim Maximum,mv,mm,null as UInt64
dim s as String
//kekulezuindexN.value auf die benötigte Anzahl Einträge erweitern

Maximum=9223372036854775809
null=0
If nr>0 then
if Person(nr).Kekulenummer<=pow(2,64) then

  'TestArray.Append(str(nr))
  'merke=UBound(TestArray)
  
  //Nur zu Testzwecken
  'if UBound(TestArray)>45 then
  'text=join(TestArray,EndOfLine)
  'exit
  'else
  'back=back+1
  'end
  
  if Person(nr).Kekulenummer>null then
    kekulezuindexN.value(Person(nr).Kekulenummer)=nr//neuen Verweis eintragen
  end
  
  if Person(nr).VerweisVater>0 then
    for i=1 to ubound(Generationsline)
      if Person(nr).VerweisVater=Generationsline(i) then
        for j=i to UBound(Generationsline)-1
          k=Generationsline(j)
          l=Generationsline(j+1)
          if Person(k).VerweisVater=l then
            s=s+str(k)+" Vater -> "+str(l)+EndOfLine
          else
            s=s+str(k)+" Mutter -> "+str(l)+EndOfLine
          end
        next
        k=Generationsline(UBound(Generationsline))
        l=Person(nr).VerweisVater
        s=s+str(k)+" Vater -> "+str(l)
        
        if Plausi then
          PlausiHinweis.Append HinweisPersonIndexnummerEigenerVorfahre_1+" "+str(Person(nr).VerweisVater)+HinweisPersonIndexnummerEigenerVorfahre_2
        else
          hinweis(HinweisPersonIndexnummerEigenerVorfahre_1+" "+str(Person(nr).VerweisVater)+HinweisPersonIndexnummerEigenerVorfahre_2)+EndOfLine+s
        end
        
        if Keyboard.AsyncShiftKey then
          Person(nr).VerweisVater=0
        end
        
        exit
      end
    next
    
    if i>ubound(Generationsline) then
      if Person(nr).Kekulenummer<Maximum then
        if Person(Person(nr).VerweisVater).Kekulenummer>null then
          mv=Person(Person(nr).VerweisVater).Kekulenummer
        end
        Person(Person(nr).VerweisVater).Kekulenummer=Person(nr).Kekulenummer+Person(nr).Kekulenummer
        Generationsline.append Person(nr).VerweisVater
        neuerProband(Person(nr).VerweisVater,Plausi)
        if mv>null and mv<Person(Person(nr).VerweisVater).Kekulenummer then
          Person(Person(nr).VerweisVater).Kekulenummer=mv
        end
        Generationsline.Remove ubound(Generationsline)
      end
    end
  end
  if Person(nr).VerweisMutter>0 then
    for i=1 to ubound(Generationsline)
      if Person(nr).VerweisMutter=Generationsline(i) then
        for j=i to UBound(Generationsline)-1
          k=Generationsline(j)
          l=Generationsline(j+1)
          if Person(k).VerweisVater=l then
            s=s+str(k)+" Vater -> "+str(l)+EndOfLine
          else
            s=s+str(k)+" Mutter -> "+str(l)+EndOfLine
          end
        next
        k=Generationsline(UBound(Generationsline))
        l=Person(nr).VerweisMutter
        s=s+str(k)+" Mutter -> "+str(l)
        
        if Plausi then
          PlausiHinweis.Append HinweisPersonIndexnummerEigenerVorfahre_1+" "+str(Person(nr).VerweisMutter)+HinweisPersonIndexnummerEigenerVorfahre_2
        else
          hinweis(HinweisPersonIndexnummerEigenerVorfahre_1+" "+str(Person(nr).VerweisMutter)+HinweisPersonIndexnummerEigenerVorfahre_2)+EndOfLine+s
        end
        
        if Keyboard.AsyncShiftKey then
          Person(nr).VerweisMutter=0
        end
        
        exit
      end
    next
    
    if i>ubound(Generationsline) then
      if Person(nr).Kekulenummer<Maximum then
        if Person(Person(nr).VerweisMutter).Kekulenummer>null then
          mm=Person(Person(nr).VerweisMutter).Kekulenummer
        end
        Person(Person(nr).VerweisMutter).Kekulenummer=Person(nr).Kekulenummer+Person(nr).Kekulenummer+1
        Generationsline.append Person(nr).VerweisMutter
        neuerProband(Person(nr).VerweisMutter,Plausi)
        if mm>null and mm<Person(Person(nr).VerweisMutter).Kekulenummer then
          Person(Person(nr).VerweisMutter).Kekulenummer=mm
        end
        Generationsline.Remove ubound(Generationsline)
      end
    end
  end
else
  hinweis(HinweisZuvieleGenerationen)
end

end

//Nur zu Testzwecken
'if UBound(TestArray)>merke then
'do
'TestArray.Remove UBound(TestArray)
'if UBound(TestArray)=merke then
'TestArray.Remove merke
'exit
'end
'Loop
'elseif UBound(TestArray)<merke then
'merke=merke
'else
'TestArray.Remove merke
'end
app.MouseCursor=mauszeiger(8)

Exception err
Fehlerbehandlung(err.message,“neuerProband”,err,false)
End Sub
[/code]

Ohne Beispiel ist das ein wenig zu kompliziert zum draufschauen. Womit verbringt denn das Programm die 4 Minuten? Hast Du Erfahrung mit dem Profiler?

Ich habe keine Erfahrung mit dem Profiler.

so wie ich es verstehe, htte ich den Profiler bentigt, als ich nch nicht wusste, welche Routine so viel Zeit verbrt :slight_smile:

Die Routine selbst wird halt sehr hufig aufgerufen. Ich sehe auch nicht, das zu umgehen.
Ich werde mal etwas mit dem Inhalt der Routine experimentieren.

Dann wird es Zeit. Den Profiler zu verwenden ist immer spannend. Der in XCode ist sogar noch besser. Ich kann mir eigentlich nur vorstellen, da der Code 100.000 mal durchlaufen wird. Ansonsten kann das keine 4 Minuten dauern.

Wie ist denn die Klasse Person aufgebaut? Hast Du versucht, etwas wie “Person(nr).VerweisMutter” zu cachen? Dauert das vielleicht lange?

Ich denke, dass ist das Problem. Bei 20 Generationen kommt man schon auf 1048576 (2^20) mögliche Personen. Sicher hat man nicht alle Personen erfasst, aber es gibt noch Ahnenschwund (dieselbe Person taucht in der Liste an mehreren Stellen auf).

Ich habe mal zählen lassen: 392176 Aufrufe (38 Generationen)

Ich komme auf ein paar Verdachtsflle, die eventuell ein bisschen was rtteln knnen, aber denke, dass bei diesen Zeiten eine grndliche Refaktorierung oder ein schlauer Cache-Mechanismus wie von Beatrix vorgeschlagen eher zu greren Gewinnen fhren.

Was so dabei war:
Speichere das Limit vor einer Schleife oder immer wiederkehrende Berechnungen als eigene Variable. Immer wieder ubound(Array) oder Person(nr) nachzuschlagen summiert sich.
Stringmanipulationen wie in s=s+str(k)+" Vater -> "+str(l)+EndOfLine sind sehr langsam. Besteht die Mglichkeit, ein Array aufzubauen und dieses am Ende mittels Join zusammenzustricken? Das knnte bei greren Schleifen eine ganze Menge ausmachen.
Wie lang wird denn die Generationsliste? Arrays gehen bei sehr groen Inhaltsmengen sprbar in die Knie. Dictionary oder JSON knnten schneller sein.

[quote]Was so dabei war:
– Speichere das Limit vor einer Schleife oder immer wiederkehrende Berechnungen als eigene Variable. Immer wieder ubound(Array) oder Person(nr) nachzuschlagen summiert sich.[/quote]

Das Problem ist, das Array ist global und wird mit jedem rekursiven Aufruf verändert. Ich kann somit kein oberes Ende angeben.

Habe ich gemerkt. Ich habe versucht, anstelle durch eine For-Next-Schleife Zirkelschlüsse zu erkennen und hoffte, dass durch InStrg und zusammenbasteln des Strings zu kürzen (weniger Schleifendurchläufe). War ein Schuss in den Ofen.

Ich merke mir den Weg der Rekursion, indem ich die angesprungenen Datensätze in ein Array schreibe:

Generationsline.append Person(nr).VerweisVater

Beim Verlassen der rekursiven Routine nehme ich den letzten Eintrag weg:

Generationsline.Remove ubound(Generationsline)

Also wie bei einem Stack.
In diesem Array dar ein Datensatz nicht zweimal vorkommen:

for i=1 to ubound(Generationsline1) if Person(nr).VerweisVater=Generationsline1(i) then //Fehler exit end next

Wäre es hier schnneller, die Nummern per Join in einen String zu wandeln (mit Trennzeichen) und eine Abfrage mit InStr zu machen (statt der vielen Schleifendurchläufe)?

Bis maximal 62 Generationen. Da ich aber im Vorfeld nicht weiß, wieviele Generationen vorhanden sind (das versuche ich hier zu ermitteln), kann ich das nicht sagen.
Ich nutze ja schon ein Dictionary.

Es gibt für das Programm zwei Zugriffswege auf die Daten. Entweder kenne ich die Person (den Datensatz) und möchte die Kekulenummer wissen:
Person(nr).Kekulenummer

Oder ich habe eine K-Nr und möchte den Datensatz wissen. Dann greife ich auf ein Dictioary zu. Genau dieses muss ich jedoch erst mal erzeugen.

Vor allem kann es sein, dass der Anwender zwischenduch mal jemand anderen als Stammhalter (Startperson) definieren möchte und die Berechnung dann erneut durchgeführt werden muss.