Annäherung an ein Soll mit 2 Werten - Problem Ausgabe

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
cbesi
User
Beiträge: 41
Registriert: Dienstag 11. August 2020, 22:04

Hallo,

leider komme ich an einem Punkt in meiner Programmierung nicht weiter.

Ziel ist es aus 8 gewogenen Werten zu ermitteln welche Kombination an den Sollwert (hier K) am nächsten ran kommt.

Das habe ich bereits mit itertools und combinations gelöst, aber nun werden nur die Werte ausgegeben welche die nächsten sind.

Ich brauche aber das Ergebnis, welche Kombination von Waagen es ist.

Kann mir vielleicht jemand auf die Sprünge helfen?

Code: Alles auswählen

from itertools import combinations

class waageclass:
    first = 0
    second = 0


    def __init__(self, f, s):
        self.first = f
        self.second = s

    def zeigename(self):
        print("Waagenbezeichnung = " + (self.first))

    def zeigegewicht(self):
        print("Gewicht der Waage = " + str(self.second))







gewicht_waage1 = 10
gewicht_waage2 = 15
gewicht_waage3 = 15
gewicht_waage4 = 20
gewicht_waage5 = 20
gewicht_waage6 = 18
gewicht_waage7 = 25
gewicht_waage8 = 30


waage1 = waageclass('Waage1',gewicht_waage1)
waage2 = waageclass('Waage2',gewicht_waage2)
waage3 = waageclass('Waage3',gewicht_waage3)
waage4 = waageclass('Waage4',gewicht_waage4)
waage5 = waageclass('Waage5',gewicht_waage5)
waage6 = waageclass('Waage6',gewicht_waage6)
waage7 = waageclass('Waage7',gewicht_waage7)
waage8 = waageclass('Waage8',gewicht_waage8)


#waage1.zeigename()



gewichte=(waage1.second,waage2.second,waage3.second,waage4.second,waage5.second,waage6.second,waage7.second,waage8.second)

# printing original list
print("The original list is : " + str(gewichte))

# Initializing K
K = 48

# Closest Sum Pair in List
# using dictionary comprehension + max()
res = {}
for ele in combinations(gewichte, 2):
    ele_sum = sum(ele)
    try:
        res[ele_sum].append(ele)
    except KeyError:
        res[ele_sum] = [ele]
res = res[min(res, key=lambda ele: abs(ele - K))]

# printing result
print("The closest sum pair is : " + str(res))
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

Klassennamen schreibt man mit großem Anfangsbuchstaben. `class` hat im Namen nichts verloren.
Klassenkonstanten werden komplett GROSS geschrieben, aber die Konstanten `first` und `second` werden eh durch Instanzattribute überschrieben, sind also überflüssig.
`first` und `second` sind schlechte Variablenname, `f` und `s` sind noch schlechter, weil Variablennamen aussagekräftig sein müssen. Das gilt auch für `res`, `ele` oder `ele_sum`.
Man setzt keine Strings per + zusammen, sondern benutzt format-Strings.
Man nummeriert keine Variablennamen durch, sondern nutz passende Datenformate, wie Listen.
Wenn Du wissen willst, welche Waage benutzt wird, dann solltest Du sie nicht wegwerfen.
Statt des try-except-Konstrukts benutzt man defaultdict.

Code: Alles auswählen

from itertools import combinations
from collections import defaultdict

GEWICHT = 8

class Waage:
    def __init__(self, bezeichnung, gewicht):
        self.bezeichnung = bezeichnung
        self.gewicht = gewicht

    def zeigename(self):
        print(f"Waagenbezeichnung = {self.bezeichnung}")

    def zeigegewicht(self):
        print(f"Gewicht der Waage = {self.gewicht}")
        
    def __repr__(self):
        return f"<Waage bezeichnung={self.bezeichnung!r}, gewicht={self.gewicht!r}>"


def main():
    waagen = [
        Waage(f"Waage{n}", gewicht)
        for n, gewicht in enumerate([10, 15, 15, 20, 20, 18, 25, 30], 1)
    ]
    results = defaultdict(list)
    for selected_waagen in combinations(waagen, 2):
        gewicht = sum(waage.gewicht for waage in selected_waagen)
        results[gewicht].append(selected_waagen)
    nearest_weight = min(results, key=lambda gewicht: abs(gewicht - GEWICHT))
    result = results[nearest_weight]
    print(result)
        
if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Verdammt, war zu langsam. Bitte die Schnittmenge mit dem Beitrag von Sirius3 einfach ignorieren. 🤡

@cbesi: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassennamen (PascalCase). Und das es eine Klasse ist, gehört nicht in den Namen.

Die Klassenattribute `first` und `second` gehören dort nicht hin. Werden auch nirgends verwendet. Klassenattribute sind in der Regel Konstanten, weil Variablen an der Stelle globaler Zustand ist, den man vermeidet.

Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen. `f` und `s` für `first` und `second` ist alles andere als selbsterklärend. Wobei auch bei `first` und `second` niemand anhand der Namen darauf kommt, dass es sich um einen Namen einer Waage und ein ermitteltes Gewicht handelt. Letztlich wird die Klasse aber gar nicht wirklich verwendet.

Namen nummeriert man nicht. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen und -werte, sondern eine Datenstruktur. Oft, und so auch im Fall von `gewichte`, eine Liste. Und auch wirklich eine Liste und das nicht nur im Kommentar schreiben, dann aber ein Tupel anlegen.

Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptrogramm steht üblicherweise in einer Funktion die `main()` heisst.

Kommentare sollten dem Leser einen Mehrwert über den Code bieten. Faustregel: Ein Kommentar beschreibt nicht was der Code macht, denn das steht dort bereits als Code, sondern warum er das so macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in der Regel was in der Dokumentation von Python oder den verwendeten Bibliotheken steht.

Besonders schlecht sind Kommentare die inhaltlich falsch sind. Dann weiss der Leser nicht ob der Kommentar oder der Code fehlerhaft ist. So ein Kommentar wirft also Fragen auf, statt welche zu klären. Das Gegenteil von dem wofür man Kommentare schreibt.

Wenn im Kommentar was von Liste steht, der Code aber ein Tupel verwendet, oder im Kommentar „dict comprehension“ steht, im Code dazu aber gar keine „comprehension“-Syntax steht, oder im Kommentar `max()` steht, im Code dann aber `min()`, dann ist das sehr schlecht. Schlechter als kein Kommentar.

`print()` kann man mehrere Argumente übergeben. Die werden mit Leerzeichen getrennt ausgegeben und jeder Wert wird von `print()` in eine Zeichenkette umgewandelt. Auch ausserhalb von `print()` ist zusammenstückeln von Zeichenketten und Werten mit `str()` und ``+`` eher BASIC als Python. Python hat dafür die `format()`-Methode auf Zeichenketten und f-Zeichenkettenliterale.

`res` ist kein guter Name, weil abgekürzt und völlig nichtssagend, und es wird im gleichen Namensraum für zwei völlig unterschiedliche Datenstrukturen verwendet.

Anstelle eines Wörterbuchs würde man auch besser ein `collections.defaultdict()` verwenden.

Auch alles mögliche `ele` nennen hat wieder die drei Probleme 1) kryptische Abkürzung, 2) nichtssagender Name, und 3) man muss so verdammt aufpassen wann welcher Wert/Typ an diesen Namen gebunden ist.

Also das Original mal überarbeitet:

Code: Alles auswählen

#!/usr/bin/env python3
from collections import defaultdict
from itertools import combinations


def main():
    gewichte = [10, 15, 15, 20, 20, 18, 25, 30]
    soll_gewicht = 48

    print("The original weights are:", gewichte)

    gesamtgewicht_auf_gewichtspaare = defaultdict(list)
    for gewichtspaar in combinations(gewichte, 2):
        gesamtgewicht_auf_gewichtspaare[sum(gewichtspaar)].append(gewichtspaar)

    gewichtspaare_mit_kleinster_abweichung = gesamtgewicht_auf_gewichtspaare[
        min(
            gesamtgewicht_auf_gewichtspaare.keys(),
            key=lambda gesamtgewicht: abs(gesamtgewicht - soll_gewicht),
        )
    ]

    print("The closest sum pairs are:", gewichtspaare_mit_kleinster_abweichung)


if __name__ == "__main__":
    main()
Wenn man jetzt nicht nur die Gewichte sondern auch die Waagen braucht, müsste man Waage und Gewicht zu einem Objekt zusammenfassen. Da kann eine Klasse oder ein mit `collections.namedtuple()` erstellter Datentyp Sinn machen.

Man muss die einzelnen Schritte dann entsprechend anpassen.

Code: Alles auswählen

#!/usr/bin/env python3
from collections import defaultdict, namedtuple
from itertools import combinations

Waegung = namedtuple("Waegung", "waagenname gewicht")


def main():
    waegungen = [
        Waegung(f"Waage{i}", gewicht)
        for i, gewicht in enumerate([10, 15, 15, 20, 20, 18, 25, 30], 1)
    ]
    soll_gewicht = 48

    print("The original weights are:", waegungen)

    gesamtgewicht_auf_waegungspaare = defaultdict(list)
    for waegungspaar in combinations(waegungen, 2):
        gesamtgewicht = sum(waegung.gewicht for waegung in waegungspaar)
        gesamtgewicht_auf_waegungspaare[gesamtgewicht].append(waegungspaar)

    waegungspaare_mit_kleinster_abweichung = gesamtgewicht_auf_waegungspaare[
        min(
            gesamtgewicht_auf_waegungspaare.keys(),
            key=lambda gesamtgewicht: abs(gesamtgewicht - soll_gewicht),
        )
    ]

    print("The closest sum pairs are:", waegungspaare_mit_kleinster_abweichung)


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
cbesi
User
Beiträge: 41
Registriert: Dienstag 11. August 2020, 22:04

Hallo zusammen,

vielen Dank für Eure Codebeispiele und vor allem die Lange Erklärung. Ich habe die Hinweise verstanden und bin dankbar dafür.

Es waren meine ersten Gehversuche mit Klassen. Die weiteren können ja nun professioneller werden :-) Ich habe mich natürlich eingelesen wie Klassen funktionieren, aber tatsächlich so wie es scheint einiges nicht berücksichtigt.

Die einzelnen Gewichte habe ich verwendet mit dem Hintergrund, das ich Werte von Waagen abgreifen will. Dies möchte ich mit pyserial gestalten... mal sehen was das gibt ;-) Daher habe ich die einzelnen Werte versucht in ein Object zu bringen um eine Verbindung der Werte zu schaffen.

Nun werde ich die einzelnen Gewichte welche ich abfrage erst in eine Liste packen und dann verarbeiten.

Vielen lieben Dank für die Hilfe!!!!
Antworten