Code effizienter gestalten

  • Hi

    ich hab mal wieder ein problemlein wo ihr mir womöglich helfen könnt.
    es geht darum, dass folgende funktion bei mehr als einem megabyte ziemlich ewig warten lässt.

    [autoit]

    Func ArrayCountS($array)
    Dim $ary[2][2]
    $ary[0][0] = 1
    $ary[1][0] = $array[1]
    $ary[1][1] = 1
    $best = $array[1]
    $cbest = 1
    $dublic = 0
    For $c = 2 To $array[0]
    For $i = 1 To $ary[0][0]
    If $ary[$i][0] = $array[$c] Then
    $ary[$i][1] += 1
    If $ary[$i][1] > $cbest Then
    $dublic = 0
    $best = $array[$c]
    $cbest = $ary[$i][1]
    ElseIf $ary[$i][1] = $cbest Then
    $dublic = 1
    EndIf
    ContinueLoop 2
    EndIf
    Next
    $ary[0][0] += 1
    ReDim $ary[$ary[0][0]+1][2]
    $ary[$ary[0][0]][0] = $array[$c]
    $ary[$ary[0][0]][0] = 1
    Next
    Return SetError($dublic, $cbest, $best)
    EndFunc

    [/autoit]

    Wie der name sagt, soll sie die teile in dem array zählen. rückgabewert ist dann das element im array, welches am oftesten vorkommt. als @extended sagt es, wie oft es vorkam und @error sagt ob ein anderes element genau gleich oft kam.

    das problem ist eben einfach die dauer bis es das jeweilige element gefunde hat. kennt dazu jemand eine lösung die möglichst doppelt so schnell ist?
    womöglich geht es kaum schneller, aber wäre schon praktisch ^^

    EDIT:
    Ich habe die geschwindigkeiten eurer Funktionen verglichen
    AspirinJunkie's Funktion war im Test mit 512kB Daten die Schnellste. (Test mit 40k Elementen)
    Die langsamste Funktion war meine mit beträchtlichem rückstand.
    Die Ergebnisse (Ausgabe des UIs von meiner UDF TimBug. Die werd ich wenn möglich mal auf meine webseite posten ^^)

    Ich denke die ergebnisse (erstaunlich) stimmen so. wenn ich was falsch gemacht habe, wirds sich schon bemerkbar machen :)
    Danke an ProgAndy und AspirinJunkie.

    Dies ist ein Arzeneimittel.
    Bei Risiken und Haluzinationen fressen sie die Packungsbeilage und schlagen Sie ihren Arzt oder Apotheker.
    Jede Haftung wird abgelent.

    Vielen Dank für Ihre Kundentreue.
    mfg. TimBlo

    Aperture Science

    http://www.youtube.com/watch?v=Y6ljFaKRTrI

    2 Mal editiert, zuletzt von TimBlo (28. Mai 2011 um 14:38) aus folgendem Grund: gelöst

  • ich verstehe nicht genau was du meinst.

    gerade habe ich z.B 1.3MB 64er Strings in nem array das macht mehr als 43'000 elemente für das Array. allein dadurch wird es langsam. die funktion geht eig noch, nur dass die halt dieselbe menge an daten verarbeiten muss und wenn jeder string anders ist, dann geht das gut ewig.

    ich muss jetzt weg. schaue später nochmal. aber danke soweit.

    Dies ist ein Arzeneimittel.
    Bei Risiken und Haluzinationen fressen sie die Packungsbeilage und schlagen Sie ihren Arzt oder Apotheker.
    Jede Haftung wird abgelent.

    Vielen Dank für Ihre Kundentreue.
    mfg. TimBlo

    Aperture Science

    http://www.youtube.com/watch?v=Y6ljFaKRTrI

  • rückgabewert ist dann das element im array, welches am oftesten vorkommt.


    Eigentlich klingt das ja eindeutig aber wenn ich deine Funktion mal mit folgendem Array teste:

    [autoit]

    Global $a_Test[10] = ["a", "v", "c", "x", "v", "c", "h", "c", "a", "a"]

    [/autoit]

    Kommt als Ergebnis "v" heraus mit der Anzahl 1 (obwohl es ja 2x vorkommt).
    So wie ich dich verstanden habe sollte aber entweder "a" oder "c" herauskommen - jeweils mit dem Vorkommen 3 - oder?
    Wenn das wirklich das sein sollte was du haben willst dann wär mein Vorschlag z.B. dieser:

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $a_Test[10] = ["a", "v", "c", "x", "v", "c", "h", "c", "a", "a"]

    [/autoit] [autoit][/autoit] [autoit]

    Global $a_Max = ArrayCountS($a_Test)
    _ArrayDisplay($a_Max, "Elemente mit maximalen Vorkommen")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func ArrayCountS(ByRef $a_Input, $i_Start = 0)
    ; by AspirinJunkie
    Local $o_Dict = ObjCreate("Scripting.Dictionary")
    Local $i_CMax = 1, $i_C

    [/autoit] [autoit][/autoit] [autoit]

    If Not IsArray($a_Input) Then Return SetError(1, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    For $x = $i_Start To UBound($a_Input) - 1
    If $o_Dict.Exists($a_Input[$x]) Then
    $i_C = $o_Dict($a_Input[$x]) + 1
    If $i_C > $i_CMax Then $i_CMax = $i_C
    $o_Dict($a_Input[$x]) = $i_C
    Else
    $o_Dict.Add($a_Input[$x], 1)
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Local $a_Return[1] = [$i_CMax]
    For $o_Key In $o_Dict.Keys
    If $o_Dict($o_Key) = $i_CMax Then
    ReDim $a_Return[UBound($a_Return) + 1]
    $a_Return[UBound($a_Return) - 1] = $o_Key
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Return $a_Return
    EndFunc ;==>ArrayCountS

    [/autoit]

  • Eigentlich klingt das ja eindeutig aber wenn ich deine Funktion mal mit folgendem Array teste:

    [autoit]

    Global $a_Test[10] = ["a", "v", "c", "x", "v", "c", "h", "c", "a", "a"]

    [/autoit]

    Kommt als Ergebnis "v" heraus mit der Anzahl 1 (obwohl es ja 2x vorkommt).

    Ja, sry ^^
    für mich ist es standard dass ich arrays mit indexen erstelle, so kann es nicht passieren dass man mit ReDim plötzlich ein leeres array $ar[0][2] oder sowas erstellt.
    du hast mir nen echten schock eingejagt weil ich grad dachte das programm läuft jetzt fehlerhaft ^^
    warum es keinen fehler ausgibt wenn man keinen index anbaut ist.... hab ich nich untersucht ^^ so gehts doch:

    Spoiler anzeigen
    [autoit]

    Global $a_Test[11] = [10, "a", "v", "c", "x", "v", "c", "h", "c", "a", "a"]
    $ret = ArrayCountS($a_Test)
    MsgBox(0, @extended, $ret&@CRLF&@error)
    Func ArrayCountS($array)
    Dim $ary[2][2]
    $ary[0][0] = 1
    $ary[1][0] = $array[1]
    $ary[1][1] = 1
    $best = $array[1]
    $cbest = 1
    $dublic = 0
    For $c = 2 To $array[0]
    For $i = 1 To $ary[0][0]
    If $ary[$i][0] = $array[$c] Then
    $ary[$i][1] += 1
    If $ary[$i][1] > $cbest Then
    $dublic = 0
    $best = $array[$c]
    $cbest = $ary[$i][1]
    ContinueLoop 2
    ElseIf $ary[$i][1] < $cbest Then
    ContinueLoop 2
    EndIf
    $dublic = 1
    ContinueLoop 2
    EndIf
    Next
    $ary[0][0] += 1
    ReDim $ary[$ary[0][0]+1][2]
    $ary[$ary[0][0]][0] = $array[$c]
    $ary[$ary[0][0]][0] = 1
    Next
    Return SetError($dublic, $cbest, $best)
    EndFunc

    [/autoit]

    So wie ich dich verstanden habe sollte aber entweder "a" oder "c" herauskommen - jeweils mit dem Vorkommen 3 - oder?

    Jawohl. Ich sags mal so. Im test funktioniert es. aber scheinbar sagt er mir grad nicht dass noch etwas anderes(-> "c") 3mal vorkommt :(

    die frage gilt aber vorwiegend wirklich der geschwindigkeit. für 1.3 MB braucht er schon einige minuten.
    ich kann sonst einmal eine "testumgebung" machen die beide methoden vergleicht.

    Dies ist ein Arzeneimittel.
    Bei Risiken und Haluzinationen fressen sie die Packungsbeilage und schlagen Sie ihren Arzt oder Apotheker.
    Jede Haftung wird abgelent.

    Vielen Dank für Ihre Kundentreue.
    mfg. TimBlo

    Aperture Science

    http://www.youtube.com/watch?v=Y6ljFaKRTrI

  • Sollte das nicht funktionieren?

    Spoiler anzeigen
    [autoit]

    Func _ArrayMaxCount($aArray)
    _ArraySort($aArray)
    Local $iB = UBound($aArray)-1
    Local $c = 1, $cMax = 0, $vVal = ''
    For $i = 1 To $iB
    If $aArray[$i-1] = $aArray[$i] Then
    $c += 1
    Else
    If $cMax < $c Then
    $vVal = $aArray[$i-1]
    $cMax = $c
    EndIf
    $c = 1
    EndIf
    Next
    SetExtended($cMax)
    Return $vVal
    EndFunc

    [/autoit]


    Oder so:

    Spoiler anzeigen
    [autoit]

    Func _ArrayCounts(ByRef $aArray)
    Local $o = ObjCreate("Scripting.Dictionary")
    Local $iB = UBound($aArray)-1
    Local $c = 1, $cMax = 0, $vVal = ''
    For $i = 0 To $iB
    $o($aArray[$i]) = $o($aArray[$i]) + 1
    Next
    $iC = $o.Count
    Local $aRes[$iC][2], $aKeys = $o.Keys
    For $i = 0 To $iC-1
    $aRes[$i][1] = $aKeys[$i]
    $aRes[$i][0] = $o($aKeys[$i])
    Next
    _ArraySort($aRes, 1, 0, 0, 0)
    Return $aRes
    EndFunc

    [/autoit]

    2 Mal editiert, zuletzt von progandy (23. Mai 2011 um 18:25)

  • für mich ist es standard dass ich arrays mit indexen erstelle,

    Klar - hätte ich auch selbst sehen können das es daran liegt. :whistling:

    die frage gilt aber vorwiegend wirklich der geschwindigkeit. für 1.3 MB braucht er schon einige minuten.

    Dafür solltest du jetzt ein paar Lösungen haben.
    Diese sollten deutlich schneller arbeiten.
    Hab auch meine noch etwas angepasst und auf Performance, vor allem bei großen Arrays, getrimmt:

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    ; Erstelle Array mit zufälligen Buchstaben als Elemente:
    Global $a_Test[43001] = [43000]
    For $i = 1 To UBound($a_Test) - 1
    $a_Test[$i] = Chr(Random(97, 122, 1)) & Chr(Random(97, 122, 1))
    Next
    _ArrayDisplay($a_Test)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Global $a_Max = _ArrayModalwert($a_Test, 1)
    _ArrayDisplay($a_Max, "Elemente mit maximalen Vorkommen")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ======================================================================================
    ; Name ..........: _ArrayModalwert()
    ; Description ...: Bestimmt den Modalwert in einem Array
    ; Syntax ........: _ArrayModalwert(ByRef $a_Inp, Const[ $i_Start = 0, Const[ $s_Delim = "|"]])
    ; Parameters ....: ByRef $a_Inp - 1D-Array mit Stichprobenmenge
    ; Const $i_Start - [optional] Start Array-Index; gewöhnlich auf 0 oder 1 gesetzt (Standard = 0) (default:0)
    ; Const $s_Delim - [optional] Wenn "|" in den einzelnen Elementen vorkommt: ändern (default:"|")
    ; Return values .: Success: Array[0]: Anzahl an Vorkommen, Array[1..n]: häufigst vorgekommene Elemente
    ; Failure: Return 0 und setzt @error
    ; =================================================================================================
    Func _ArrayModalwert(ByRef $a_Inp, Const $i_Start = 0, Const $s_Delim = "|")
    Local $o_Dict = ObjCreate("Scripting.Dictionary")
    Local $i_CMax = 1, $i_C, $s_Mods
    If Not IsArray($a_Inp) Then Return SetError(1, 0, 0)
    If UBound($a_Inp, 0) <> 1 Then Return SetError(2, 0, 0)
    For $x = $i_Start To UBound($a_Inp) - 1
    $o_Dict($a_Inp[$x]) = $o_Dict($a_Inp[$x]) + 1
    Next
    For $i In $o_Dict.Keys
    $i_C = $o_Dict($i)
    If $i_C = $i_CMax Then
    $s_Mods &= $i & $s_Delim
    ElseIf $i_C > $i_CMax Then
    $i_CMax = $i_C
    $s_Mods = $i & $s_Delim
    EndIf
    Next
    Local $a_Return = StringSplit(StringTrimRight($s_Mods, 1), $s_Delim)
    $a_Return[0] = $i_CMax
    Return $a_Return
    EndFunc ;==>_ArrayModalwert

    [/autoit]


    Die Frage ist aber halt ob man nicht noch mehr optimieren kann wenn man wüsste woher das Array kommt und wie es entsteht.
    Ich meine wenn man direkt beim Einlesen der Daten schon ein Dictionary verwenden kann statt diesem großen Array sollte man schon vorher was rausholen können.

    Einmal editiert, zuletzt von AspirinJunkie (23. Mai 2011 um 21:04)

  • hmmm. ich weiss leider auch nicht genau wie AutoIt funktioniert aber ich bin besorgt, weil ich gerade feststelle dass er für die letzten 2 dateien etwa 70 Minuten brauchte.
    das wären 171KB oder sowas...
    ich weiss nicht recht, aber ich glaube ich geb euch mal meine UDF für das interface und dann stelle ich die source rein.
    ich wills aber erst noch ausbessern. der UDF fehlt noch der letzte schliff. am donnerstag werde ich womöglich nochmals tiefer darauf eingehen.

    das programm welches ich habe verarbeitet einfach massenweise daten. in meinem fall grade 278MB aber gibt noch mehr daten dieser art.
    ich habe einfach alle files in ein array als warteschlange gestellt und lass jede datei durchlaufen...

    die funktionen von euch sind aber super ^^ als ich meine geschrieben habe, dachte ich, ich bräuchte am ende das array noch. tja. so kanns gehen.

    Dies ist ein Arzeneimittel.
    Bei Risiken und Haluzinationen fressen sie die Packungsbeilage und schlagen Sie ihren Arzt oder Apotheker.
    Jede Haftung wird abgelent.

    Vielen Dank für Ihre Kundentreue.
    mfg. TimBlo

    Aperture Science

    http://www.youtube.com/watch?v=Y6ljFaKRTrI