[Wettbewerb] String komprimieren

  • Ich nominiere mich mit dem pastebin Beispiel selbst als Kandidat für den "worst abuse of the rules" Award. Dicht gefolgt von der Methode den vollständigen Text im Code zu speichern und nur auszugeben :rofl:

  • Die Länge beträgt nun 6033 Bytes und befindet sich in der Datei "original.txt" im ZIP-Archiv.

    Eigentlich sind es nicht 6033 Bytes sondern 6033 Zeichen.

    Da die Kodierung der Datei UTF-8 ist und einige Zeichen außerhalb des ASCII-Bereiches enthalten sind, wird für einige Zeichen mehr als 1 Byte benötigt.

    Der String hat daher eine tatsächliche Größe von 6105 Bytes.

    Man könnte daher schon alleine durch die Umkodierung in ISO 8859-2 1,2% Dateigröße einsparen.

    In der Bewertung hier würde dies jedoch nicht honoriert werden, da diese ausschließlich auf die Zeichenanzahl abstellt.

    Noch wilder würde es, wenn man diesen Umstand ausnutzt und den großen Wertebereich von UTF-32 nimmt um jeweils 4 Ausgangszeichen in nur ein einziges UTF-32-Zeichen zusammenzufassen (analog geht auch UTF-16).
    Auf diese Weise würde man hier als wahrscheinlicher Sieger vom Platz gehen, da wir nur noch 25% soviele Zeichen haben, obwohl die Dateigröße nahezu gleich bleibt.

    Daher plädiere ich dafür, die Bewertung komplett von der Zeichenlänge zu entkoppeln und nur die resultierende Dateigröße als Bewertungsmaßstab zu verwenden.

    Mal zur Verdeutlichung - Folgende 2 Funktionen:

    AutoIt
    Func _StringCompress($sString)
        Return BinaryToString(StringToBinary($sString & (Mod(StringLen($sString), 2) ? Chr(0) : ""), 4), 2)
    EndFunc
    
    Func _StringDecompress($vCompress)
        Return BinaryToString(StringToBinary($vCompress, 2), 4)
    EndFunc

    würden zu einer Bewertungskompressionsrate von 49.395 % führen obwohl eine hieraus resultierende Datei genauso groß ist wie die Ausgangsdatei. (hier speziell sogar noch 3 Byte mehr, da die ungerade Anzahl an Bytes hier einfach nicht behandelt wurde)

    Einmal editiert, zuletzt von AspirinJunkie (10. März 2022 um 10:25)

  • Daher plädiere ich dafür, die Bewertung komplett von der Zeichenlänge zu entkoppeln und nur die resultierende Dateigröße als Bewertungsmaßstab zu verwenden.

    Ja, ich verstehe, was Du meinst, aber mir fehlt eine mögliche Lösung.

    Wenn ich die Datei als UTF8_NOBOM einlese und die komprimierte Datei als Binary speichere, danach wieder als Binary einlese und mit dem Original vergleiche, dann stimmen die Daten nicht mehr, weil jetzt ANSI.

    Oder ich muss gleich die Originaldaten als ANSI speichern. Dann ist das halt nur für den Wettbewerb.

    Angesichts der vielen Probleme, ist die Aufgabenstellung vielleicht doch nicht so gut. :/

  • Wenn ich die Datei als UTF8_NOBOM einlese und die komprimierte Datei als Binary speichere, danach wieder als Binary einlese und mit dem Original vergleiche, dann stimmen die Daten nicht mehr, weil jetzt ANSI.

    Die wird nur als ANSI interpretiert wenn du veranlasst, dass der Binary als ANSI zu interpretieren ist.
    Vielleicht steckt auch gar kein String in der Datei sondern eine ganz andere Struktur, so dass erst bestimmte weitere Schritte durchgeführt werden müssen?

    Kurz: Wenn die Funktionen gar keine Strings als Input und Output bekommen sollen, sondern Dateinamen, dann hat sich der Teilnehmer darum zu kümmern, dass die komprimierte Datei korrekt in eine Datei geschrieben werden und von dort auch korrekt wieder eingelesen werden.

    Der Größenvergleich ist hier einfach und fair, da einfach nur die beiden Dateigrößen miteinander verglichen werden müssen.

    Die beiden Funktionen _StringCompress() und _StringDecompress() werden also um Dateioperationen erweitert:

    AutoIt
    ; komprimiert den String $sString und speichert das Komprimat (gibts das Wort?) in die Datei $sFilePath
    Func _StringCompress($sString, $sFilePath)
    
    
    ; extrahiert aus der komprimierten Datei $sFilePath den Ausgangsstring
    Func _StringDecompress($sFilePath)
        ...
        Return $sString
  • Wenn ich die Datei als UTF8_NOBOM einlese und die komprimierte Datei als Binary speichere, danach wieder als Binary einlese und mit dem Original vergleiche, dann stimmen die Daten nicht mehr, weil jetzt ANSI.

    Beim Einlesen der Daten mit angegebenem Encoding UTF8_NOBOM, sollte AutoIt es intern in UCS2 umwandeln. Letzten Endes ist die Codierung ja aber egal, es muss nach dem Dekomprimieren nur das gleiche rauskommen, wie die Daten zuvor waren...

    Neuster Stand mit Prekompressor:

    ============================================================

    Result for "Ahnungslos"

    ============================================================

    Decompressed = Original: True

    Length Original: 6033

    Length compressed: 3236

    Length decompressed: 6033

    Compress-Ratio: 46.362 %

    Time compressed: 4420 ms

    Time decompressed: 258 ms

    ============================================================

  • Ich hab etwa 2000 Zeilen Spaghetticode fabriziert der zu allem Überfluss auch noch ganz besonders langsam ist und nicht die Kompression erreicht die ich gerne hätte^^

    Werde das irgendwann Morgen abgeben, viel Spaß beim Durchlesen Oscar :rofl:

    PS: Nächstes Mal wird die 50% in Angriff genommen. Das ist zwar noch ein paar Meter, aber das schaffen wir ^^

  • Ich hab etwa 2000 Zeilen Spaghetticode fabriziert

    ...willkommen im Club....

    Ich bin durch eine Augen-OP seit einigen Wochen ziemlich eingeschränkt, daher weiß ich nicht, ob ich überhaupt bis zum Termin fertig werde. Aber die 50% habe ich schon (halbwegs locker) geknackt, sogar bei (*.unkomprimierteEXE-Binärdateien)

  • AspirinJunkie = Komprimat (gibts das Wort?)

    Komprimat: [das] Substantiv, Neutrum
                        
    Zusammengefasstes, -gepresstes


    Und selbst, wenn es das Wort nicht gegeben würde, würden wir dich aus dem Zusammenhang verstehen.

    Und sonst haben wir die Möglichkeit nachzufragen, was es bedeuten würde. Ist ja alles auch nur Bewohner des blauen Planeten. :rock:

    Lieben Gruß,
    Alina

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Geheime Information: ;)
    OuBVU5ebLhHu5QvlnAyQB4A7SzBrvWulwL7RLl2BdH5tI6sIYspeMKeXMSXl

  • Ohje, wenn Andy die 50 geknackt hat muss ich wohl nochmal schrauben :rofl:

    Ich hatte einfach keine Lust auf alt bewährtes zurückzugreifen und hab meine Idee (Prediction geboosteter dynamischer Huffman Tree) weiterverfolgt. Leider hat das eine entscheidende Schwäche: Ganze Bits.

    Egal wie gut die Vorhersage ist, im Regelfall kann man 3 Bit/Symbol nicht unterschreiten, da im Huffman-Tree, genauso wie bei Telefonnummern keine vollständigen 01-Ketten existieren dürfen die gleichzeitig der Anfang einer anderen Kette sind. Wenn es also eine 2Bit Kette gibt (z.B. "01"), dann kann es keine einzige weitere Kette geben die mit "01" beginnt. Der Tree wird "optimal" erstellt, und wird den Teufel tun, bei sämtliche anderen Zeichen die Bitketten um 1 zu verlängern, nur weil die Chance für ein "a" als nächstes Zeichen 20% ist (das wäre aber nötig um unter die 3Bit/Symbol zu kommen).

    Der Ansatz ist super, rennt aber bei Huffman komplett gegen die Wand...

    Nächste Idee ist dann: Prediction geboosteter arithmetischer Kodierer. Der sollte mindestens 10%-20% besser klarkommen (damit sollte auch 60% bei Text kein Problem sein).

  • Na da lag ich mit meiner Schätzung, dass mein Skript etwa im Mittelfeld landet ja perfekt richtig :D

    Schade jedoch, dass es wirklich nur 3 Einsendungen gab.

    Das Interesse daran schien anfangs ja größer zu sein (Andy? :/ )

    Hätte gerne weitere Ansätze gesehen.

    Eventuell hatten einige Angst sich "zu blamieren", weil schon vorab Ergebnisse gepostet wurden?

    Das wäre wirklich schade, denn z.B. bei mir lag die Optimierung rein aus persönlichen Interesse auf der möglichst optimalen Huffman-Kodierung.
    Ich habe jedoch auch eine Vorkompilierung drin die wirklich simpel ist und in die ich nur wenig Zeit gesteckt habe.

    Hier hatte ich auf Ansätze von anderen gehofft, welche bisschen mehr rausholen - das Potential ist definitiv da.

    Am Ende hätte man ein kombiniertes Skript gehabt, welches noch höhere Kompressionsraten erreicht.

    Also wer ein Skript geschrieben hat, welches den String wieder selbst in einen String komprimiert, der mag ihn bitte bitte mit uns teilen - auch wenn er nicht an die anderen Ergebnisse ranreicht.

    Diese werden primär durch entsprechende Zeichenkodierung erreicht und eben nicht durch Veränderungen des Strings selbst - daher können uns solche Lösungen weiterhelfen.

    Zum Thema: "Performance-Krone": Ganz ganz ehrlich: ich hab nicht im Mindesten auf Performance hin optimiert. Mir fallen auf Anhieb 4 Stellen ein, die man durch umcoden noch beschleunigen könnte. Klar über die Zeit hat man gelernt, was Performance frisst und was nicht und hat sein normales coden daraufhin angepasst.

    Aber direkt auf Performance ist das Ding überhaupt nicht optimiert.

    Ebenso gibt es so gut wie kein Error-Handling, keine hinreichende Codedokumentation/kommentierung und getestet wurde ausschließlich mit diesem String.

    Bugs bei anderen Inputs halte ich für hinreichend wahrscheinlich.

    So - nun schaue ich mir aber mal genauer an was die anderen so fabriziert haben. :)

    Danke nochmal Oscar - hat wirklich Spaß gemacht! :rock:

    Edit: Oscar : Genau genommen ist die Kompressionsrate von Mars noch besser. Bei Ahnungslos und mir wird die Anzahl an Bytes des Eingangsstrings als Vergleichsgröße genommen (6105). Bei Mars hingegen ist es die Stringlänge (6033). Die Kompressionsrate bei ihm sollte also zum Vergleich eher 50,762 % betragen.

    Einmal editiert, zuletzt von AspirinJunkie (1. April 2022 um 10:06)

  • Glückwunsch an Mars für die beste Komprimierung:thumbup:

    AspirinJunkie für die auch sehr gute und sehr schnelle Komprimierung :thumbup:

    Hier mal das Resultat mit externen Tools / DLLs:

    Size File Ratio %
    6105 original.txt 0.00%
    3552 original.png 41.82%
    2875 original_WinAPI_XPRESS_HUFF.bin 52.91%
    2803 original_lzma2.7z 54.09%
    2776 original.rar 54.53%
    2626 original_bzip2.7z 56.99%
    2492 original_ppmd.7z 59.18%

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Andy war wahrscheinlich verhindert, vielleicht kommt von ihm später noch etwas :)

    Der Trick an der Sache war bei mir das Finetuning (was aber eigentlich auch Informationen über den zu Komprimierenden Inhalt braucht, also die eingestellten Parameter funktionieren gut für deutschen Text, aber vermutlich nicht so gut für andere Sachen).

    z.B. Wie stark wird das Histogramm geboostet? (1 Parameter)

    z.B. Wird der boost im Verlauf der Kodierung stärker oder schwäche (1 Parameter)

    z.B. Wie arbeitet der Predictor intern? (2 Parameter (lässt sich zusammenfassen als Verhältnis))

    z.B. Wie stark wird das Histogramm aktualisiert nachdem dein Zeichen dekodiert wurde? (1 Parameter)

    Ich habe insgesamt 4 Magic Numbers die alle für gewisse Einstellungen verantwortlich sind. Die kann man auch so einstellen, dass der Text 5% schlechter Komprimiert wird und genaugenommen müsste ich diese Werte (die man quasi bruteforcen muss) mit in den kodierten String stecken was nochmal ca. 40 Bit braucht. Da ich aber ohnehin schon so knapp an den 50% war wollte ich das einfach nicht machen... Wenn man das berücksichtigt bin ich quasi gleichauf mit AspirinJunkie (nur noch ein paar Bytes besser), wobei sein Skript 50x schneller ist :D