1. Dashboard
  2. Mitglieder
    1. Letzte Aktivitäten
    2. Benutzer online
    3. Team
    4. Mitgliedersuche
  3. Forenregeln
  4. Forum
    1. Unerledigte Themen
  • Anmelden
  • Registrieren
  • Suche
Alles
  • Alles
  • Artikel
  • Seiten
  • Forum
  • Erweiterte Suche
  1. AutoIt.de - Das deutschsprachige Forum.
  2. Mitglieder
  3. Mars

Beiträge von Mars

  • Movie Barcode Generator

    • Mars
    • 5. September 2023 um 16:19

    Moin,

    Anbei ist ein Skript, das aus einem Film einen "Movie Barcode" vorgegebener Größe erzeugt.

    Der Code ist mehr oder weniger aus dem DE und EN Forum zusammengeklaut und neu zusammengesetzt. Da ich aber noch niemanden gesehen habe der so einen Generator gebastelt hat, dachte ich es wäre praktisch das mal zu machen. Damit das Skript funktioniert muss irgendwo eine FFMPEG.exe herumliegen. Da man sich heutzutage erstmal durch 20 Websites die 50 Versionen des Sourcecodes für alle möglichen Linux-Versionen anbieten klicken muss, bevor man eine .exe findet ist hier ein Direktlink: https://github.com/BtbN/FFmpeg-Builds/releases

    Hier ist das Skript

    AutoIt
    #include <GDIPlus.au3>
    #include <Memory.au3>
    #include <Array.au3>
    #include <Date.au3>
    
    Global Const $ffmpeg_path = 'ffmpeg.exe'
    
    ; Movie Barcode test
    _FFMPEG_Barcode('irgendein_film.avi', 'irgendein_film.png', 1024, 256)
    
    Func _FFMPEG_Barcode($sFile_Input, $sFile_Output, $iWidth, $iHeight, $nOversampling = 0.1)
    
        Local $iOffset = 0
        Local $iFrames = 1000 ; Irgendeine Zahl größer als jeder Frame des Videos
        Local $iFPS = 29.97
    
        Local Const $tagBITMAPFILEHEADER = "struct;align 2;word bfType;dword bfSize;word bfReserved1;word bfReserved2;dword bfOffBits;endstruct;"
        Local $tBMPHEADER = DllStructCreate($tagBITMAPFILEHEADER & $tagBITMAPINFO)
        Local $tBMPHEADER_B = DllStructCreate("byte[" & DllStructGetSize($tBMPHEADER) & "];", DllStructGetPtr($tBMPHEADER))
    
        Local $bData, $bTmp, $sErr, $iLen
    
        Local $sCMD = __FFMPEG_CMDGetFrames($sFile_Input, 0, 0, 0, 0, 1)
        ConsoleWrite("> CMD: " & $sCMD & @CRLF)
        Local $hPID = Run($sCMD, @ScriptDir, @SW_HIDE, BitOR(0x2, 0x4))
    
        Local $sStderr = ''
        While True
            Local $sTmp = StderrRead($hPID)
            $sStderr &= $sTmp
            If StringInStr($sStderr, 'Duration') Then ; Das steht nach Duration im Output
                $sStderr = $sTmp
                While True
                    $sStderr &= StderrRead($hPID)
                    If StringInStr($sStderr, 'Stream') Then ExitLoop 2
                WEnd
            EndIf
        WEnd
    
        Local $aLines = StringSplit($sStderr, @CRLF, 3), $iDuration, $nFPS_Source
        For $i = 0 To UBound($aLines) - 1 Step 1
            Local $sLine = StringStripWS($aLines[$i], 8)
            If StringStartsWith($sLine, 'Duration') Then
                Local $aSplit = StringSplit(StringSplit($sLine, ',', 2)[0], ':', 2)
                $iDuration = Number($aSplit[1]) * 3600 + Number($aSplit[2]) * 60 + Number($aSplit[3])
            EndIf
            If StringStartsWith($sLine, 'Stream') Then
                Local $aSplit = StringSplit($sLine, ',', 2)
                For $ii = 0 To UBound($aSplit) - 1 Step 1
                    If StringInStr($aSplit[$ii], 'fps') Then
                        $nFPS_Source = Number($aSplit[$ii]) ; Schneidet den String "fps" ab und macht ein double daraus.
                        ExitLoop 2
                    EndIf
                Next
            EndIf
        Next
        ConsoleWrite('get info ...')
        ProcessWaitClose($hPID, 1000)
        ConsoleWrite('done.' & @CRLF)
    
        $iFPS = $nFPS_Source * $nOversampling
        $iFrames = Round($iDuration * $iFPS * 1.1) ; Etwas überschätzen damit platz ist.
    
        Local $iFrameWidth = 1 ; Jeder Frame = 1 pixel breit
        Local $iFrameHeight = $iHeight ; mal Oversampling
    
        ConsoleWrite('Time[s]   ' & $iDuration & @CRLF)
        ConsoleWrite('FPS       ' & $nFPS_Source & @CRLF)
        ConsoleWrite('Frames  ~ ' & StringLeft(Round($iDuration * $nFPS_Source) & '        ', 10) & ' ( ~ ' & Round($iDuration * $iFPS) & ' with oversampling factor of ' & $nOversampling & ')' & @CRLF)
    
        $sCMD = __FFMPEG_CMDGetFrames($sFile_Input, $iOffset, $iFrames, $iFrameWidth, $iFrameHeight, $iFPS)
        ConsoleWrite("> CMD: " & $sCMD & @CRLF)
        $hPID = Run($sCMD, @ScriptDir, @SW_HIDE, BitOR(0x2, 0x4))
    
        _GDIPlus_Startup()
        Local $hBMP = _GDIPlus_BitmapCreateFromScan0($iFrames, $iFrameHeight)
        Local $hGFX = _GDIPlus_ImageGetGraphicsContext($hBMP)
        Local $vLINE = DllStructCreate('byte col[' & $iFrameHeight * 4 & ']') ; Reverse order (stride = -4)
        Local $hLINE = _GDIPlus_BitmapCreateFromScan0(1, $iFrameHeight, $GDIP_PXF32ARGB, -4, DllStructGetPtr($vLINE) + ($iFrameHeight - 1) * 4)
        Local $iLineOffset = 0, $bFirst = True
    
        While True
            $bTmp = StdoutRead($hPID, False, True)
            If @error Then ExitLoop
    
            If BinaryLen($bTmp) Then
                $bData = Binary($bData & $bTmp)
    
                While 1
                    DllStructSetData($tBMPHEADER_B, 1, $bData)
    
                    $iLen = BinaryLen($bData)
                    If $iLen < $tBMPHEADER.bfSize Then ExitLoop
    
                    If Not $bFirst Then ; Der erste Frame ist immer doppelt ?! Keine Ahnung warum...
                        DllStructSetData($vLINE, 1, BinaryMid($bData, 55, $tBMPHEADER.bfSize - 54))
                        _GDIPlus_GraphicsDrawImageRect($hGFX, $hLINE, $iLineOffset, 0, 1, $iFrameHeight)
                        $iLineOffset += 1
                    EndIf
                    $bFirst = False
    
                    $bData = BinaryMid($bData, $tBMPHEADER.bfSize + 1, $iLen)
                WEnd
            EndIf
    
            $sErr = StderrRead($hPID)
    ;~         If $sErr Then ConsoleWrite($sErr)
    
            Sleep(10)
        WEnd
    
        _GDIPlus_GraphicsDispose($hGFX)
        _GDIPlus_BitmapDispose($hLINE)
    
        Local $hArea = _GDIPlus_BitmapCloneArea($hBMP, 0, 0, $iLineOffset, $iFrameHeight, $GDIP_PXF32ARGB)
        Local $hNew = _GDIPlus_ImageResize($hArea, $iWidth, $iHeight)
        _GDIPlus_ImageSaveToFile($hNew, $sFile_Output)
        _GDIPlus_BitmapDispose($hBMP)
        _GDIPlus_BitmapDispose($hNew)
        _GDIPlus_Shutdown()
    
    EndFunc   ;==>_FFMPEG_Test
    
    Func StringStartsWith($sString, $sSubstring)
        Return StringLeft($sString, StringLen($sSubstring)) = $sSubstring
    EndFunc
    
    Func __FFMPEG_CMDGetFrames($sFile, $iOffset, $iFrames, $iW, $iH, $iFPS)
        Local $sCMD = $ffmpeg_path & ' -y'
        If $iOffset Then
            Local $_iH, $_iM, $_iS
            _TicksToTime($iOffset, $_iH, $_iM, $_iS)
            Local $_iMS = $iOffset - _TimeToTicks($_iH, $_iM, $_iS)
            $sCMD &= ' -ss ' & StringFormat("%02s:%02s:%02s.%03s", $_iH, $_iM, $_iS, $_iMS)
        EndIf
        If $iFPS <> -1 Then
            $sCMD &= ' -i "' & $sFile & '"' & ' -r ' & $iFPS
        Else
            $sCMD &= ' -i "' & $sFile & '"'
        EndIf
        If $iW > 1 Or $iH > 1 Then $sCMD &= ' -vf "scale=' & $iW & ':' & $iH & '"'
        $sCMD &= ' -vframes ' & $iFrames & ' -update true -vcodec bmp -pix_fmt bgra -f image2 -'
        Return $sCMD
    EndFunc
    
    ; Jaaaa
    Func __FFMPEG_GetAverageColor($aBMPi0)
        Local $iData, $hData, $pData, $tData, $pStream, $hImage
        $iData = BinaryLen($aBMPi0)
        $hData = _MemGlobalAlloc($iData, $GMEM_MOVEABLE)
        $pData = _MemGlobalLock($hData)
        $tData = DllStructCreate("byte[" & $iData & "]", $pData)
        DllStructSetData($tData, 1, $aBMPi0)
        Local $bgra[4]
        For $i = 55 To $iData Step 4
            $bgra[0] += DllStructGetData($tData, 1, $i + 0)
            $bgra[1] += DllStructGetData($tData, 1, $i + 1)
            $bgra[2] += DllStructGetData($tData, 1, $i + 2)
            $bgra[3] += DllStructGetData($tData, 1, $i + 3)
        Next
        For $i = 0 To 3 Step 1
            $bgra[$i] /= ($iData - 54) / 4
        Next
        Return $bgra
    EndFunc
    
    ; Kann man so machen. Für "große" Bilder ist das auch richtig so. Wir haben hier aber nur 1px breite Bilder. Man schießt nicht mit Kanonen auf Spatzen.
    Func __FFMPEG_GetImage(ByRef $aBMP, $i)
        Local $iData, $hData, $pData, $tData, $pStream, $hImage
        $iData = BinaryLen($aBMP[$i][0])
        $hData = _MemGlobalAlloc($iData, $GMEM_MOVEABLE)
        $pData = _MemGlobalLock($hData)
        $tData = DllStructCreate("byte[" & $iData & "]", $pData)
        DllStructSetData($tData, 1, $aBMP[$i][0])
        _MemGlobalUnlock($hData)
        $pStream = _WinAPI_CreateStreamOnHGlobal($hData)
        $hImage = _GDIPlus_ImageLoadFromStream($pStream)
        _WinAPI_ReleaseStream($pStream)
        Return $hImage
    EndFunc
    Alles anzeigen

    Damit lässt sich z.B. sowas hier generieren:

    (das fünfte element)

    DasfünfteElement.png

    (watchmen)

    Watchmen.png

    Die Einstellungen fürs Oversampling sind mit Vorsicht zu genießen. Der Wert von 1.0 bedeutet: JEDER Frame wird decodiert und als eine 1px hohe Linie gespeichert bevor die Bitmap anschließend in Form gebracht wird. Bei einem Film mit 200.000 Frames wird das Probleme geben (daher habe ich als Default-Wert eine 0.1 eingetragen, dann wird jeder 10te Frame verwendet und mit 20.000 Frames geht noch alles gut).

    (edit: Da ffmpeg die meiste Arbeit verrichtet werden sehr viele Videoformate unterstützt und die Geschwindigkeit ist auch ganz gut, da AutoIt nur das "Management" übernimmt und selbst gar nicht rechnen muss)

    (edit2: Für sehr große Mengen an Frames ist AutoIt das Bottleneck, da im Code angenommen wird, dass die Zeit die AutoIt braucht um die dekodierten Frames abzuarbeiten vergleichbar ist mit der Zeit die zum dekodieren gebraucht wird. Geht das Dekodieren signifikant schneller als das Managen der Daten gibt es eine quadratische Laufzeit, da im inner Loop $bData = BinaryMid($bData, $tBMPHEADER.bfSize + 1, $iLen) verwendet wird, was extrem langsam ist, wenn bData z.B. 10.000 Frames enthält. Vielleicht überarbeite ich diese Methode nochmal für Lineare Laufzeit)

    lg

    M

  • kompilierte Datei unterschiedlich groß

    • Mars
    • 13. August 2023 um 13:54

    Ob du das nachvollziehen kannst, oder nicht. In der kompilierten exe steckt "der Interpeter" & "das Skript". Das habe ich mir nicht ausgedacht, das war schon immer so und wird auch immer so bleiben. Wenn dein Skript eine konstante größe hat, dann ist der Interpreter der Teil der immer größer wird.

    Nachlesen kannst du das hier: https://www.autoitscript.com/autoit3/docs/intro/running.htm (im Unteren Teil: Run a script using another compiled script, irgendwo gibts dazu auch noch eine bessere Beschreibung, das ist die erste die ich auf die Schnelle gefunden habe)

    Wie das mit den verschiedenen Größen aussieht kann ich auch nur mutmaßen.

    lg

    M

  • kompilierte Datei unterschiedlich groß

    • Mars
    • 10. August 2023 um 22:29

    Weil die "kompilierte" Datei im Prinzip eine Kombination aus "Interpreter + Skript" ist.

    Der Interpreter (die AutoIt.exe) wird mit jeder Version ein wenig größer. Das hat mehrere Gründe (spekulativ, da ich es natürlich nicht wissen kann, aber ich kann einen educated guess anstellen):

    - Es wird selten/nie alter Code entfernt/ersetzt, sondern nur neuer hinzugefügt (kein Refactoring, sondern immer nur hinzufügen/instandhalten)

    - Es werden werden Lookuptables für einige Funktionen hinzugefügt (damit sie grundsätzlich dasselbe Ergebnis liefern, egal auf welcher Hardware das Skript läuft)

    - Es werden Features hinzugefügt/Bugs gefixt die neue #includes erfordern, sodass vielleicht nur 500 Byte neuer Code im Interpreter gelandet ist der aber eine 50 KB Vergrößerung verursacht.

    - usw.

    lg

    M

  • Heiteres Umgestalten der au3 Syntax

    • Mars
    • 8. August 2023 um 23:38

    Es ist eigentlich nur ein Spaß aus der SB, aber falls jemand interesse an sowas hat (und ggf daran weiterbasteln will) gibts dazu einen Thread.

    Warnung: Der Code ist 100% Spaghetti (daher auch nicht im Skripte Forum, sondern in OffTopic). Nicht aufgeräumt, vollkommen verwirrend und ineffizient/falsch programmiert, und an verschiedenen Stellen die dasselbe machen wird "dasselbe" auf "verschiedene Art und Weise" erledigt (da ich einfach ein paar Methoden ausprobieren wollte). Alles in allem ist es nur ein Test wie ein paar Syntaxänderungen aussehen würden, wenn sie denn existieren würden.

    Die eigentliche Idee dahinter ist einfach: Comfort.

    AutoIt zwingt dem User gewisse Patterns auf: Es gibt keine One-Line Funktionen, Es gibt keine Initialisierungsmethode für Maps, es gibt kein #define, es gibt keine impliziten Arrays in einem Return (nur um 4 Beispiele zu nennen die auch im hier geposteten Code auftreten). Bei jedem dieser Teile fällt Boilerplate-Code an (trivialer Code der immer und immerwieder nahezu gleich ist und unmengen an Zeilen, oder ein Workaround braucht).

    Ein Skript wie das folgende sollte mit dieser "UDF" nativ in Scite ausführbar sein, dieses Skript ist nur eine Demo die funkioniert. Es ist besser nicht zu erwarten, dass ein beliebiges großes Skript mit AutoItPlus laufen wird.

    AutoIt
    #AutoIt3Wrapper_Run_Au3Check=n
    #include "AutoItPlus.au3"
    
    ; #Define - Compile Time Constants
    ; Diese Konstanten werden 1:1 im Code ersetzt.
    #Define CaseSensitiveValue "CamelCase"
    #Define casesensitivevalue "lowercase"
    ConsoleWrite(CaseSensitiveValue & @CRLF)
    ConsoleWrite(casesensitivevalue & @CRLF)
    
    ; #Define mit mehreren Zeilen (vorsicht, kein Support von #Define mit Variablen, also #Define something(x) x^2 wird nicht funktionieren.)
    #Define MyFuncDefinition Func EuclideanLength($a, $b) \ Return Sqrt($a^2 + $b^2) \ EndFunc
    MyFuncDefinition ; wird entpackt zu
    ; Func EuclideanLength($a, $b)
    ; Return Sqrt($a^2 + $b^2)
    ; EndFunc
    ConsoleWrite('3^2 + 4^2 = ' & EuclideanLength(3, 4) & '^2' & @CRLF)
    
    ; #Define mit Strings
    ; Normalerweise werden Strings von #Define in Ruhe gelassen, es sei denn sie ergeben einen 100% match.
    #Define 'test' 'erfolgreich'
    ConsoleWrite('test ' & 'test' & @CRLF) ; das erste 'test ' enthält ein Leerzeichen und ist daher kein 100% match.
    
    ; #Enum - Compile Time Enum (verringert Overhead bei der Verwendung von Enums)
    #Enum EINS, ZWEI, DREI
    ConsoleWrite(EINS & ' ' & ZWEI & ' ' & DREI & @CRLF)
    
    ; Map als Nachschlagewerk, jeder Key wird mit dem leeren String initialisiert.
    Global $List['a', 'b', 'c']
    ConsoleWrite(MapExists($List, 'a') & ' ' & MapExists($List, 'x') & @CRLF)
    
    ; Map mit Initializer-List -> Da der Matcher nicht robust ist dürfen in der Initializerlist KEINE Operatoren verwendet werden. Nur Strings und Integer.
    Global $Map['a', 'b', 'c', 1, 2, 3] = ['str a', 'str b', 'str c', 'int 1', 'int 2', 'int 3']
    ConsoleWrite($Map['a'] & ' ' & $Map[1] & @CRLF)
    
    ; Implizites Array im Return
    Func ReturnTest()
        Return [1, 2, 3]
    EndFunc
    ConsoleWrite(ReturnTest()[0] & ' ' & ReturnTest()[1] & ' ' & ReturnTest()[2] & @CRLF)
    
    ; Single Line Function (aktuell nur Global-Scope)
    Global Add($a, $b) Return $a + $b
    ConsoleWrite('3 + 5 = ' & Add(3, 5) & @CRLF)
    
    ; Kombinationen von obigen Sachen funktionieren "manchmal"
    Global Array($a, $b) Return [$a, $b]
    ConsoleWrite(Array(1, 2)[0] & ' ' & Array('a', 'b')[1] & @CRLF)
    Alles anzeigen
    Code: AutoItPlus.au3 (die Datei MUSS so heißen, sonst gibts Probleme weil der Name Hardcoded verwendet wird)
    ; Es stehen nur 40.000 Zeichen zur Verfügung...

    Wenn weitere Ideen auftauchen, schreibt einen Kommentar :)

    Gesucht sind keine "Verunstaltungen" der Syntax (Geschweifte Klammern, Semikolons, Syntax anderer Sprachen, etc), sondern "Vereinfachungen" von Stellen die beim Programmieren immer wieder nerven.

    Und nicht vergessen: Immer schön "#define True False" verwenden, da kommt Freude auf <3

    Andere Ansätze:

    - synsug (von chesstiger)

    lg

    Mars

    Dateien

    AutoItPlus.au3 44,27 kB – 373 Downloads
  • Formen erkenn

    • Mars
    • 19. Juli 2023 um 12:27

    Bitte formuliere die Frage besser.

    "gibt es eine Möglichkeit in autoit formen wie z.B ein Viereck oder ein Dreieck oder sowas zu erkennen"

    Beispiel 1: Speichere die Koordinaten für ein Axis-Aligned-Rect und ein Dreieck in einem Array. Vierecke brauchen 4 Werte (x, y, w, h im R²), Dreiecke brauchen 6 Werte (3 Punkte mit je x und y)
    -> Die Unterscheidung ist hier trivial via UBound.

    Beispiel 2: Das Dreieck/Viereck ist in einer 2D Computergrafik (pixel), Alle Vierecke sind Axis-Aligned (also durch x, y, w, h darstellbar), alle Kanten sind scharf, alle Flächen sind einfarbig.
    -> Ebenfalls trivial. Das Bild besteht nur aus einfarbigen Flächen verschiedener Form, ein "Ist das ein Viereck" check ist hier trivial. Ein Dreieck-Check wird etwas anstrengender aber auch möglich.

    Beispiel 3: Das Dreieck/Viereck ist in einer 2D Computergrafik (pixel), alle flächen sind einfarbig. Freie Rotation ist erlaubt.
    -> Hier hört der (triviale) Spaß bereits auf. Ein (halbwegs) einfacher weg (wenn alles einfarbig ist) besteht darin die Flächen via floodfill algorithmus aus dem Gesamtbild zu trennen. Dann kann man sie einzeln betrachten und außenkanten finden wo 50% der Pixelzentren links und 50% rechts von einer Linie liegen. Das macht man ein Mal für 3 und ein Mal für 4 Kanten, schaut wo der Fit besser passt -> Dreieck/Viereck unterschieden.

    Beispiel 4: Das Dreieck/Viereck ist in einer 2D Computergrafik (pixel), Flächen können (milde) texturiert sein (eine Textur die Dreiecke/Vierecke beinhaltet wäre natürlich fies), freie Rotation ist erglaubt.
    -> Hier hört das auf was ich zuverlässig ohne nachzuschlagen programmieren kann.

    Beispiel 5: Das Dreieck/Viereck ist in einer Fotografie (pixel). Es ist perspektivisch verzerrt, hat eine Textur, die Kanten sind unscharf.
    -> Hier hört das auf was ich zuverlässig mit viel Arbeit und Nachschlagen programmieren kann. Eine halbwegs funktionierende Version davon musste ich vor Ewigkeiten mal für eine Computer-Vision VL basteln. Da habe ich aber C++ draufgeworfen und zusammen mit OpenCV alles mit Rechenpower besiegt was nicht bei 3 auf dem Baum war. Das wird in AutoIt nicht funktionieren.


    Daher bitte: Formuliere die Frage besser. Keiner kann wissen was du willst.

    M

  • ChatGPT für AutoIt - Talk, Ideen, Kuriositäten

    • Mars
    • 6. Juli 2023 um 12:20

    Das letzte ist wieder eine Permutation.

  • ChatGPT für AutoIt - Talk, Ideen, Kuriositäten

    • Mars
    • 5. Juli 2023 um 22:30

    Es geht einfach nicht. Ich habe es auch eine ganze Weile lang versucht. Sobald man eine Anzahl N Zahlen (oder Buchstaben) aus einem Bereich der ebenfalls die Größe N hat will kommen die Sachen in geordneter Reihenfolge, oder als Permutation (keine doppelten). Und selbst wenn man mehrfach darauf hinweist, dass Zufallsziehungen voneinander Unabhängig erfolgen (ich muss ja nicht erst alle Zahlen von 1 bis 6 würfeln, bevor der Würfel wieder "resettet" ist. Der Würfel "resettet" sich nach jedem einzelnen Wurf) gibt es kein Licht am Ende des Tunnels. Daher kam auch meine ursprüngliche Formulierung, dass "doppelte ausdrücklich erwünscht sind" weil ich erwartet habe, dass Buchstaben dann wenigstens doppelt vorkommen. In voller Trollmanier gabs dann das eben gepostete Ergebnis mit aa, bb, cc, ... das "technisch gesehen" eine Zufallsfolge mit doppelten Buchstaben ist. Aber jeder Grundschüler würde das besser machen^^

    Die einzige Chance ist es z.B. nach 10 Zufallszahlen im Bereich von 0 bis 3 zu fragen. Dann bekommt man tatsächlich einige Zahlen doppelt (weil es sonst unmöglich ist diese Frage zu beantworten)...

    Ist schon seltsam: Schlüsselfragen zu wirklich kompliziertem Kram (Finite Element Analysis, das ist gerade mein Bastelthema) kann ChatGPT überraschend ordentlich beantworten und auch hinweise auf Fallstricke bei verschiedenen Methoden abliefern (vermutlich hat es sämtliche Fachzeitschriften dazu in den Trainingsdaten gehabt), aber einfach 26 zufällige Buchstaben ausgeben ist unmöglich :rofl:

    PS: An dem ersten Ergebnis ist einfach "alles" perfekt.
    - Der Trollpost ansich "aa, bb, cc, dd, ..." (mit hervorhehobenem doppel d)
    - Die Sprache "Bash" (in Anlehnung an die 2000er, wo "jemanden bashen" noch als "jemanden fertigmachen" in der Jugendsprache zu finden war)
    - Der grenzwertig sarkastische Zusatzkommentar, dass Buchstaben doppelt vorkommen "können".
    Etwas schöneres habe ich noch nie gesehen :D

  • Funktionsnamen mit Unterstrich

    • Mars
    • 28. Juni 2023 um 13:17
    AutoIt
    Ich verwende das immer so:
    MyFunc    -> Funktion die "klein" ist. z.B. Cosh, Sinh, Array, Map, MapContains, Print, StringToStruct, etc.
    _MyFunc   -> Mittelgroße Funktionen, im Falle von UDFs: Diese Funktionen enthalten die Hauptfunktionalität
    __MyFunc  -> ByRef Wrapper oder Internals.
    ___MyFunc -> ByRef Wrapper (andere Ausreden gibts nicht für 3 Layer :D)
    
    Globale Variablen:
    $ABC_MyVar, wobei ABC die Abkürzung für das Modul, oder die UDF ist. z.B. $A3D_PEN -> Teil von A3D, Enthält einen GDIPlus-Pen.

    ("große" Funktionen gibts bei mir nicht, weil ich üblicherweise alles zerteile, wenn es zu groß wird)

    M

  • Halteproblem

    • Mars
    • 23. Juni 2023 um 18:19
    Zitat von Alina

    Sorry, aber ich verstehe gar nichts. Was für ein Haltepunkt? Mir fehlt das innere Bild vor den Augen um was es geht.

    Es geht um das Halteproblem was in gefühlt 99.9% der Fälle als theoretisch unlösbar dargestellt wird, dabei ist es nur auf Maschinen mit unendlichem Speicher theoretisch unlösbar (und selbst dagegen würde ich ein Veto einlegen. Wenn man schon "unendlich" viel Speicher ins Spiel bringt, dann ist 2^unendlich (die Menge Speicher die man brauchen würde um alle inneren Zustände von unendlich vielen Bits zu loggen) nicht weit hergeholt) dargestellt wird, obwohl es für Maschinen mit "endlichem" Speicher (N Bits) theoretisch lösbar ist (vorausgesetzt man hat 2^(N+1) Bits an Speicher um alle theoretisch möglichen Zustände der Maschine mitzuschreiben).

    Dank der englischen Wikipedia (und noch ein paar anderen Infos die ich gefunden habe) ist mein Problem damit eigentlich geklärt, habe den Thread trotzdem offengelassen (und nicht gelöscht/geschlossen), weil mich die Darstellung immer ärgert


    Anschauliches Beispiel:

    - Ein Computer (oder eine Virtual Machine) hat 32 Bit RAM (ja ich weiß, das ist sehr wenig)

    - Der Computer hat einen beliebig großen Programmspeicher.

    - Jedes beliebige Programm A das auf diesem Computer läuft (egal wie groß es ist, es liegt im Programmspeicher) kann ausschließlich die 32 Bit RAM manipulieren

    - Es gibt also 2^32 verschiedene Zustände die das RAM haben kann.

    - Ein anderes Programm B (von außerhalb) soll beurteilen ob A hält oder nicht.

    - Das andere B schreibt jeden Zustand von A in eine Logdatei (Größe der Logdatei ist maximal 2^33Bit = 8GBit)

    - Sobald A in einen Zustand verursacht der schonmal in der Logdatei vorkam -> Programm ist in einer Endlosschleife

    - Sonst: A wird eine Anzahl Innerer Zustände < 2^32 durchlaufen und dann terminieren ->

    - qed.

    Fazit:
    Um das Halteproblem für ein Programm A zu lösen muss dem Überwachungsprogramm B ein Speicher von 2^N Bits zur Verfügung stehen, wobei N die Anzahl Bits ist die A manipulieren kann.

    Für endliche Speichergrößen ist das Problem also lösbar (wenn auch unpraktikabel... Angenommen A hat 1MB RAM, dann braucht B 2*2^8388608 Bits RAM, was ziemlich viel ist), und für unendliche Speichergrößen ist es logischerweise auch lösbar, weil wenn man schon mit unendlich anfängt ist 2^(unendlich+1) auch möglich.

    M

  • Halteproblem

    • Mars
    • 21. Juni 2023 um 20:37

    Moin,

    immer mal wieder taucht irgendwo eine Doku, ein Video, ein Artikel, oder sonstiges über das Halteproblem auf (auch vor ein paar Wochen in der SB, wo ich etwas ähnliches geschrieben habe wie hier im Thread). Leider bin ich nicht in der Lage zu verstehen warum das Halteproblem ein Problem ist. Und inzwischen geht mir jede Erklärung die ich irgendwo sehe auf den Zeiger, weil ich sie einfach nicht verstehe (bzw. jede beliebige Erklärung sofort mit den unten genannten 2 Punkten widerlegen kann).

    Definition (stark vereinfacht. Aber ich gehe davon aus, dass jeder das Problem kennt):

    Das Programm H entscheidet ob Programm P mit input X (also P(X)) hält, oder nicht.

    Das Programm R mit Input H(...) hält, wenn H(...) entscheidet, dass "..." nicht hält, das Programm R geht in eine Endlosschleife, wenn H(...) entscheidet, dass "..." hält.

    H soll entscheiden ob R hält.

    R hält, wenn H entscheidet, dass R nicht hält und umgekehrt -> Problem.

    Meine Probleme damit:

    1. Instanzen. ALLES ist instanziiert. Die Aussage "R hält, wenn H entscheidet, dass R nicht hält" ist Unfug, da H für einen vollständigen Input (P(X), oder eben R(...)) entscheidet ob P(X) hält. Die Parameter mit denen H gestartet werden sind also zum Zeitpunkt wenn H gestartet wird fixiert. Also können die Parameter sich im Nachhinein nicht mehr ändern und das Ergebnis von H beeinflussen. Und selbst wenn man annimmt, dass es keine Instanzen gibt, dann werden H, R, P und X alle auf einem endlichen Computer (mit endlichem Speicherplatz) ausgeführt, sodass Punkt 2 zur Geltung kommt.

    2. Endlichkeit. ALLES ist endlich. Jedes Programm (wie auch immer es aussehen mag) hat eine endliche Anzahl innerer Zustände (alle bits die das Programm beeinflussen kann). Ein Programm H könnte jetzt jeden inneren Zustand von P(X) mitschreiben. Wenn P(X) in einen inneren Zustand eintritt den es früher schonmal hatte (weil P(X) deterministisch arbeitet), ist P(X) in einer Endlosschleife. Wenn P(X) vorheriges nicht erfüllt wird es nach endlicher Zeit seine endlichen inneren Zustände durchlaufen haben und halten. -> Problem gelöst.

    Kann es sein, dass das Halteproblem nur für "unendliche" Maschinen sinnvoll definiert ist?

    Ich bin einfach zu blöd dafür und Wikipedia hilft mir auch nicht weiter...

    Falls jemand eine "einfache" Erklärung hat wie der Hase läuft, her damit. :D

    Vielleicht habe ich auch einen Denkfehler. Auch in diesem Fall: Her damit.


    Edit: Kaum hab ich die englische Wikipedia angeschaut habe ich meinen eigenen Satz auf englisch gefunden...

    The halting problem is theoretically decidable for linear bounded automata (LBAs) or deterministic machines with finite memory. A machine with finite memory has a finite number of configurations, and thus any deterministic program on it must eventually either halt or repeat a previous configuration

    Na super... Dann bleibt nur noch die Frage übrig, warum das offensichtlich für "nicht existierende Computer" definierte Problem andauernd falsch dargestellt wird...

    lg

    M

  • Rückgabewert in leerer Variable

    • Mars
    • 6. Juni 2023 um 23:02

    Ich bin nicht sicher ob ich das Problem verstanden habe, aber hier der übliche Lösungsweg:

    - Denke dir einen "Errorwert" aus (in den Meisten fällen ist das Int32 -1, also $s_Breite = -1)

    - Prüfe am Ende, ob dein Wert -1 ist (If $s_Breite = -1 Then ...) um herauszufinden ob ein Wert gesetzt wurde.

    Alternativ kann man auch ausnutzen, dass AutoIt einen Variant Datentyp hat. Wenn du exakt weißt, dass der Wert zu einem String (egal ob leer, oder voll) wird, dann initialisiere sie z.B. als Int32 (z.B. $s_Breite = 0). Anschließend kannst du via If IsInt($s_Breite) Then ... prüfen ob die Variable von irgendwem gesetzt wurde. Ein richtiger Errorwert wie -1 ist aber idr die bessere Idee.

    lg

    M

  • Jahresstromverbrauch

    • Mars
    • 5. Juni 2023 um 17:11

    Das mit dem Akku ist bei uns auch so ne Sache. Mein Vater (der alte Hobbyelektriker) hat da was gebastelt, sodass wenigstens alle Kühlschränke und Gefrierschränke und einige andere kleine 24/7 Verbraucher auch Nachts über Solar laufen. Da ich keine Ahnung habe was der Spaß gekostet hat weiß ich nicht wie die Amortisierung aussieht.
    Außerdem haben wir deutlich über 100% (im Sommer) ausgebaut, sodass man ca. 70% des Jahres quasi komplett über Solarstrom läuft sobald die Sonne aufgeht (ich wohne aber auch auf nem Berg, bei mir scheint immer die Sonne, in der Stadt kann man das wahrscheinlich knicken wenn der Schatten von Nachbarhäusern etc. die Sonne verdeckt, die Garage nicht im Freien steht, oder das Dach zu klein ist). Im Winter kommt dafür natürlich nicht genug rein, sodass hier wirklich nur die "Kleinverbraucher" dauerhaft via Solarstrom funktionieren. Was bei Überproduktion auch ganz praktisch ist ist ein Heizstab im Wassertank (Zentralheizung, aktuell mit Holzofen als Wärmequelle... eine Wärmepumpe ist in Planung, kommt aber erst irgendwann später, dafür muss man erstmal sparen), dann bleibt der Ofen das halbe Jahr lang aus und man hat trotzdem Warmwasser und Heizung (wobei letztere im Sommer ja eher wenig genutzt wird... hauptsächlich im Keller). Klingt (und ist) verschwenderisch, aber es ist aktuell (bei uns) sinnvoller den Überschuss komplett zu verbraten als ihn einzuspeisen^^

    Was die Akkutechnik angeht hab ich aber schon seit Ewigkeiten aufgegeben an ein Wunder zu glauben. Zu oft wurde uns der "neue XYZ Akku mit 95% Wirkungsgrad und 3x Kapazität (vgl mit LiPo) und halbem Preis" versprochen und nie ist etwas dabei herumgekommen.

    Selbstverständlich ist alles obige vollkommen Subjektiv. Für andere Leute an anderen Orten gelten andere Regeln :)

  • Jahresstromverbrauch

    • Mars
    • 5. Juni 2023 um 02:39

    Kleines Haus auf dem Land, 3 bewohnte Etagen (Keller, Erdgeschoss, Dach), 4 Personen sind dauerhaft da, manchmal 5. Jahresstromverbrauch "vor" Solarausbau: ca. 8000 kwh. Aktueller Verbrauch (mit Solar): ca. 2500 kwh.

    Das mit der Solarkraft war echt mal nötig. Wenn es ne anständige Vergütung gäbe würde ich noch doppeltsoviel installieren (aktuell ist nur die Garage und der Carport vollgepackt, ein Großteil vom Hausdach ist noch frei). Aber das werde ich nicht mehr erleben, scheinbar will niemand meinen grünen Strom haben :D

  • wie spricht man Array im Array direkt an?

    • Mars
    • 2. Juni 2023 um 16:42

    Die "reine" multidimensionale Arrays funktioniert das (so sollte es ja auch sein). Bei verschachtelten Arrays funktioniert es aber nicht. Vermutlich wird AutoIt-intern nicht nach jedem Array Indexzugriff via Brackets aktualisiert welche Variable gerade verwendet wird. Ist natürlich nur eine Vermutung, kenne den aktuellen Code von AutoIt ja nicht.

    AutoIt
    Global $a[1], $b[1]
    $b[0] = 123
    $a[0] = $b ; Legt eine Kopie von $b in $a[0] an.
    
    ;~ $a[0][0] = 5
    ; Interpretation:
    ; - gib mir den Index [0][0] von $a -> Error, $a hat keinen Index [0][0]
    
    ($a[0])[0] = 5
    ; Interpretation:
    ; - Werte den Klammerinhalt zuerst aus
    ; - aktualisiere die gerade verwendete Variable ByVal auf $a[0] (unsichtbare Kopie)
    ; - schreibe an Index [0] von dieser Kopie eine 5 -> Das bewirkt nichts, geht aber.
    
    ConsoleWrite(($a[0])[0] & @CRLF)
    ; Lesezugriff geht natürlich, weil es wurst ist ob man eine Kopie, oder das Original ausliest.
    Alles anzeigen

    Bei Maps ist die Sache vollkommen anders. Hier wird nach jedem Index AutoIt-intern ByRef aktualisiert wo man gerade ist. Deshalb kann man beliebig geschachtelte Maps haben und hat immer direkten lese- und Schreibzugriff.

    AutoIt
    Global $a[], $b[]
    $b[0] = 123
    $a[0] = $b ; Legt eine Kopie von $b in $a[0] an.
    
    $a[0][0] = 5
    ; Interpretation:
    ; - gib mir den Index [0] von $a
    ; - aktualisiere die gerade verwendete Variable ByRef auf $a[0]
    ; - schreibe an Index [0] von dieser Variable eine 5
    
    ConsoleWrite($a[0][0] & @CRLF)
    ; Interpretation:
    ; - gib mir den Index [0] von $a
    ; - aktualisiere die gerade verwendete Variable ByRef auf $a[0]
    ; - lese von Index [0] von dieser Variable
    Alles anzeigen

    M

  • wie spricht man Array im Array direkt an?

    • Mars
    • 1. Juni 2023 um 13:15

    Hier ist auch etwas Vorsicht geboten. Es gibt einen Unterschied zwischen "Lesezugriff" und "Schreibzugriff" auf Arrays und Maps. Die Klammernotation funktioniert (bei Arrays) nur für den Lesezugriff. Bei Maps werden die Klammern (meistens) nicht benötigt. Das "meistens" schreibe ich dazu, weil Array/Map Kombinationen scheinbar komplett wahnsinnig sind.

    AutoIt: Array Lesen/Schreiben
    ; Version mit Arrays
    Global $a = ['a.Element0', 'a.Element1']
    Global $b = [0, $a]
    
    ; Schreibzugriff:
    ($b[1])[0] = 'Ich bin das neue Element 0' ; Klammernotation wirft zwar keinen Error, schreibt aber auch nicht.
    
    ; Lesezugriff: Klammernotation klappt.
    ConsoleWrite('b[0] = ' & VarGetType($b[0]) & ' -> ' & $b[0] & @CRLF)
    ConsoleWrite('b[1] = ' & VarGetType($b[1]) & ' -> [' & ($b[1])[0] & ', ' & ($b[1])[1] & ']' & @CRLF)
    
    ; Schreibzugriff via ByRef Hilfsfunktion:
    _writeAt($b[1], 0, 'Ich bin das neue Element 0')
    
    ConsoleWrite('b[0] = ' & VarGetType($b[0]) & ' -> ' & $b[0] & @CRLF)
    ConsoleWrite('b[1] = ' & VarGetType($b[1]) & ' -> [' & ($b[1])[0] & ', ' & ($b[1])[1] & ']' & @CRLF)
    
    Func _writeAt(ByRef $a, $i, $value)
        $a[$i] = $value
    EndFunc
    Alles anzeigen
    AutoIt: Maps Lesen/Schreiben
    ; Version mit Maps
    Global $a[], $b[]
    $a[0] = 'a.Element0'
    $a[1] = 'a.Element1'
    $b[0] = 0
    $b[1] = $a
    
    ; Schreibzugriff: Funktioniert ohne Klammern, wenn beides Maps sind
    $b[1][0] = 'Ich bin das neue Element 0'
    
    ; Lesezugriff: Funktioniert ohne Klammern, wenn beides Maps sind
    ConsoleWrite('b[0] = ' & VarGetType($b[0]) & ' -> ' & $b[0] & @CRLF)
    ConsoleWrite('b[1] = ' & VarGetType($b[1]) & ' -> [' & $b[1][0] & ', ' & $b[1][1] & ']' & @CRLF)
    Alles anzeigen
    AutoIt: Test von Array/Map Kombinationen
    #AutoIt3Wrapper_Run_Au3Check=n ; Um auszuschließen, dass au3Check dazwischenfunkt, (selbst wenn es das in diesem Beispiel nicht macht)
     
    #cs
    ; Map Version
    Global $Map[], $Item[]
    $Map.items = $Item
     
    MapAppend($Map.items, 123)
     
    ;~ $Map.items[0] = 5 ; ERROR
    ;~ ($Map.items)[0] = 5 ; Kein Error, aber Klammersyntax ist readonly
    $Map['items'][0] = 5 ; OK
     
    ConsoleWrite('ReadAccess (dot syntax): ' & $Map.items[0] & @CRLF) ; OK
    ConsoleWrite('ReadAccess (array syntax): ' & $Map['items'][0] & @CRLF) ; OK
    #ce
     
    ;~ #cs
    ; Map Version2
    Global $Array[1], $Item[]
    $Array[0] = $Item
     
    ;~ $Array[0][0] = 5 ; ERROR
    ;~ ($Array[0])[0] = 5 ; Kein Error, aber Klammersyntax ist readonly
    MapAppend($Array[0], 5) ; OK
    ;~ _($Array[0], 5) ; OK
     
    ConsoleWrite('ReadAccess (parentheses syntax): ' & ($Array[0])[0] & @CRLF) ; OK
    ;~ ConsoleWrite('ReadAccess (bracket syntax): ' & $Array[0][0] & @CRLF) ; ERROR
     
    Func _(ByRef $a, $b)
        $a[0] = $b
    EndFunc
    ;~ #ce
     
    #cs
    ; Array Version
    Global $Map[], $Item[0]
    $Map.items = $Item
     
    ArrayAppend($Map.items, 123)
     
    ;~ $Map.items[0] = 5 ; ERROR
    ;~ ($Map.items)[0] = 5 ; Kein Error, aber Klammersyntax ist readonly
    ;~ $Map['items'][0] = 5 ; ERROR
     
    ; Weiß jemand wie man hier ohne error und ohne byref funktion schreiben kann?
     
    ConsoleWrite('ReadAccess (dot syntax): ' & $Map.items[0] & @CRLF) ; OK
    ;~ ConsoleWrite('ReadAccess (array syntax): ' & $Map['items'][0] & @CRLF) ; ERROR
     
    Func ArrayAppend(ByRef $a, $v)
        ReDim $a[UBound($a)+1]
        $a[UBound($a)-1] = $v
    EndFunc
    #ce
    Alles anzeigen

    All diese Tests sind unvollständig (es gibt noch ein paar Syntaxtricks die ich nicht ausprobiert habe), sollten aber einen Überblick darüber geben was geht, und was nicht geht.

    Es ist außerdem (gerade bei den Array/Map Kombinationen) nicht ersichtlich ob es sich hierbei um Bugs handelt oder nicht. Von daher ist jeder "Trick mit Klammern" potentiell irgendwann nicht mehr gültig, die "Hilfsfunktion mit ByRef Version" sollte aber immer funktionieren, auch wenn sie die umständlichste Methode darstellt.

    lg

    M

  • Huffman-Kodierung + Helferfunktionen für Umgang mit binären Daten

    • Mars
    • 24. Mai 2023 um 17:55

    Der Grund warum ich niemals "sinnvolle" Sachen mache ist, weil ich genau weiß, dass es irgendwo irgendwen gibt der genau das Selbe schonmal gemacht hat. Wenn ich aber die Vernunft hinten anstelle wird die Chance etwas "neues" auszuprobieren höher ^^

    Und da wir alle in der Matrix leben wird "denen da oben" doch bestimmt langweilig wenn niemand aus der Reihe tanzt :thumbup:

    (Wobei man sich dann fragen muss wie viele Leute von der Sorte es gibt. Wenn es genügend sind wird es auch wieder langweilig, weil sich dann jeder nur einbildet etwas "neues" zu machen)

  • Huffman-Kodierung + Helferfunktionen für Umgang mit binären Daten

    • Mars
    • 24. Mai 2023 um 14:39
    Zitat von AspirinJunkie
    • Je nach Umfang der Umsortierung ist dies mal eine kurze, mal eine lange Liste. Sie ist aber immer kürzer (geht auch gleich lang?) als das Gesamtarray.
      • Die Liste hat (zumindest bei meinem Ansatz) immer die Länge n-1, da ich für die Swaps nur "eine einzige Zahl" Speichern will und nicht 2. Daher ist der eine Swap-Partner fix ($i ist immer der erste Parameter, also geht man das Array einfach linear durch), und der andere bekommt eine Zahl die man Huffman-kodiert. Man hat also immer n-1 Swaps, was außer bei vollständig zufälligen Arrays nicht optimal ist, aber jede andere Methode war mir zu kompliziert (bzw. dann brauchen die Swaps immer 2 Parameter, und da muss man erst noch untersuchen welche Eigenschaften die haben um die zu komprimieren, bei dem "Ein Parameter Swap" (also immer Swap($i, $n)) weiß man ja exakt welche Werte $n haben kann (immer im Bereich [0, $iLen - $i], und dieser Bereich wird kleiner wenn $i steigt), und das nutzt man zum komprimieren aus.
  • Huffman-Kodierung + Helferfunktionen für Umgang mit binären Daten

    • Mars
    • 23. Mai 2023 um 16:12

    Jop, das was Andy anspricht war auch mein eigentlicher Plan für die Kompression (aber in Bezug auf Permutationen). Allerdings habe ich den erstmal auf Eis gelegt, weil mir keine gute Methode eingefallen ist um zu entscheiden welche Operation man jetzt durchführt.

    Mein bisheriger Ansatz war (aufbauend auf der Swap methode)

    Mögliche Operationen:

    - Swap(n) -> Vertausche [i] und [n], an Position [i] ist jetzt das gewünschte Symbol aus [n], danach i+=1 (Swap Methode, in meinem Anhang heißt sie Compress3, Compress4 ist die Huffman Version davon)

    - Rot(n) -> Rotiere das Subarray von [i] bis [n], sodass das Symbol aus [n] bei [i] landet, danach i+=1
    - Mirror(n) -> Kehre das Subarray von [i] bis [n] um. Jetzt liegt das gewünschte Symbol aus [n] bei [i], danach i+=1
    - Nop -> Lasse alles so wie es ist (symbol bereits am richtigen Ort), danach i+=1

    Jetzt müsste man eine Analyse des Restarrays machen die für jede mögliche Operation bestimmt, welche Operation das Restarray im "besten Zustand" hinterlässt. Das ist ansich ein typisches Optimierungsproblem das man mit verschiedenen Ansätzen (näherungsweise, müsste NP sein) lösen kann, sobald man eine gute Fehlerfunktion (die angibt wie "gut" das Restarray ist) & Heuristik (die spekuliert wie gut ein potentieller Schritt ist und damit den Entropieencoder füttert, sodass der weiß: "mit 22.5% Wahrscheinlichkeit kommt Mirror(24)") gefunden hat.

    Meine Swap-Methode jetzt ist eigentlich ziemlich schlecht (die funktioniert hier nur durch Zufall). Angenommen man hat die Permutation [1, 2, 3, 4, 5, 0]. Dann bräuchte das trotzdem 5 Swaps. Eigentlich müsste man also Rotationen auf jeden Fall mit einbauen. Gleiches gilt für Mirrors, angenommen ich habe [5, 4, 3, 2, 1, 0], dann brauche ich wieder 5 Swaps. (Das sind jetzt nur Extrembeispiele).




    Edit: Und die Idee für das CharArray ist folgendes:
    - Zerlege in: "Welche Chars sind vorhanden", und "in welcher Reihenfolge".

    - "Welche Chars sind vorhanden" kann man via iMin, iLen (incl Nullen) und 1Bit/Char speichern. Problem: Es gibt Lücken, diese Lücken enthalten sämtliche Information.
    - "In welcher Reihenfolge" ist eine Permutation. Da weiß ich noch nicht ob es sinnvoll ist die überhaupt getrennt zu betrachten.

    Ansatz:

    - Finde den "Schwerpunkt" in der 1Bit/Char Liste (z.B. 10 Symbole, davon sind 0, 3, 4, 5, 6, 7, 9 vorhanden -> 1001111101, Schwerpunkt = Jeder Ort an dem links und rechts davon gleich viele nullen stehen.
    - Finde eine Menge Swaps, sodass die Liste Lückenlos wird. (am Beispiel hier: Swap(0, 2) und Swap(8, 9)) -> Die Liste ist jetzt 0011111110
    - Speichere nur noch iMin, iLen, (iSchwerpunkt müsste sich daraus berechnen lassen wenn er vorher korrekt gewählt wurde) + Swaps anstatt die Liste selbst, denn die Liste kann daraus rekonstruiert werden. (die "Welche Chars sind vorhanden" Information ist damit kompakt gespeichert). Aus "1001111101" wird "2, 7, swap(0,2), swap(8, 9)", wobei ich bei den Swaps noch überlegen muss ob die lieber relativ zu iMin, zum Schwerpunkt, oder zu irgendwas anderem am besten sind.
    - Für die Reihenfolge kann man wieder entweder eine Permutation komprimieren, oder die liste direkt manipulieren. Was davon besser ist.... wer weiß, muss man ausprobieren.

    Alernativ könnte man auch RLE auf die vorherige Methode (sortiertesCharArray in binärform nach dem Huffman) anwenden. Das gibt bestimmt auch nochmal ein paar bytes.


    M

  • Huffman-Kodierung + Helferfunktionen für Umgang mit binären Daten

    • Mars
    • 23. Mai 2023 um 13:32

    Ob der Ansatz einen Namen hat weiß ich nicht, den habe ich mir spontan ausgedacht :D

    Warum ich überhaupt darauf gekommen bin (hätte vor einiger Zeit auch noch gesagt, dass man eine "zufällige" Zeichenkette nicht komprimieren kann) ist weil ich viel zu viel Zeit damit verbracht habe zu versuchen vollständig zufällige Permutationen zu komprimieren (was interessanterweise tatsächlich geht), sogar deutlich unter das intuitive Limit (z.B. hat eine Permutation der Länge 64 NICHT 6 Bit/Symbol Entropie, sondern nur ca. 4.6).

    Ein sehr einfaches Beispiel dazu klaue ich aus meinem eigenen Code:

    AutoIt
        ; Primitives Beispiel: Tabelle der Länge 10
        ; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <- Sortierte Version
        ; [8, 0, 4, 3, 6, 5, 7, 1, 2, 9] <- Permutation
        ; i = 0, steigt mit jedem swap um 1
        ;                [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -> Sortierte Liste
        ; Swap(i=0+8) -> [8, 1, 2, 3, 4, 5, 6, 7, 0, 9] -> Encode 8 (max 9 -> 4 Bit)
        ; Swap(i=1+7) -> [8, 0, 2, 3, 4, 5, 6, 7, 1, 9] -> Encode 7 (max 8 -> 4 Bit)
        ; Swap(i=2+2) -> [8, 0, 4, 3, 2, 5, 6, 7, 1, 9] -> Encode 2 (max 7 -> 3 Bit)
        ; Swap(i=3+0) -> [8, 0, 4, 3, 2, 5, 6, 7, 1, 9] -> Encode 0 (max 6 -> 3 Bit)
        ; Swap(i=4+2) -> [8, 0, 4, 3, 6, 5, 2, 7, 1, 9] -> Encode 2 (max 5 -> 3 Bit)
        ; Swap(i=5+0) -> [8, 0, 4, 3, 6, 5, 2, 7, 1, 9] -> Encode 0 (max 4 -> 3 Bit)
        ; Swap(i=6+1) -> [8, 0, 4, 3, 6, 5, 7, 2, 1, 9] -> Encode 1 (max 3 -> 2 Bit)
        ; Swap(i=7+1) -> [8, 0, 4, 3, 6, 5, 7, 1, 2, 9] -> Encode 1 (max 2 -> 2 Bit)
        ; Swap(i=8+0) -> [8, 0, 4, 3, 6, 5, 7, 1, 2, 9] -> Encode 0 (max 1 -> 1 Bit)
        ; Swap(i=9+0) -> [8, 0, 4, 3, 6, 5, 7, 1, 2, 9] -> Encode Nichts, ist immer 0
        ; -> Encode 8, 7, 2, 0, 2, 0, 1, 1, 0 ( = 25 Bit, wenn man die Maximale Bitrate jedes Mal verwendet)
        ; Hier kann man jetzt angreifen, da je nach Aufbau der Permutation viele kleine Zahlen auftreten können. -> Compress 4
    Alles anzeigen

    Man beginnt mit einer sortierten Liste (Entropie davon ist quasi null, die einzige Information ist die "Länge der Liste").
    Dann hat man einen laufenden Index ($i) der in jedem Schritt um eins erhöht wird.
    Nun sucht man die Zahl mit der man einen Swap durchführen muss, um Position $i in der Permutation zu restaurieren.

    Anschließend erhöht man $i um eins und geht solange weiter, bis man aus der sortierten Liste die zu kodierende Permutation gemacht hat.

    Der Trick ist nun der folgende: Die Zahlen die man im Swap angeben muss (Swap($n) bedeutet Tausche [$i] mit [$n]) werden mit der Zeit logischerweise kleiner, da $i größer wird und die Liste ihre Länge beibehält. Im obigen Beispiel muss man also [8, 7, 2, 0, 2, 0, 1, 1, 0] + Länge der Liste kodieren um die Permutation [8, 0, 4, 3, 6, 5, 7, 1, 2, 9] zu rekonstruieren (ja die Liste ist ein Symbol kürzer, das liegt daran, dass nach n-1 swaps das letzte Element automatisch richtig liegt). Hier kann man jetzt mit Huffman ansetzen, da die zu kodierenden Symbole nicht nur immer kleiner werden, sondern auch eine bestimmte Wahrscheinlichkeitsverteilung (die von der "Sortiertheit" der Restliste abhängt) haben. (Ich habe den Teil der die "Sortiertheit" berücksichtigt aber nicht implementiert, sondern immer eine Gleichverteilung angenommen, im Fall des CharArrays (das "fast" zufällig ist) klappt das jedenfalls ganz gut^^).

    So schafft man es eine Permutation der Länge 64 (Beispiel) mit ca. 4.9 Bit/Symbol (Compress4 schafft ca. Entropielimit + 0.5Bit/Symbol im worst case) zu speichern anstatt (wie intuitiv angenommen) 6 Bit/Symbol aufzuwenden.

    Jetzt kommt Schritt 2: Wie wende ich das auf ein CharArray an?
    - Wo sind die Informationen im CharArray? -> In der Anordnung (Permutation), In den Absolutwerten (also "welche Zahl ist wo" auf gut Deutsch)
    - Die Anordnung kann man trivial finden, da wir wissen, dass jedes Symbol nur 1x vorkommt. Also einfach eine Kopie der Liste sortieren und in O(n²) alles durchgehen (If $aList[$i] = $aCopy[$j] Then $aPerm[$i] = $j)
    - Die Sortierte Liste ist z.B. 9, 10, 27, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ... -> (Abstandskodierung) -> 9, 1, 17, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...

    - Was wir speichern müssen ist $iMin, $iMax und die Länge (die Länge kann man auch weglassen, wenn man es schlau anstellt, habe ich aber nicht gemacht)

    - Außerdem müssen die Zahlen 9, 1, 17, 5, 1, 1, 1, ... in dieser Reihenfolge gespeichert werden. Huffman freut sich, denn die 1 ist quasi 80% von allen Zahlen.

    - Für die Wahrscheinlichkeiten habe ich das hier gewählt, nachdem ich mir ein paar Daten geplottet habe $aHist[$i] = 1 / ($i+1)^3. Die 1 ist extrem wahrscheinlich, alles andere eher weniger.

    -> 10011 000101011 11111100001 001111100 1 001100000 001010 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 nach der Entropiekodierung.

    -> ($iMin, $iMax, $iUbound-1, 9, 1, 17, 5, ... jeweils binär). -> Man kann das sortierte CharArray also recht kompakt speichern. Mit einem arithmetischen Encoder wäre hier noch einiges drin, weil die vielen Einsen beim Huffman jeweils 1 Bit ergeben was eigentlich nutzlos ist.

    Beides Zusammen ergibt dann 15 Byte Ersparnis beim Text der englischen AutoIt Seite, und ganze 26 Byte beim Text der deutschen Seite :D

    Habe aber noch eine andere Idee (wo Anordnung und Absolutwerte nicht erst getrennt und dann getrennt kodiert werden), wenn ich Zeit habe bastel ich die mal.

    lg

    M

    Dateien

    CompressPermutationTest.zip 9,52 kB – 406 Downloads
  • Huffman-Kodierung + Helferfunktionen für Umgang mit binären Daten

    • Mars
    • 22. Mai 2023 um 22:19

    Du wusstest doch, dass ich hier jetzt irgendetwas poste :P

    Weil ich ja eine lustige Kompressionsmethode für Permutationen gebaut habe dachte ich: Das kann man doch bestimmt für deinen komprimierten Huffmantree verwenden (genauer: Für die Charliste), und siehe da: Nach mindestens 3-4 Stunden herumgebastel habe ich 15 Bytes rausgeholt (was bei der Dateigröße richtig sinnvoll ist... 73334 ->73319 Bytes, und ich bin stolz drauf :rofl: )

    Vermutlich gibt es einige deutlich einfachere und weniger Rechenaufwendige Methoden die vermutlich auch mehr als 15 Byte Gewinn abwerfen, aber wie heißt es so schön: Der Weg ist das Ziel.

    Ein Disclaimer direkt zu Beginn: Ich bin nicht sicher ob die Methode "immer" so performed. Ich bin nichtmal sicher ob sie "immer" korrekt decodiert. Habe nur ein wenig herumprobiert und es sah "okay" aus. 80% von der Datei im Anhang sind C&P, eigentlich habe ich nur etwas ganz kleines geändert (das CharArray), aber irgendwie musste ich dafür 500 Zeilen Code kopieren weil da so viel dranhängt :S


    Die Methode ist ganz einfach:
    Das CharArray besteht ja aus "irgendwelchen Chars an irgendwelchen Positionen in einem Array". Dabei ist kein Char doppelt.
    -> Man kann eine Permutation finden, sodass die Chars alle sortiert sind.
    -> Die Charliste besteht also aus [Permutation] + [SortierteCharliste], beides lässt sich ziemlich gut (dicht am Entropielimit) komprimieren.
    -> Die Permutation hat schöne Eigenschaften (z.B. nichts doppelt, in sortierter Form sind es die Zahlen 0 bis n-1, also ist im sortierten Zustand der Abstand zweier Werte immer 1, und so weiter), die sortierte Charliste hat quasi überall den Abstand 1 (den man via "Huffman im Huffman" entropiekodieren kann).

    lg

    M

    Dateien

    TreeCompressionTest.au3 35,61 kB – 381 Downloads

Spenden

Jeder Euro hilft uns, Euch zu helfen.

Download

AutoIt Tutorial
AutoIt Buch
Onlinehilfe
AutoIt Entwickler
  1. Datenschutzerklärung
  2. Impressum
  3. Shoutbox-Archiv
Community-Software: WoltLab Suite™