Zufall in einer Liste mit definierter Wahrscheinlichkeit

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
einsdreiundzwanzig
User
Beiträge: 13
Registriert: Dienstag 24. August 2010, 23:31

Hallo Pythonistas,

ich habe ein kleines Problem mit dem Zufall, und zwar habe ich eine Liste wie diese hier:

Code: Alles auswählen

[["text1", "text2", "text3"], [4, 2, 7]]
Jetzt will ich zufällig einen der drei Texte ziehen, die Wahrscheinlichkeit soll allerdings dabei von den Zahlen im zweiten Teil der Liste abhängen, diese Werte gehen dabei von 0=nie bis 9=immer
(Die Zuordnung ist klar? "text1": 4, "text2": 2, "text3": 7)

Die Extremfälle 0 und 9 werden vorher schon abgefangen, also geht's bei meiner Frage eigentlich nur noch um Werte von 1 bis 8...

Gibt's da irgendeine raffinierte Lösung?

Gruss,
123
Zuletzt geändert von einsdreiundzwanzig am Mittwoch 25. August 2010, 01:22, insgesamt 2-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hallo einsdreiundzwanzig, willkommen im Forum,

Ich würde mit ``(i)zip()`` und ``dict()`` ein Dictionary basteln und dann eine Liste mit den jeweiligen Elementen zusammensetzen und darauf ein ``random.choice()`` machen. Wenn bei dem Wert eine 9 ist, dann ist nur dieses Element in der Liste (= wird immer gezogen), wenn der Wert 0 ist, dann nicht in der Liste (= wird nie gezogen). Alle anderen Werte musst du dann anteilsmäßig in diese Liste stecken. Vielleicht gibt es eine elegantere Lösung, aber die fällt mir auch grad nicht ein.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
ntrunk
User
Beiträge: 83
Registriert: Sonntag 7. September 2008, 23:09
Wohnort: Buchen (Odenwald)

Hi,
kann es sein, dass deine Aufgabenstellung etwas unscharf ist? Deine Daten besagen, dass der 1. Wert mit ca. 44%, der 2. mit 22% und der dritte mit 77% Wahrscheinlichkeit gezogen werden sollen. Wenn 9 "immer", also 100% bedeutet, dann dürften m.E. alle Werte zusammen auch nicht größer als 9 sein.
Wenn du allerdings mit deinen Zahlen eine relative Wahrscheinlichkeit meinst: ich würde die Summe bilden, danach eine Zufallszahl im Bereich der Summe ermitteln und diese dann den Texten anhand der Wertbereiche zuordnen.
In deinem Beispiel ist die Summe 13. Der erste Text wird dann bei einer Zufallszahl von 1-4 gezogen, der 2. bei 5-6, der dritte bei 7-13.

Gruß
Norbert
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

ntrunk hat geschrieben:Wenn du allerdings mit deinen Zahlen eine relative Wahrscheinlichkeit meinst: ich würde die Summe bilden, danach eine Zufallszahl im Bereich der Summe ermitteln und diese dann den Texten anhand der Wertbereiche zuordnen.
In deinem Beispiel ist die Summe 13. Der erste Text wird dann bei einer Zufallszahl von 1-4 gezogen, der 2. bei 5-6, der dritte bei 7-13.
Spontan fiel mir dazu so etwas ein:

Code: Alles auswählen

from bisect import bisect
                
values = [4, 2, 7]
last = 0
breakpoints = []
for value in values:
    breakpoints.append(last + value)
    last += value                

for random_value in xrange(last):
    pos = bisect(breakpoints, random_value)
    print(random_value, pos)
pos enthält die Position des Wertes in der Liste.
einsdreiundzwanzig
User
Beiträge: 13
Registriert: Dienstag 24. August 2010, 23:31

Moin,

Hmja, _etwas_ unscharf kam mir mein Problem schon auch vor... Das war ja quasi grad mein Problem. :) Aber das mit der Summe aller Werte ist ein guter Tip, bin ich nicht drauf gekommen und werd damit mal ein bisschen rumprobieren.

Zwei Sachen sollte ich vielleicht noch ergänzen: 1.)Die Werte für die Wahrscheinlichkeiten können durchaus auch mehrfach vorkommen, und 2.) ich hatte mich gestern abend vertan mit einem anderen Teil meiner Liste - 0 und 9 bedeuten nicht nie und immer sondern sehr selten und sehr oft, also ist doch kein diesbezügliches Spezialproblem zu berücksichtigen.

Hab Deine Idee jetzt so umgesetzt:

Code: Alles auswählen

from random import randint

txtListe = [["text1", "text2", "text3", "text4", "text5", "text6", "text7", "text8", "text9", "text10"],
            [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]]

summeWahrscheinlichkeiten = sum(txtListe[1]) + len(txtListe[1])
t = [0,0,0,0,0,0,0,0,0,0]

for k in range(1,10000):
    a = 1
    gewinnTxt = ""
    gewinnLos = randint(0, summeWahrscheinlichkeiten)
    for i,j in zip(txtListe[0], txtListe[1]):
        if a <= gewinnLos <= a+j+1:
            gewinnTxt = i
            t[txtListe[0].index(gewinnTxt)] += 1
        a += j + 1
        
for a,b,c in zip(t, txtListe[0], txtListe[1]):
    print b + " (" + str(c) + ") - " + str(a/100) + "%" 

Klappt ganz gut, hätte nur noch zwei Fragen dazu:
1.) Es scheint einen Unterschied zu machen, ob die Liste mit den Wahrscheinlichkeiten aufsteigend oder absteigend angelegt ist - wenn sie absteigend ist so wie jetzt oben im code bekomme ich eine Verteilung von 1% bis 20%. Sind sie aufsteigend, also von 0..9, geht die Verteilung von 3% bis 18%, und komischerweise bekomme ich dann meistens für 8 und 9 gleichermassen 18% raus. Woran kann das liegen?
2.) Ist in meinem code irgendwas generell totaler Unsinn / sehr umständlich / viel zu kompliziert / etc.?

Oh, /me, das ist ja nochmal ganz was anderes... werd ich mir mal ansehen! :)

Danke für eure Hilfe,
LG,
123

EDIT: /me, Dein Vorschlag ist schon grundsätzlich auch das, was ntrunk vorgeschlagen hat und ich jetzt probiert habe, oder? Wäre Deine Variante schneller?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Hier eine Lösung mit relativer Wahrscheinlichkeit.

Code: Alles auswählen

import random
x = [["text1", "text2", "text3"], [4, 2, 7]]
y = sum(([t] * w for t, w in zip(*x)), [])
for i in range(10):
    print(random.choice(y))
Stefan
einsdreiundzwanzig
User
Beiträge: 13
Registriert: Dienstag 24. August 2010, 23:31

Hallo Stefan,

Das ist nicht nur kürzer und schicker sondern funktioniert auch perfekt... Hat auch nicht mehr dieses komische Problem mit dem Unterschied zwischen auf- und absteigender Sortierung der Werte... Besten Dank!

(Auch wenn ich nicht so 100% kapiere, _wie_ das eigentlich funktioniert, aber da komm ich schon auch noch dahinter... :))

LG,
Jo
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Es entspricht eigentlicht genau meinem Vorschlag. In ``x`` ist deine Eingabe, in ``y`` wird eine Liste konstruiert die n-mal das entsprechende Element enthält. Danach wird zufällig ein Element aus dieser Liste gezogen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
einsdreiundzwanzig
User
Beiträge: 13
Registriert: Dienstag 24. August 2010, 23:31

Achso? :) Ich bin leider noch nicht so fit dass ich mir das aus Deinem Vorschlag selbst hätte zusammenbasteln können, deswegen war ich sehr froh über den Codeschnipsel dazu. Aber mit beidem zusammen, code und Erklärung, kann ich jetzt nachvollziehen wie das funktioniert.

Danke! :)
Michi_J
User
Beiträge: 110
Registriert: Samstag 7. August 2010, 08:35

Zu diesem Thema hab ich auch eine Frage:
Kann ich den Befehl random.choice auch auf eine verschachtelte Liste anwenden, sodass er mir ein beliebiges Element ausgibt?
Danke für eure Hilfe!
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Michi_J hat geschrieben:Kann ich den Befehl random.choice auch auf eine verschachtelte Liste anwenden, sodass er mir ein beliebiges Element ausgibt?
random.choice liefert dir ein Element aus einer Liste. Dieses Element kann auch durchaus selber wieder eine Liste sein.
Michi_J
User
Beiträge: 110
Registriert: Samstag 7. August 2010, 08:35

Hmmm, ich krieg aber folgende Fehlermeldung:

Traceback (most recent call last):
File "C:\Temp\owner_neighbour7.py", line 155, in <module>
n=random.choice(neighbour)
File "C:\Python25\lib\random.py", line 248, in choice
return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty
IndexError: list index out of range

Ok, list index out of range, aber woran liegt das?
Michi_J
User
Beiträge: 110
Registriert: Samstag 7. August 2010, 08:35

Gut, habs jetzt selbst rausgefunden:

ich hatte falsch eingerückt! Danke. Funktioniert sogar, super!
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Michi_J hat geschrieben:Traceback (most recent call last):
File "C:\Temp\owner_neighbour7.py", line 155, in <module>
n=random.choice(neighbour)
File "C:\Python25\lib\random.py", line 248, in choice
return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty
IndexError: list index out of range

Ok, list index out of range, aber woran liegt das?

Code: Alles auswählen

>>> import random
>>> data = [[1, 2], [3, 4], [5, 6]]
>>> print random.choice(data)
[3, 4]
Von welchem Typ ist denn die Sequenz "neighbour"?
Michi_J
User
Beiträge: 110
Registriert: Samstag 7. August 2010, 08:35

Das folgende Abgebildete ist EIN Element meiner Liste, wie gesagt: sehr verschachtelt.
  • ...
    (1, [[0, [0, [-45625.5, 249824.5, 424.90600000000001]]], [0, [1, [-46505.5, 249454.5, 423.92090000000002]]], [16, [32, [-46505.5, 249454.5, 423.92090000000002]]], [16, [33, [-47255.5, 254754.5, 408.16120000000001]]], [19, [38, [-45625.5, 249824.5, 424.90600000000001]]], [19, [39, [-47255.5, 254754.5, 408.16120000000001]]]])
    ...
hat aber bei mir jetzt auch mit random.choice(neighbour) geklappt, so wie von dir vorgeschlagen.
Danke!
BlackJack

@Michi_J: Dieses Element ist IMHO ein sehr klares Indiz dafür, dass Du langsam mal auf objektorientierte Programmierung umsteigen solltest. Da steigt doch kein Mensch mehr durch was welche "Stelle" in dieser Datenstruktur bedeutet. Selbst Du wirst den Quelltext dazu wahrscheinlich nach einem Jahr nur noch sehr mühsam nachvollziehen können.

Warum Dein `IndexError` kam steht in dem Traceback doch übrigens sehr deutlich in dem Kommentar zum Quelltext von `random.choice()`.
Michi_J
User
Beiträge: 110
Registriert: Samstag 7. August 2010, 08:35

Das stimmt allerdings, sehr verschachtelt das ganze :-)
Die Fehlermeldung habe ich auch verstanden. Danke nochmals für den Hinweis!
Antworten