Water Bricks Programm

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Unxendlicher
User
Beiträge: 15
Registriert: Freitag 28. Januar 2022, 20:18

Hallo ihr,

Ich verzweifle mal wieder an einer Aufgabe oder Projekt wie mans nimmt.
Auf jeden Fall möchte ich ein Programm schreiben welches bei Eingabe einer Liste zb [2, 0, 2] Die Zahl der Freien Blöcke innendrin ausgibt, also die man mit "Wasser füllen kann"
bei [3, 1, 3] dann auch 2, bei [4, 0, 1, 4, 2, 4] wären es 9 usw.

Auf jeden Fall habe ich jetzt nach 3 Tagen und ohne Spaß 5-6 Stunden Arbeit dieses Programm geschrieben, welches bei den einfacheren Beispielen auch funktioniert dennoch war die Lesbarkeit des Programms und generell die Länge sehr schlecht und ich hab es neu probiert. Doch das neue Programm ist zwar viel kompakter funktioniert aber auch nicht wirklich, und ich krieg es einfach nicht hin, wenn jemand mir dabei helfen könnte und auch alles erklären könnte wäre das sehr nett, hier sind die 2 Programme:

Und bedenkt bitte das ich erste seit nicht allzu langer Zeit und nur in meiner Freizeit programmiere also bitte nicht zu kompliziert machen, danke.

Code: Alles auswählen

class BricksAndWaterPython:
    def how_much_water(bricks_array: list) -> int:
        def calculating(bricks_array: list, element: int):
            count = bricks_array.count(bricks_array[element])
            if (bricks_array[element] > bricks_array[0]) and count >= 2:     #größerer neuer wert
                if element + 2 == length:
                    res = 0
                    return res
                try:
                    if bricks_array[element] > bricks_array[element + 1]:
                        res = bricks_array[element] - bricks_array[element + 1]
                        print(f"res: {res}")
                        print("-------------------------------")
                        return res
                    else:
                        res = 0
                        print(f"res: {res}")
                        print("-------------------------------")
                        return res
                except:
                    res = 0
                    print(f"res: {res}")
                    print("-------------------------------")
                    return res
            else:
                print("test")
                try:
                    if bricks_array[0] >= bricks_array[element + 1]:
                        res = bricks_array[0] - bricks_array[element + 1]
                        print(f"res: {res}")
                        print("-------------------------------")
                        return res
                    else:
                        res = 0
                        print(f"res: {res}")
                        print("-------------------------------")
                        return res
                except:
                    res = 0
                    print(f"res: {res}")
                    print("-------------------------------")
                    return res
        print(f"Test 1: {bricks_array}")
        #prüfen ob es freie wasserfläche gibt
        bricks_array_addition = 0
        length = len(bricks_array)
        #liste sortieren und höchsten wert vergleichen
        sorted_list = sorted(bricks_array)
        highest_val = sorted_list[-1]
        for nr in bricks_array:                 #alle elemente der liste zusammenrechnen
            bricks_array_addition = bricks_array_addition + nr          #////
        if highest_val * len(bricks_array) != bricks_array_addition:
            end_res = 0
            for element in range(0, length):
                print(f"element: {element}")
                res = calculating(bricks_array, element)
                end_res = res + end_res
            return end_res
        else:
            return
            #ganz falsch hier

print(BricksAndWaterPython.how_much_water([2, 0, 3, 0, 2]))
Und hier das neue "bessere"

Code: Alles auswählen

class BricksAndWaterPython:
    def how_much_water(bricks_array: list) -> int:
        water = 0
        length = len(bricks_array)

        for i in range(1, length - 1):
            print(f"i: {i}")
            left_hight = bricks_array[i - 1]
            right_hight = bricks_array[i + 1]
            res = min(left_hight, right_hight) - bricks_array[i]
            water = res + water

        return water
        
bricks_array1 = [1, 0, 1]
bricks_array2 = [2, 0, 2]
bricks_array3 = [3, 1, 3]
bricks_array4 = [1, 1, 1]
bricks_array5 = [4, 0, 1, 4, 2, 4]
bricks_array6 = [2, 0, 3, 0, 4]
print(BricksAndWaterPython.how_much_water(bricks_array1))
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Unxendlicher hat geschrieben: Sonntag 20. Februar 2022, 13:16 Und bedenkt bitte das ich erste seit nicht allzu langer Zeit und nur in meiner Freizeit programmiere also bitte nicht zu kompliziert machen, danke.
Eigentlich machst du es dir selbst zu kompliziert ;) Du braucht keine Klassen. Und über die Funktion kann man auch diskutieren, denn die eigentliche Berechnung kann doch in einer einzigen Zeile stattfinden (mit Hilfe der Built-ins 'sum', 'len' und 'max'.). Datentypen sollte man nicht in die Bezeichner schreiben, zumal das nicht mal Arrays sind (die gibt es in Python auch in der Standardbibliothek, machen aber was ganz anderes). Und durchnummerierte Listen sind oft besser als verschachtelte Listen ausdrückbar.
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das sind zwei Zeilen: Bestmimmung der hoechsten Saeule. Und Summe der Reste. Eine Klasse ist unnoetig.

Code: Alles auswählen

def how_much_water(bricks):
    largest = max(bricks)
    return sum(largest - v for v in bricks)

bricks = [4, 0, 1, 4, 2, 4] 
print(how_much_water(bricks))
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

__deets__ hat geschrieben: Sonntag 20. Februar 2022, 14:28 Das sind zwei Zeilen:
Alternative Variante:

Code: Alles auswählen

result = max(bricks) * len(bricks) - sum(bricks)
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

@nezzcarth: das ist eine nette Vorgehensweise!
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Python ist nicht Java, man muß nicht alles in sinnlose Klassen stecken.
Das was Du `array` nennst, sind Listen, also ist der Variablename falsch. Warum also nicht einfach nur `bricks`?
Man definiert nicht Funktionen in anderen Funktionen (außer in ganz seltenen Fällen).
Man benutzt keine nakten Excepts, sondern gibt immer die Exceptions an, die man auch erwartet. Was erwartest Du in Deinen try-Blöcken für Fehler?
Du sortiest eine ganze Liste, nur um das Maximum zu finden? Dafür gibt es die `max`-Funktion.
`bricks_array_addition` ist ein komischer Name. Ist das nicht nur die Gesamtzahl der Bricks, also sum(bricks)?
Deine Funktion liefert mal None und mal eine Zahl zurück, das sollte nicht sein.

Jetzt zur `calculating`-Funktion. Der Name ist schlecht, weil er keine Tätigkeit beschreibt und zu generisch ist. `calculate_water_amount_for_one_element` wäre besser.
Kommen wir zurück zu den Exceptions: eine Exception, die Auftreten kann ist ein IndexError, weil element + 1 größer ist, als die Anzahl der Elemente in `bricks`. Diesen Fall kann man aber schon am Anfang abfragen.
Du hast 6mal den selben Code zur Debug-Ausgabe drin, das sollte einmal am Ende der Funktion stehen.
Das `if bricks[element] > bricks[element + 1]`-Konstrukt läßt sich einfacher durch die max-Funktion ausdrücken.
Wir kommen damit also zu ungefähr dem hier:

Code: Alles auswählen

def calculate_water_amount_for_one_element(bricks, element):
    print(f"element: {element}")
    if element + 1 >= len(bricks):
        return 0
    count = bricks.count(bricks[element])
    if bricks[element] > bricks[0] and count >= 2:
        # größerer neuer wert
        if element + 2 == len(bricks):
            return 0
        res = max(bricks[element] - bricks[element + 1], 0)
    else:
        print("test")
        res = max(bricks[0] - bricks[element + 1], 0)
    print(f"res: {res}")
    print("-------------------------------")
    return res

def how_much_water(bricks):
    print(f"Test 1: {bricks}")
    #prüfen ob es freie wasserfläche gibt
    total_bricks = sum(bricks)
    if total_bricks == max(bricks) * len(bricks):
        return 0
    return sum(
        calculate_water_amount_for_one_element(bricks, element)
        for element in range(len(bricks))
    )

print(how_much_water([2, 0, 3, 0, 2]))
Den Fall `element + 1 >= len(bricks)` kann man verhindern, in dem man in ´how_much_water` die for-Schleife nur bis len(bricks)-1 laufen läßt.
Den Sonderfall, dass alle Bricks gleich hoch sind, ist unnötig, weil das kein Sonderfall ist.

Code: Alles auswählen

def calculate_water_amount_for_one_element(bricks, element):
    print(f"element: {element}")
    count = bricks.count(bricks[element])
    if bricks[element] > bricks[0] and count >= 2:
        # größerer neuer wert
        if element + 2 == len(bricks):
            return 0
        res = max(bricks[element] - bricks[element + 1], 0)
    else:
        print("test")
        res = max(bricks[0] - bricks[element + 1], 0)
    print(f"res: {res}")
    print("-------------------------------")
    return res


def how_much_water(bricks):
    print(f"Test 1: {bricks}")
    return sum(
        calculate_water_amount_for_one_element(bricks, element)
        for element in range(len(bricks) - 1)
    )
Das ist identisch zu Deinem ersten Code, aber trotzdem fehlerhaft, wenn Du mal [0, 0, 3, 0, 2] testest.

Eigentlich mußt Du nur für jedes Element prüfen, was der höchste Turm links und recht sind, und dann davon das Minimum zu nehmen.
Da das ja Deine Aufgabe ist, liefere ich dazu aber keine Musterlösung.
Unxendlicher
User
Beiträge: 15
Registriert: Freitag 28. Januar 2022, 20:18

__deets__ hat geschrieben: Sonntag 20. Februar 2022, 14:28 Das sind zwei Zeilen: Bestmimmung der hoechsten Saeule. Und Summe der Reste. Eine Klasse ist unnoetig.

Code: Alles auswählen

def how_much_water(bricks):
    largest = max(bricks)
    return sum(largest - v for v in bricks)

bricks = [4, 0, 1, 4, 2, 4] 
print(how_much_water(bricks))
naja nicht ganz bei dem beispiel zb [2, 0, 3, 0, 4] gibt das programm 11 aus was aber eindeutig falsch ist

Und ich möchte immer mit Funktionen und Klassen arbeiten weil ich dadurch besser lerne mit diesen umzugehen, in der mathe kann ich auch alles einfach in den taschenrechner eintippen, macht man trotzdem nicht.
Unxendlicher
User
Beiträge: 15
Registriert: Freitag 28. Januar 2022, 20:18

Sirius3 hat geschrieben: Sonntag 20. Februar 2022, 14:55 Python ist nicht Java, man muß nicht alles in sinnlose Klassen stecken.
Das was Du `array` nennst, sind Listen, also ist der Variablename falsch. Warum also nicht einfach nur `bricks`?
Man definiert nicht Funktionen in anderen Funktionen (außer in ganz seltenen Fällen).
Man benutzt keine nakten Excepts, sondern gibt immer die Exceptions an, die man auch erwartet. Was erwartest Du in Deinen try-Blöcken für Fehler?
Du sortiest eine ganze Liste, nur um das Maximum zu finden? Dafür gibt es die `max`-Funktion.
`bricks_array_addition` ist ein komischer Name. Ist das nicht nur die Gesamtzahl der Bricks, also sum(bricks)?
Deine Funktion liefert mal None und mal eine Zahl zurück, das sollte nicht sein.

Jetzt zur `calculating`-Funktion. Der Name ist schlecht, weil er keine Tätigkeit beschreibt und zu generisch ist. `calculate_water_amount_for_one_element` wäre besser.
Kommen wir zurück zu den Exceptions: eine Exception, die Auftreten kann ist ein IndexError, weil element + 1 größer ist, als die Anzahl der Elemente in `bricks`. Diesen Fall kann man aber schon am Anfang abfragen.
Du hast 6mal den selben Code zur Debug-Ausgabe drin, das sollte einmal am Ende der Funktion stehen.
Das `if bricks[element] > bricks[element + 1]`-Konstrukt läßt sich einfacher durch die max-Funktion ausdrücken.
Wir kommen damit also zu ungefähr dem hier:

Code: Alles auswählen

def calculate_water_amount_for_one_element(bricks, element):
    print(f"element: {element}")
    if element + 1 >= len(bricks):
        return 0
    count = bricks.count(bricks[element])
    if bricks[element] > bricks[0] and count >= 2:
        # größerer neuer wert
        if element + 2 == len(bricks):
            return 0
        res = max(bricks[element] - bricks[element + 1], 0)
    else:
        print("test")
        res = max(bricks[0] - bricks[element + 1], 0)
    print(f"res: {res}")
    print("-------------------------------")
    return res

def how_much_water(bricks):
    print(f"Test 1: {bricks}")
    #prüfen ob es freie wasserfläche gibt
    total_bricks = sum(bricks)
    if total_bricks == max(bricks) * len(bricks):
        return 0
    return sum(
        calculate_water_amount_for_one_element(bricks, element)
        for element in range(len(bricks))
    )

print(how_much_water([2, 0, 3, 0, 2]))
Den Fall `element + 1 >= len(bricks)` kann man verhindern, in dem man in ´how_much_water` die for-Schleife nur bis len(bricks)-1 laufen läßt.
Den Sonderfall, dass alle Bricks gleich hoch sind, ist unnötig, weil das kein Sonderfall ist.

Code: Alles auswählen

def calculate_water_amount_for_one_element(bricks, element):
    print(f"element: {element}")
    count = bricks.count(bricks[element])
    if bricks[element] > bricks[0] and count >= 2:
        # größerer neuer wert
        if element + 2 == len(bricks):
            return 0
        res = max(bricks[element] - bricks[element + 1], 0)
    else:
        print("test")
        res = max(bricks[0] - bricks[element + 1], 0)
    print(f"res: {res}")
    print("-------------------------------")
    return res


def how_much_water(bricks):
    print(f"Test 1: {bricks}")
    return sum(
        calculate_water_amount_for_one_element(bricks, element)
        for element in range(len(bricks) - 1)
    )
Das ist identisch zu Deinem ersten Code, aber trotzdem fehlerhaft, wenn Du mal [0, 0, 3, 0, 2] testest.

Eigentlich mußt Du nur für jedes Element prüfen, was der höchste Turm links und recht sind, und dann davon das Minimum zu nehmen.
Da das ja Deine Aufgabe ist, liefere ich dazu aber keine Musterlösung.
Naja bei [2, 0, 3, 0, 4] kommt fehlerhafterweise ja 4 raus obwohl es 5 sein müssten, bis dahin hat es mein Programm ja auch geschafft. Bei den Funktionen und Variablen Namen bin ich immer einfallslos deswegen sind die so.
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Unxendlicher hat geschrieben: Sonntag 20. Februar 2022, 15:06 Naja bei [2, 0, 3, 0, 4] kommt fehlerhafterweise ja 4 raus obwohl es 5 sein müssten, bis dahin hat es mein Programm ja auch geschafft. Bei den Funktionen und Variablen Namen bin ich immer einfallslos deswegen sind die so.
Aus der Problemstellung, wie du sie im Eingangspost beschrieben und gezeigt hast, ergibt sich m. M. n. der Wert 11. Wenn das falsch ist, dann ist die Aufgabenstellung entweder anders, oder es gibt Nebenbedingungen, die du bisher nicht benannt hast.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@nezzcarth: man baut Mäucherchen und füllt die Zwischenräume mit Wasser. An Position 1 kann das Wasser 2m hoch stehen, an Position 3 dann 3m, bevor es überläuft, also insgesamt 5m³.

Code: Alles auswählen

    #
  #~#
#~#~#
#~#~#
@Unxendlicher: das Argument mit den Klassen ist nicht sehr überzeugend, weil Du mußt auch lernen, wann Klassen nicht sinnvoll sind.
Bevor Du hier nach Verbesserungen fragst, solltest Du eine funktionierende Lösung haben, wie die aussehen könnte, habe ich Dir ja beschrieben.
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Unxendlicher hat geschrieben: Sonntag 20. Februar 2022, 14:58 naja nicht ganz bei dem beispiel zb [2, 0, 3, 0, 4] gibt das programm 11 aus was aber eindeutig falsch ist

Und ich möchte immer mit Funktionen und Klassen arbeiten weil ich dadurch besser lerne mit diesen umzugehen, in der mathe kann ich auch alles einfach in den taschenrechner eintippen, macht man trotzdem nicht.
Du hast nicht wirklich viel zur Erklärung gesagt. Darum haben ich und nezzcarth diese Implementierung gewählt. Dann musst du wohl etwas weiter ausholen, was da passieren soll.

Und das Argument, mit Klassen arbeiten zu wollen, wenn sie zur Lösung nichts beitragen, ist Quatsch. Dann übst du nicht , mit denen umzugehen. Sondern falsch mit denen umzugehen.
Unxendlicher
User
Beiträge: 15
Registriert: Freitag 28. Januar 2022, 20:18

Sirius3 hat geschrieben: Sonntag 20. Februar 2022, 15:14 @nezzcarth: man baut Mäucherchen und füllt die Zwischenräume mit Wasser. An Position 1 kann das Wasser 2m hoch stehen, an Position 3 dann 3m, bevor es überläuft, also insgesamt 5m³.

Code: Alles auswählen

    #
  #~#
#~#~#
#~#~#
@Unxendlicher: das Argument mit den Klassen ist nicht sehr überzeugend, weil Du mußt auch lernen, wann Klassen nicht sinnvoll sind.
Bevor Du hier nach Verbesserungen fragst, solltest Du eine funktionierende Lösung haben, wie die aussehen könnte, habe ich Dir ja beschrieben.
In dem Fall wusste ich das eine Klasse nicht sinnvoll ist trotzdem hab ich sie eingebaut.
Ich hab nach einer Lösung gefragt also ob jemand eine Lösung dafür hat weil mein geschriebenes Programm nicht funktioniert und ich es auch nicht schaffe ein fehlerfreies zu schreiben.
Das ich gleichzeitig viel an den Variablen Namen noch ändern sollte hatte ich ja auch erwähnt, weswegen ich ja auch ein neues Programm gestartet hatte.
Und dein Code klappt mit [2, 0, 3, 0, 4] auch nicht, genauso wie meins.
Unxendlicher
User
Beiträge: 15
Registriert: Freitag 28. Januar 2022, 20:18

Dies hier wäre eine komplett funktionierende Lösung, welche ich gerade gefunden habe, für mein Problem nur verstehe ich daran so gut wie garnichts...

Code: Alles auswählen

class BricksAndWaterPython:

    def how_much_water(bricks_array: list) -> int:
        water = 0
        laenge = len(bricks_array)

        for i in range(1, laenge - 1):
            links = bricks_array[i]
            for z in range(i):
                links = max(links, bricks_array[z])

            rechts = bricks_array[i]

            for z in range(i + 1, laenge):

                rechts = max(rechts, bricks_array[z])

            water = water + (min(links, rechts) - bricks_array[i])

        return water

print("Water: ", BricksAndWaterPython.how_much_water([5, 0, 2, 5, 6, 5] ))
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Die Sache ist nur, dass das beim letzte Beispiel nicht mehr so einfach ist.

bricks = [2, 0, 3, 0, 4]

Da kann man ja nicht einfach bis zum Maximum auffüllen.

@Unxendlicher, wenn ich deine Version mal minimal anpasse, komme ich auf das:

Code: Alles auswählen

def how_much_water(bricks_array: list) -> int:
    water = 0
    length = len(bricks_array)

    for i in range(1, length - 1):
        print(f"i: {i}")
        left_height = bricks_array[:i]
        right_height = bricks_array[i + 1:]
        lower_height = min(max(left_height), max(right_height)) - bricks_array[i]
        water += max(lower_height, 0)

    return water
        
bricks_array1 = [1, 0, 1]
bricks_array2 = [2, 0, 2]
bricks_array3 = [3, 1, 3]
bricks_array4 = [1, 1, 1]
bricks_array5 = [4, 0, 1, 4, 2, 4]
bricks_array6 = [2, 0, 3, 0, 4]
print(how_much_water(bricks_array6))
Damit sollte das richtige raus kommen.
Man kann es aber bestimmt noch weiter verbessern...

Das Problem bei dir lag darin, dass du nur auf die jeweils linke und rechte Säule geschaut hattest. Du must aber schauen, was das Maximum des gesamten linken, bzw, des gesamten rechten Bereichs ist.
Dann muss man noch aufpassen, dass in bestimmten Fällen, wenn eine negative Differenz herauskommt, man nicht wieder Wasser abzieht.
Ansonsten warst du nah dran.
Unxendlicher
User
Beiträge: 15
Registriert: Freitag 28. Januar 2022, 20:18

rogerb hat geschrieben: Sonntag 20. Februar 2022, 15:27 Die Sache ist nur, dass das beim letzte Beispiel nicht mehr so einfach ist.

bricks = [2, 0, 3, 0, 4]

Da kann man ja nicht einfach bis zum Maximum auffüllen.

@Unxendlicher, wenn ich deine Version mal minimal anpasse, komme ich auf das:

Code: Alles auswählen

def how_much_water(bricks_array: list) -> int:
    water = 0
    length = len(bricks_array)

    for i in range(1, length - 1):
        print(f"i: {i}")
        left_height = bricks_array[:i]
        right_height = bricks_array[i + 1:]
        lower_height = min(max(left_height), max(right_height)) - bricks_array[i]
        water += max(lower_height, 0)

    return water
        
bricks_array1 = [1, 0, 1]
bricks_array2 = [2, 0, 2]
bricks_array3 = [3, 1, 3]
bricks_array4 = [1, 1, 1]
bricks_array5 = [4, 0, 1, 4, 2, 4]
bricks_array6 = [2, 0, 3, 0, 4]
print(how_much_water(bricks_array6))
Damit sollte das richtige raus kommen.
Man kann es aber bestimmt noch weiter verbessern...

Das Problem bei dir lag darin, dass du nur auf die jeweils linke und rechte Säule geschaut hattest. Du must aber schauen, was das Maximum des gesamten linken, bzw, des gesamten rechten Bereichs ist.
Dann muss man noch aufpassen, dass in bestimmten Fällen, wenn eine negative Differenz herauskommt, man nicht wieder Wasser abzieht.
Ansonsten warst du nah dran.
Genau das ist die Lösung, funktioniert alles, und die Erklärung hab ich auch verstanden vielen Dank.
Du hast mir sämtliche Stunden an ausprobieren etc erspart, weil ich den : nicht kannte, zumindest nicht wie man ihn einsetzt und ich wäre auch nicht drauf gekommen :)
Antworten