Code Kata - Happy Numbers

  • Hi Leute,

    hier mal was für zwischendurch, die "Happy Numbers" Code Kata.

    💡 Ziel: Ist es einen Algorithmus, bestehend aus einer oder mehreren Funktionen zu schreiben, womit erkannt wird ob eine gegebene Zahl "happy" ist oder nicht 😁 .

    • Ein "glückliche" Zahl ist eine Zahl, bei der die Summe der Quadrate ihrer Ziffern auf die Dauer 1 ergibt.
    • Beispiel für Zahl 19: 19 -> 1^2 + 9^2 = 82 -> 8^2 + 2^2 = 68 -> 6^2 + 8^2 = 100 -> 1^2 + 0^2 + 0^2 = 1

    🥈 Teil 1: Entwickle die Funktion(en) und lass dir 19 -> 1 :) mit ConsoleWrite() bspw. ausgeben.
    🥇 Teil 2: Lass dir alle "happy numbers" im Bereich von 1 bis 100 ausgeben (analog wie bei Teil 1).

    ⚡ Rahmenbedingungen:

    • Versuche es möglichst komplett ohne Hilfsmittel und ohne Google Suche.
    • Zeitliche Vorstellung für diese kleine Challenge ist um die 30 min.
    • Wenn du eine Lösung hast und diese hier postest, dann bitte den Code in Spoiler einbinden, damit jeder selbst entscheiden kann ob er die Lösungsvariante der Anderen sehen möchte oder nicht 😉 .

    Ich bin gespannt auf eure Lösungen und wünsche happy coding 👍 .

    Viele Grüße

    Sven

  • Mal was anderes...

    Hier mein Lösungsvorschlag:

    Spoiler anzeigen

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    Einmal editiert, zuletzt von UEZ (4. Februar 2022 um 12:17) aus folgendem Grund: Kleine Optimierung.

  • Das hat mich grad auch gecatcht. ^^

    Spoiler anzeigen

    Bevor jemand fragt: Die 20 in Zeile 4 ist quasi experimentell ermittelt. Alle Zahlen im Bereich 1-100 laufen entweder auf 1 hinaus oder enthalten (periodisch) eine 20.

  • Hi zusammen,

    Xorianator ja das ist eine ganze Weile her 😅 , bei mir länger als bei dir wenn deine Geburtsangabe stimmt. Jedoch wurde damals auf "agile" oder "clean code" nur wenig eingegangen.
    Egal [...], ich denke du kommst sicherlich noch mit deinem Lösungsvorschlag "um die Ecke" oder 🤞 ?

    UEZ Korrekt 👍 , kurz und äh, kurz 😂 .

    chesstiger Korrekt 👍 , ebenfalls kurz und doch ein wenig anders.

    Hier mal mein Lösungsansatz:

    Spoiler anzeigen

    Den Preis für Code-Effizienz verdient dieser sicherlich nicht 😔 , aber vielleicht Punkte ich in Sachen Lesbarkeit/Verständlichkeit?




    Viele Grüße
    Sven

  • Hi, ich schreibe nicht mehr viel in AutoIt. Daher ist meine ursprüngliche Lösung in Lua. Ich habe jedoch den Code nach AutoIt übersetzt nachdem ich fertig war.

    Lua 5.4 Skript: Alle "Happy Numbers" von 1 bis 10'000'000 in ca. 8 Sekunden in eine Text Datei.

    AutoIt Skript: Alle "Happy Numbers" von 1 bis 100'000 in ca. 8 Sekunden in die Konsole.

    Spoiler anzeigen

    chesstiger

    Spoiler anzeigen

    Bevor jemand fragt: Die 20 in Zeile 4 ist quasi experimentell ermittelt. Alle Zahlen im Bereich 1-100 laufen entweder auf 1 hinaus oder enthalten (periodisch) eine 20.



    Tatsächlich ist es so dass jede Zahl entweder auf 1 ended oder in diese Zahlenloop sich verläuft:
    4, 16, 37, 58, 89, 145, 42, 20, 4

    Deine 20 ist dabei. Demnach ist deine Lösung ebenfalls korrekt. Man kann also gegen einer dieser Zahlen prüfen.

    5 Mal editiert, zuletzt von Yjuq (4. Februar 2022 um 06:53)

  • Hi Yjuq ,

    auch dein Lösungsvorschlag liefert das korrekte Ergebnis 👍 .

    • Sowohl bei Zahlen von 1 bis 100, aber auch bei 1 bis 10000 bspw. => dies gilt für alle bisherigen Lösungen 😀 .
    • Bisher ist deine Variante auch die schnellste bei bspw. 10000 in meinem Test.
    • Eine Optimierung würde jedoch in einem Folgeschritt folgen, daher nicht relevant für die Aufgabenstellung.

    Danke für's Mitmachen.

    Viele Grüße
    Sven

  • Eine Optimierung würde jedoch in einem Folgeschritt folgen, daher nicht relevant für die Aufgabenstellung.

    Falls du daran denkst ggf. bereits gefundene Zahlen zwischenzuspeichern um den Rechenaufwand zu minimieren -> Das klappt nicht. Es stellt sich heraus (zumindest was ich dazu versucht hatte) dass dies langsamer ist und zusätzlich Speicher im RAM benötigt. Zumindest mit den Lösungsansätzen welche ich versucht hatte. Es ist schneller die Zahlen einfach neu zu berechnen.

  • Hi Yjuq ,

    Falls du daran denkst ggf. bereits gefundene Zahlen zwischenzuspeichern um den Rechenaufwand zu minimieren -> Das klappt nicht. [...]

    Okay, spannend das du bereits einige Tests gemacht hast 👍 . In erster Linie geht es um die Aufgabenstellung nicht um Performanz oder ähnliches - dennoch gut zu wissen, Danke.

    Viele Grüße

    Sven

  • Und RegEx ist langsamer als die StringSplit Funktion.

    Benchmark nach der Reihenfolge der Beiträge:

    Rekursionen sind i.d.R. einfacher zu implementieren -> kürzerer Code, aber langsamer.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Da so wenige mitmachen, trage ich halt auch mal was dazu bei. Da die Aufgabenstellung war "von 1 bis 100" kann meine Version auch nur eine begrenzte Anzahl machen (nur bis 3-stellig, also max. 999)

  • Könnte hinhauen

    Spoiler anzeigen
  • Hi zusammen,

    zunächst muss ich sagen, dass ich noch nie von diesen "Happy Numbers" gehört hatte^^

    Daher bin ich mal völlig unvoreingenommen an diese ganze Sache herangegangen.

    Wie einige von euch sicherlich wissen, schwärme ich für die "brotlose Kunst" aka Grafikspielereien und oldschool Assemblerprogrammierung, und so war mein Ziel, die Daten der "Happy Numbers" als Bitmap darzustellen.

    OT Das habe ich mir übrigens bei diversen "Hackings" von unbekannten Daten angewöhnt, da findet man die interessantesten Sachen. Wer darüber mehr erfahren will, bitte per PN.

    Diese Bitmap wäre dann nichts weiter als eine LUT (LookUpTabelle), die Frage ob eine Zahl im Wertebereich Happy oder Unhappy ist beschränkt sich dann nur noch auf eine Abfrage des Pixels an dieser Adresse.....

    Also sollte eine Bitmap erstellt werden, in der jede Happy Number ein gesetztes Pixel ist. Fehlte nur noch der Algorithmus^^

    Der "offensichtliche" Weg für mich war, die Zahlen von 1 bis 9 hinzuschreiben und dann die "Folge" nach Vorschrift zu erstellen, die dann hoffentlich zur 1 (Zahl ist Happy) führt.....oder eben nicht (Zahl ist Unhappy).

    Dabei hatte ich festgestellt, dass die "Zwischensummen" (der Quadrate der Ziffern) unweigerlich ebenfalls Happy bzw. Unhappy sind, da sie ja in direkter Folge auf Happy bzw. Unhappy hinauslaufen.

    Man muss also während der Ermittlung der Folge nur die aktuell ermittelte Quadratsumme auf Happy bzw. Unhappy überprüfen, um diese "Folge" frühzeitig abbrechen zu können.

    Bei Beispielsweise der 6 ergeben sich die Quadratsummen 36, 45, 41, 17, 50 ..... !!!BÄM!!!

    50 wird in der Folge zu nichts anderem als 5²+0² aufgelöst. 5 ist aber bereits Unhappy, daraus folgt, dass 6 auch Unhappy sein muss. Desweitern sind 36, 45, 41, 17 auch Unhappy.

    Ist eine Zahl Happy, dann sind definitiv sämtliche Quadratsummen auf dem Weg zur 1 auch Happy!

    Dann kam ich auf die Idee, diese Quadratsummen in ein Array einzutragen (aka Bitmap^^) und entsprechend zu markieren.

    Jede Zahl, die als Happy ermittelt wurde, wird im Array als 1 markiert ($array[happyzahl]=1), jede Unhappy Zahl als ihren Wert ($array[unhappyzahl]=unhappyzahl)

    Ich stellte fest, dass die maximale Größe einer einzelnen Quadratsumme höchstens die Summe aus der Anzahl der Ziffern multipliziert mit dem Quadrat der größten Ziffer, also 9, sein kann.

    Bsp. Eine Zahl hat 14 Ziffern, die größte Ziffer ist 9, das zum Quadrat ist 81, das mal 14 ergibt 1134.

    Man braucht also "nur" beim Test einer 14-stelligen Ziffer ein Array mit 1134 Feldern....

    Innerhalb dieses Arrays müssen nur noch die Indizes mit 1 für Happy und mit Zahl für Unhappy gesetzt werden (s.o) und tadaaa, im Algorithmus wird bei Zahlen von 1135 bis 99999999999999 nur noch EINE EINZIGE QUADRATSUMME (die immer kleiner ist als 1134) gebildet, aus der man dann ermittelt, ob diese Zahl dann Happy oder Unhappy ist!

    Ich hatte dafür eine rekursive Funktion _IsHappy() geschrieben, im Endeffekt bedeutet das, dass spätestens ab der Zahl 1135 die Rekursion im ersten Schritt verlassen wird! Und das IMMER, egal wie groß die Zahl wird, es wird immer nur die Berechnung der ersten Quadratsummenzahl benötigt um per Lookup im Array sofort auf Happy bzw. Unhappy zu kommen....


    Und dann wurde es spannend!!!

    Ich erstellte eine Bitmap von 1000x1000 Pixeln (jaja, ermittelt wurden die Happy Numbers von 1 bis eine Million) und markierte nur die ermittelten Happy Numbers mit einem farbigen "rosa" Pixel.

    AutoIt-typisch erfolgte die "Berechnung" recht langsam, aber immerhin etliche Faktoren schneller als mit der von (bisher schnellsten _IsHappyNumber()-Funktion vom geschätzten Kollegen Yjuq!

    Mir fielen sofort "Muster" im Bild auf! OHA! Seht ihr auch diese "Fensterkreuze"?

    Als ich dann die obere linke Ecke stark vergrößerte, wunderte ich mich noch mehr!

    Fällt euch auch auf, dass die Happy Pixel an der Diagonale von oben links nach unten rechts gespiegelt sind!?

    Was nichts anderes bedeutet, als dass NUR BEI DER HÄLFTE der Zahlen überhaupt der Status "Happy" ermittelt werden muss....

    Beispiel: In der obersten Pixelzeile sieht man von links die "Happy Pixel" 1, 7,10,13 usw. (in der Bitmap ist das erste Pixel an Adresse 0!)

    Das bedeutet, in der linken Pixelspalte von oben ist ebenfalls das 1te, 7te,10te,13te usw. Pixel gesetzt.

    Wenn also an den Koordinaten (x,y) ein Happy Pixel ist, dann auch das an der Koordinate (y,x).

    Anbei mein unoptimiertes und auf die schnelle hingehacktes Script zur Ermittlung der "Happy Numbers" incl. Erstellung der 1000x1000 Bitmap...ich werde mich morgen mal an die Beschleunigung machen...

    Im abschließenden Arraydisplay wird das Array angezeigt, welches zur Ermittlung der Happy Numbers erstellt wurde.

    Ist der Feldinhalt 1, dann ist die Indexzahl Happy, ansonsten der Feldinhalt (Indexzahl ist Unhappy)

    Nach schließen des Arraydisplay kann man mit der Maus in der Grafik die Positionen der Pixel anfahren und im Tooltip die dort "gespeicherte" Zahl ermittelt aus den Koordinaten erfahren, der Farbwert des Pixels bestimmt dann, ob die Zahl Happy (Pixelfarbe hat nicht Hintergrundfarbe) oder Unhappy ist (Pixelfarbe hat Hintergrundfarbe)

    //EDIT

    Script aktualisiert, es wird nun zuerst das Sieb mit den Happy/Unhappy Zahlen gefüllt, danach muss für jede Zahl nur noch die Quadratsumme ermittelt werden, die der Index für das Sieb ist.

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    6 Mal editiert, zuletzt von Andy (7. Februar 2022 um 12:22) aus folgendem Grund: Script aktualisiert

  • Coole Analyse Andy ! Die Tatsache der Spiegelung ist definitiv ein Booster bezgl. Performance (keine Impfung :P ). Mir kam intuitiv die Rekursion als Lösung in den Kopf, aber das Ganze grafisch darzustellen, war mit nicht eingefallen.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Andy : Wirklich eine coole Analyse :thumbup: . Danke zudem, dass Du die Herleitung so ausführlich beschrieben hast.

    86598-musashi-c64-png

    "Am Anfang wurde das Universum erschaffen. Das machte viele Leute sehr wütend und wurde allenthalben als Schritt in die falsche Richtung angesehen."

  • Hehe....die "Aufgabe" hat mich an die µ-It hier im Forum erinnert....das ist bestimmt schon 10 Jahre her, oder?!

    So etwas sollte/könnte man wieder mal aufleben lassen.

    BTT:

    Die Tatsache der Spiegelung ist definitiv ein Booster bezgl. Performance

    Ja, mich hat gewundert, dass das noch keiner gemacht hatte, ich habe jedenfalls keine Info darüber gefunden. Daten "grafisch" zu analysieren ist ein uralter Hut, "früher" hatte ich bei Analysen von fremden Programmen einfach die gesamte (*-EXE-) Datei in eine Bitmap gemapped (omg, was ein Wortspiel^^) und diese Bitmap per Programm in diversen Pixelbreiten anzeigen lassen. Da hat man dann feine Sachen gefunden, Zeichensätze, eingebundene Bilder uvm.

    Im vorliegenden Fall war ich aber echt überrascht, diese Muster zu finden...naja, wenn man mal bissl nachdenkt, dann ist ja auch klar wo die herkommen. 8)

    Die Idee mit dem "kleinen" Array habe ich mir bei einem eigenen Script, dem erweiterten Primzahlensieb (auch ein µ-It gewesen) abgeschaut, mit LUT geht vieles viel schneller und trotz meines biblischen Alters und fortschreitendem Gehirnschwund fallen mir doch noch so manche Tricks ein :rock:

    Jedenfalls hats Spass gemacht! Danke auch dafür an @SOLVE-SMART!

    Ich habe das zum Anlass genommen, wieder mehr mit AutoIt zu machen, hab noch soooo viele Sachen offen, OpenCL und OpenCV, und nicht zuletzt Assembler, habe letzte Woche Kontakt mit Jungs und Mädels aus Jülich gehabt, das ist da wo die RICHTIG DICKEN KISTEN stehen=O und die werden auch mit auf der letzten Rille optimierten Programmen gefüttert, ggf. werde ich mir da mal den letzten Schliff holen. Die frei verfügbaren Unterlagen dort sichern mir schon die Wochenendlektüre fürs ganze Jahr^^

    Wen es interessiert https://www.fz-juelich.de/SharedDocs/Mel…jurecapt16.html


    Forschungszentrum Jülich - JSC - Presentations in courses and talks - Slides of the Seminar on Modular Supercomputing Architectures (MSA)

    uvm....

  • Hi Leute,

    BananaJoe und Lambdax auch eure Lösungsvorschläge liefern das korrekte Ergebnis 👍 . Danke für die weiteren Varianten und an sich für's Mitmachen.

    Da die Aufgabenstellung war "von 1 bis 100" kann meine Version auch nur eine begrenzte Anzahl machen (nur bis 3-stellig, also max. 999)

    Ich finde es gut, dass du die Problemstellung zunächst so angegangen bist, wie die Aufgabe es verlangte 😀 , auch wenn ein Umbau nötig ist um mit den anderen Versionen mitzuhalten - dennoch sehr "Agile", nice 👍 .

    Nun zu dir Andy :

    • Deine Bezeichnugn "Märchenonkel" klingt super, wird dir aber nicht gerecht - du bist mehr 😁 .
    • Im ersten Augenblick war ich sprachlos wie viel Kreativität, Energie und Erfahrung in dir und in den Anderen hier stecken => #wow! #ErfürchtigSchauend 😇 .
    • Nachdem ich deinen Beitrag ein zweites Mal gelesen hatte war ich noch überraschter, denn dein Ansatz/die Umsetzung war absolut verständlich und nachvollziehbar erläutert #HutAb!

    Die Idee mit dem "kleinen" Array habe ich mir bei einem eigenen Script, dem erweiterten Primzahlensieb (auch ein µ-It gewesen) abgeschaut, mit LUT geht vieles viel schneller und trotz meines biblischen Alters und fortschreitendem Gehirnschwund fallen mir doch noch so manche Tricks ein [...]

    Was soll man dazu sagen? Einfach Danke, konnte herzhaft darüber lachen 😂 👍 .

    Jedenfalls hats Spass gemacht! Danke auch dafür an SOLVE-SMART!

    Ich habe das zum Anlass genommen, wieder mehr mit AutoIt zu machen [...]

    Bitteschön, das freut mich ehrlich ganz besonders!

    Viele Grüße

    Sven

  • Hi zusammen....

    so ist das mit den Beweihräucherungen....taugt alles nix^^

    Ich hatte im vorgestellten Script beim "Verschönern" irrtümlich 3 Zeilen gelöscht....was das Script in seiner Lauffähigkeit zwar nicht beeinflusste, aber die Idee dahinter völlig gegen die Wand fuhr!

    Die fehlenden Zeilen waren genau die Schleife, welche die in der Rekursion gefundenen NICHT HAPPY - Zahlen in das Array eintragen.....

    AutoIt
            Else                             ;keine happynumber gefunden
                If $i < $maxarray Then
                    $sieb[$i] = $i           ;dann Zahl als zahl im index ins sieb eintragen
                    For $z = 1 To $maxrecurs ;alle in den rekursionen gefundene zahlen merken
                        $sieb[$temp[$z]] = $temp[$z] ;diese Zahl ist nicht auch nicht happy weil führt in der Folge nicht zu 1
                    Next
                EndIf
            EndIf

    Ich hab das jetzt im Post ergänzt, wenn man das _Arraydisplay() einkommentiert , sieht man den Unterschied zu vorher...shame on me...aber an euch noch mehr weil es keiner gemerkt hat :rofl:

    //EDIT

    Da es sinnvoll ist, erst das Sieb zu füllen und danach erst die Grafik zu erstellen habe ich mich entschlossen, das Script im Post #14 upzugraden.

    Was zur annähernden Verdopplung der Geschwindigkeit geführt hat....

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    2 Mal editiert, zuletzt von Andy (7. Februar 2022 um 12:13)

  • Ich hab das jetzt im Post ergänzt, wenn man das _Arraydisplay() einkommentiert , sieht man den Unterschied zu vorher...shame on me...aber an euch noch mehr weil es keiner gemerkt hat :rofl:

    Wir waren wohl alle noch zu sehr vor Ehrfurcht erstarrt :P .

    86598-musashi-c64-png

    "Am Anfang wurde das Universum erschaffen. Das machte viele Leute sehr wütend und wurde allenthalben als Schritt in die falsche Richtung angesehen."