Seite 1 von 1
Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 16:36
von tobifire
Hi zusammen! (Bin neu in dem Forum, deshalb die Frage im Allgemeinen gestellt)
Ich bin gerade an einem Projekt, wo ich als Vorgabe eine Dictionary habe die wie folgt aussieht: { "Name": Int Zahl, "Name": Int Zahl, "Name": Int Zahl } (Das Dict kann auch größer oder kleiner sein, jedoch bleibt das Muster)
dann bekomme ich einen weiteren Wert als Int geliefert. Bspw. t = int(200). Meine Aufgabe ist es diese Zahl unter dem Variabel Namen "t" gleichmäßig unter den Zahlen in der Dictionary aufzuteilen.
Kleines Beispiel:
Vorher:
t = 150
{ "Peter": 150, "Alex": 250, "Tobias": 50 }
Ergebnis soll sein:
t = 0
{ "Peter": 175, "Alex": 250, "Tobias": 175 }
Ich hoffe man sieht, anhand des Beispiels, was ich erreichen möchte..
Meine Frage ist wie ich das am Effizientesten lösen könnte.
Bisher ist mir nur eine While Schleife eingefallen t > 0 und in der Schleife sortiert das Programm nach dem kleinsten Wert des Dictionary (sort by value) und füllt ihn mit +=1 auf und beginnt von vorn.
Grüße
Tobias
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 22:25
von sparrow
Dein Beispiel entspricht für mich nicht deiner Erklärung.
Du hast geschrieben "[...] Aufgabe ist es diese [...] gleichmäßig unter den Zahlen aufzuteilen".
Wenn ich etwas gleichmäßig aufteile, dann haben die Teile die gleiche Größe. Also würde jeder Wert um 50 erhöht werden.
Was genau soll denn passieren?
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 22:44
von __blackjack__
@sparrow: Ich hätte das jetzt so interpretiert das immer der/die mit dem kleinsten Wert solange Gummipunkte bekommen bis alles aufgebraucht ist. Also würde erst Tobias solange was bekommen bis er mit Peter gleich auf liegt oder die Gummipunkte alle sind. Und dann bekommen Peter und Tobias solange Gummipunkte bis sie mit Alex gleichauf sind oder die Punkte alle sind. Was im Beispiel passiert wenn beide 175 erreicht haben.
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 23:04
von tobifire
@sparrow
Da gebe ich dir Recht.. mein Fehler. Wie im Beispiel zu sehen müssen die Zahlen nicht "gleichmäßig" aufgefüllt werden, sondern
das geringste Value soll zunächst insoweit aufgefüllt werden, bis es gleich viel hat wie die das zweit kleinste (wenn "t" es überhaupt zulässt).
Heißt also in dem Beispiel: "Tobias": 50 werden 100 aufgefüllt bis es so viele hat wie "Peter": 150, ab dann wird der Wert so lange aufgeteilt, bis das nächst größere Value erreicht wird.
In den Beispiel wird das nächst größere Value nicht erreicht, heißt also die übrigen 50 in t werden hälfte hälfte aufgeteilt: "Peter": 175, "Tobias": 175
Ab dann sollte auch das mit in die Rechnung rein.
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 23:05
von tobifire
@__blackjack__
Ganz genau! Besser hätte ich es nicht erklären können..
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 23:26
von narpfel
@tobifire: Jetzt hast du ja eigentlich schon die (bzw. eine?) Lösung auf deine Frage beschrieben. Ein bisschen effizienter kann man das machen, wenn man eine passende Datenstruktur nutzt, mit der das Finden der jeweils kleinsten Elemente effizienter als linear ist. In der Standardbibliothek gibt es da zum Beispiel das
`heapq`-Modul.
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 23:28
von tobifire
@narpfel Ja ich habe bereits eine Lösung die auch funktioniert.. wie gesagt mit der While Schleife und dem rauspicken des kleinsten Wertes.
Dachte nur eventuell eine Effizientere Lösung vorgeschlagen zu bekommen. Ich schaue mir das mal an!
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 23:31
von narpfel
@tobifire: In deiner Beschreibung
hier kommt doch überhaupt keine `while`-Schleife mehr vor? (Edit: Damit meine ich die Schleife, in der jeweils der kleinste Wert inkrementiert wird.)
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Dienstag 9. Februar 2021, 23:37
von tobifire
@narpfel Das ist auch die Beschreibung explizit für das Beispiel.. Wenn ich das genau so übernehme wie beschrieben kann ich das nur bei Dictionarys verwenden, die 3 Werte haben.
Mein bisheriger Lösungsansatz war wie beschrieben:
In einer While Schleife t > 0, wird nach dem kleinsten Wert des Dictionarys gesucht. Dem Wert wird +=1 hinzugefügt und gespeichert und am Ende der While schleife wird t -=1 subtrahiert. Das wird solange gemacht, bist t = 0 ist.
Das ist meiner Meinung keine elegante Lösung, da ich aber noch nicht alle Möglichkeiten von Python kenne frage ich nach Rat.
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Mittwoch 10. Februar 2021, 06:46
von sparrow
@tobifire: Zeit doch mal deine bisherige Lösung, dann schauen wir mal gemeinsam.
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Mittwoch 10. Februar 2021, 08:06
von tobifire
Ich hoffe mal man kann die Funktion erkennen
Code: Alles auswählen
t = int(150)
daten = { "Peter": 150, "Alex": 250, "Tobias": 50 }
sortedlist = sorted(daten.items(), key = lambda kv: kv[1])
sortdaten = dict(sortedlist)
while 0 < t:
if len(sortdaten) >= 2:
n0 = int(list(sortdaten.values())[0])
n1 = int(list(sortdaten.values())[1])
while n0 < n1:
if 0 < t:
n0 += 1
t -= 1
name = list(sortdaten.keys())[0]
sortdaten[name] = int(n0)
pbar.update(1)
break
if 0 < t:
n0 += 1
t -= 1
name = list(sortdaten.keys())[0]
sortdaten[name] = int(n0)
pbar.update(1)
sortedlist = sorted(sortdaten.items(), key = lambda kv: kv[1])
sortdaten = dict(sortedlist)
else:
n0 = int(list(sortdaten.values())[0])
n0 += t
pbar.update(t)
t = 0
name = list(sortdaten.keys())[0]
sortdaten[name] = int(n0)
sortedlist = sorted(sortdaten.items(), key = lambda kv: kv[1])
sortdaten = dict(sortedlist)
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Mittwoch 10. Februar 2021, 08:15
von Thants
@tobifire: Die nächste offensichtliche Optimierung von deinem Algorithmus wäre jetzt die Punkte nicht einzeln zu verteilen, sondern in Blöcken. Die textuelle Beschreibung von blackjack enthält ja bereits eine Idee, die musst du jetzt in Code umsetzen. Um das nochmal aufzugreifen, du könntest die Leute in dem dict in zwei Gruppen einteilen, einmal alle Teilnehmer, evtl. sogar bereits nach Punkte sortiert, und dann die Punktekandidaten. Zu Beginn wechselt derjenige mit der geringsten Punktezahl von der Teilnehmerliste in die Kandidatenliste (oder auch mehrere, falls es mehrere mit gleicher niedriger Punktezahl gäbe). In deinem Beispiel also Tobias mit seinen 50 Punkten. Die Differenz zum nächsthöheren in der Teilnehmerliste, Peter, ist 100. t ist noch >= 100, also kannst du sofort 100 Punkte an Tobias zuweisen und von t abziehen. Jetzt haben Peter und Tobias gleichviel Punkte, also wechselt Peter auch in die Kandidatenliste, da er ab jetzt auch Punkte erhält. Die Differenz zu Alex ist wieder 100, wir bräuchten also 200 Punkte, um alle Punktekandidaten auf den gleichen Punktestand wie Alex zu bringen. Soviel Punkte haben wir aber nicht mehr, also sind wir am Ende und müssen nur noch die verbliebenen Punkte gleichmäßig auf die aktuellen Punktekandidaten verteilen, jeder bekommt also 50/2 Punkte.
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Mittwoch 10. Februar 2021, 11:25
von Sirius3
@tobifire: einbuchtabige Variablennamen sollte man möglichst vermeiden. Was soll denn `t` bedeuten?
Dass Wörterbücher inzwischen eine feste Reihenfolge haben, sollte man möglichst nicht brauchen. Bei Dir ist das Wörterbuch sortdaten auch gar nicht nötig, weil Du eh viel besser mit der Liste arbeitest.
Dass 150 ein int ist, sollte Dir klar sein, dem Computer ist es jedenfalls, wodurch der int-Aufruf unsinnig ist.
Eine while-Schleife, die gleich beim ersten Durchgang per break verlassen wird, ist eigentlich eine if-Abfrage.
Auch hier wieder n0 ist schon ein int, das Umzuwandeln ist überlfüssig.
Dann hast Du einen else-Block, wo garantiert ist, dass t == 0 ist. So dass der else-Block in sich zusammenfällt:
Code: Alles auswählen
t = 150
daten = { "Peter": 150, "Alex": 250, "Tobias": 50 }
sorted_data = sorted(daten.items(), key = lambda kv: kv[1])
while 0 < t:
if len(sorted_data) >= 2:
n0 = sorted_data[0][1]
n1 = sorted_data[0][1]
if n0 < n1:
if 0 < t:
n0 += 1
t -= 1
sorted_data[0][1] = n0
pbar.update(1)
if 0 < t:
n0 += 1
t -= 1
sorted_data[0][1] = n0
pbar.update(1)
sorted_data = sorted(sorted_data, key = lambda kv: kv[1])
else:
pbar.update(0)
Wenn man nun den Pfad n0 < n1 anschaut und das, was nach diesem if-Block kommt, siehst Du, dass beides Mal das selbe passiert, so dass man das zusammenfassen kann:
Code: Alles auswählen
t = 150
daten = { "Peter": 150, "Alex": 250, "Tobias": 50 }
sorted_data = sorted(daten.items(), key = lambda kv: kv[1])
while 0 < t:
if len(sorted_data) >= 2:
if 0 < t:
n0 = sorted_data[0][1]
n0 += 1
t -= 1
sorted_data[0][1] = n0
pbar.update(1)
sorted_data = sorted(sorted_data, key = lambda kv: kv[1])
else:
pbar.update(0)
Die Abfrage 0 < t wird aber schon durch die while-Schleife garantiert:
Code: Alles auswählen
t = 150
daten = { "Peter": 150, "Alex": 250, "Tobias": 50 }
sorted_data = sorted(daten.items(), key = lambda kv: kv[1])
while 0 < t:
if len(sorted_data) >= 2:
t -= 1
sorted_data[0][1] += 1
pbar.update(1)
sorted_data = sorted(sorted_data, key = lambda kv: kv[1])
pbar.update(0)
Der selbe Code, viel einfacher, und man sieht gleich ein Problem. Was passiert wenn es nur einen Eintrag gibt?
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Mittwoch 10. Februar 2021, 14:08
von tobifire
@Sirius3 Das ist schlichtweg Genial. Durch die Erklärung fallen mir erst die unnötigen Wiederholungen sowie unnötige Zwischenschritte auf!
Jedoch ist es nicht möglich mit sorted_data[0][1] += 1 den Werten eine Zahl zu addieren Fehlercode: can only concatenate tuple (not "int") to tuple
Somit habe ich den Code ein wenig verändert, was dann im Endeffekt für mich funktioniert hat!
Vielen Dank!
Code: Alles auswählen
t = 150
daten = { "Peter": 150, "Alex": 250, "Tobias": 50 }
with tqdm(total=t, desc='Berechne..') as pbar:
sorted_data = dict(sorted(daten.items(), key = lambda kv: kv[1]))
save_daten = dict(sorted_data)
while 0 < t:
sorted_data_values = list(sorted_data.values())
sorted_data_keys = list(sorted_data.keys())
if len(sorted_data) >= 2:
t -= 1
sorted_data_values[0] += 1
pbar.update(1)
sorted_data = dict(zip(sorted_data_keys, sorted_data_values))
sorted_data = dict(sorted(sorted_data.items(), key = lambda kv: kv[1]))
else:
sorted_data_values[0] += t
pbar.update(t)
t = 0
sorted_data = dict(zip(sorted_data_keys, sorted_data_values))
sorted_data = dict(sorted(sorted_data.items(), key = lambda kv: kv[1]))
print(sorted_data)
>>> {'Peter': 175, 'Tobias': 175, 'Alex': 250}
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Mittwoch 10. Februar 2021, 14:43
von sparrow
Ich wusste doch, dass ich so etwas mal unter den Fingern hatte. Ich weiß nur nicht mehr, welche Aufgabe es war, aber es muss etwas mit dem Advent of Code zu tun gehabt haben.
Umgemünzt auf das Problem hier, sieht meine Lösung so aus - ich hab es aber noch nicht ausführlich getestet:
Code: Alles auswählen
def divide_points(points, receiver_count, maximum):
"""Divides points for receiver_counts, but not more than maximum for each
returns a tuple, which first element is a list of the points for the receiver
and the secodn is the remainder.
"""
spreadable = min(points, receiver_count * maximum)
points_receiver = spreadable // receiver_count
if points_receiver > 0:
spreads = [points_receiver] * receiver_count
else:
spreads = [1] * points + [0] * (receiver_count - points)
remainder = points - sum(spreads)
return spreads, remainder
def spread_points_to_players(player_to_points, points_to_give):
if len(player_to_points) == 1:
player_to_points[list(player_to_points.keys())[0]] += points_to_give
return player_to_points
player_to_points = {
k: v for k, v in sorted(player_to_points.items(),
key=lambda item: item[1])
}
receivers = set()
minimum_points = list(player_to_points.values())[0]
for name, points in player_to_points.items():
if points == minimum_points:
receivers.add(name)
else:
points_to_spread = points - minimum_points
spreads, remainder = divide_points(
points_to_give, len(receivers), points_to_spread
)
for receiver, spread in zip(receivers, spreads):
player_to_points[receiver] += spread
receivers.add(name)
minimum_points += spread
points_to_give = remainder
return player_to_points
def main():
t = 150
daten = {"Peter": 150, "Alex": 250, "Tobias": 50}
daten = spread_points_to_players(daten, t)
print(daten)
if __name__ == "__main__":
main()
Re: Zahlen in einer Dict gleichmäßig auffüllen
Verfasst: Donnerstag 11. Februar 2021, 00:41
von LeSchakal
Eine eher plumpe Lösung:
Code: Alles auswählen
def give_to_the_poor(people):
poor = people.index(min(people))
people[poor] += 1
return "Thank You, Sir!"
def main():
people = [150, 250, 50]
coins = 150
while coins:
give_to_the_poor(people)
coins -= 1
print(people)
if __name__ == "__main__":
main()