Eindimensionaler Random-Walk

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.
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

Ich habe mich wieder mit einen Mitstudenten an eine weitere Aufgabe getraut:

Sie stehen auf einer nach beiden Seiten
sehr langen Strasse und wissen nicht so recht, wohin. Das sind sicherlich die Nachwirkungen der
letzten durchgemachten Nacht. . .Sie werfen für jeden Schritt, den Sie tuen, ein Münze. Zeigt die
Münze ‚Zahl‘, so gehen Sie einen Schritt nach links. Zeigt die Münze ‚Kopf‘, so gehen Sie einen
Schritt nach rechts.
Wie weit kommen Sie im Mittel nach N Schritten? (Gemeint ist der erzielte Abstand von der Startposition.)
Schreiben Sie dazu eine Computersimulation und spielen Sie für verschiedene Schrittzahlen
N jeweils viele Random-Walks durch.
Überlegen Sie sich eine grafische Darstellung.

Code: Alles auswählen

import random

import pylab

def random_steps(n):

    """simuliert n Schritte. Zufall entscheidet bei jedem Schritt ob er
    zum Startpunkt hin oder vom Startpunkt weg gemacht wird.
    Gibt den Abstand vom Startpunkt zurueck."""

    steps = 0

    location = 0

    while steps < n:

        location += random.choice([-1,+1])

        steps += 1

    return location


def avg_distance(steps_to_take, repeats):

    
    """"Ruft random_steps repeats-mal auf, und laesst es jeweils
    steps_to_take-Schritte simulieren.
    Funktion gibt dann den Mittelwert der Antworten zurueck"""

    location_sum = 0

    for i in xrange(repeats):

        location_sum += random_steps(steps_to_take)

    avg_location = location_sum / (repeats * 1.)

    return avg_location
Diesmal stehen die Importe oben und die Variablen sind klein geschrieben.
Aber bestimmt haben wir wieder einige Schönheitsfehler drin.
Stimmts??? :)
Benutzeravatar
Craven
User
Beiträge: 223
Registriert: Dienstag 24. Januar 2006, 13:37

Ich würde nicht jeweils eine Zeile frei lassen, das sieht seltsam aus.
[code]q = 'q = %s; print q %% repr(q)'; print q % repr(q) [/code]
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Naja, etwas kompakter kann man es schon machen: :)

Code: Alles auswählen

>>> from random import random
>>> random_steps = lambda n : (n - 2*sum(random() > 0.5 for k in xrange(n)))
>>> random_steps(100)
-10
Die 2. Zeile leistet das gleiche wie eure gleichnamige Funktion.
Auch wenn es so knapp nicht sein muss: Nehmt doch eine Zählschleife statt while und verzichtet auf choice().

Die 2. Funktion lässt sich verschlanken, aber das überlasse ich euch (oder anderen).
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

Jetzt wieder eine super tolle Frage:

Was ist lambda und wie kann man das so kurz schrieben? Verstehe den Befehl nicht. :cry:

(n - 2*sum(random() > 0.5 for k in xrange(n)))
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Sconine hat geschrieben:Jetzt wieder eine super tolle Frage:

Was ist lambda und wie kann man das so kurz schrieben? Verstehe den Befehl nicht. :cry:

(n - 2*sum(random() > 0.5 for k in xrange(n)))
Das musst du nicht verstehen und lambda brauchst du auch nicht.
Hier eine Variante, bei der es sich eher lohnt, sie zu verstehen:

Code: Alles auswählen

from random import random

def random_steps(n):
    pos = 0
    for k in xrange(n):
        pos += 1 if random() > 0.5 else -1
    return pos
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Da ich den Random-Walk auch sehen will, hier mein
Vorschlag:

Code: Alles auswählen

#!/usr/bin/env python

import random as rand
import sys


def random_walk(steps=1000):

  while steps:
    yield rand.choice([-1, 1])
    steps -= 1


def avg_distance(steps, repeats):

  locations = (sum(random_walk(steps)) for _ in xrange(repeats))
  return sum(locations) / float(repeats)


def write_randomwalk(steps, middle, scale):

  loc = middle
  for i in random_walk(steps):
    sys.stdout.write('%*s\n' % (middle + int(loc * scale), '*'))
    loc += i
    

if __name__ == '__main__':

  print 'Average', avg_distance(100, 100)
  write_randomwalk(1111, 33, 0.7)
:wink:
yipyip
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Warum nicht so?

Code: Alles auswählen

sum((random.choice((1, -1)) for n in xrange(100)))
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

Ich habe es jetzt so geschrieben:

Code: Alles auswählen

import pylab

from random import random

def random_steps(n):
    
"""simuliert n Schritte. Zufall entscheidet bei jedem Schritt ob er
    # Startpunkt hin oder vom Startpunkt weg gemacht wird.
    #Gibt den Abstand vom Startpunkt zurueck."""

    pos = 0 
    for k in xrange(n): 
        pos += 1 if random() > 0.5 else -1 
    return pos

def avg_distance(steps_to_take, repeats):

    
    """"Ruft random_steps repeats-mal auf, und laesst es jeweils
    steps_to_take-Schritte simulieren.
    Funktion gibt dann den Mittelwert der Antworten zurueck"""

    location_sum = 0

    for i in xrange(repeats):
        location_sum += random_steps(steps_to_take)
    avg_location = location_sum / (repeats * 1.)
    return avg_location

if __name__=='__main__':
    repeats = 20
    steps_list = range (500)
    distance_list = []
    for steps in steps_list:
        distance_list.append(avg_distance(steps, repeats))

        

pylab.plot(steps_list,distance_list)
pylab.xlabel('Steps taken') 
pylab.ylabel('Distance from the midpoint') 
pylab.show()  
Bekomme:

expected an indented block (für steps_to_taken)

Wieso auf einmal 0.5, obwohl ich doch nur 1 Schritt vor oder zurückgehe?

Tut mir Leid, dass ich eure Geduld so strapaziere.

[/code]
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Dauerbaustelle hat geschrieben:Warum nicht so?

Code: Alles auswählen

sum((random.choice((1, -1)) for n in xrange(100)))
Darum: :D

Code: Alles auswählen

from time import time
from random import random, choice

def random_steps_1(n):
    pos = 0
    for k in xrange(n):
        pos += 1 if random() > 0.5 else -1
    return pos

def random_steps_2(n):
    return sum((choice((1, -1)) for k in xrange(n)))

n = 10**6
t0 = time()
random_steps_1(n)
print "Dauer: %.2f s" %(time()-t0)
t0 = time()
random_steps_2(n)
print "Dauer: %.2f s" %(time()-t0)
Liefert auf meinem Rechner:

Code: Alles auswählen

Dauer: 0.78 s
Dauer: 3.10 s
Benutzeravatar
Craven
User
Beiträge: 223
Registriert: Dienstag 24. Januar 2006, 13:37

Code: Alles auswählen

Dauer: 0.14 s
Dauer: 0.69 s
Geht doch recht zügig.
[code]q = 'q = %s; print q %% repr(q)'; print q % repr(q) [/code]
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Dauer: 0.73 s
Dauer: 1.60 s
Dafuer finde das "choice()" etwas uebersichtlicher ...
:wink:
yipyip
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

Da sind die Profis unter sich und ich störe. :)

Auch wenn ich nicht viel verändert habe, könnt ihr ma gucken, was bei dem Programm falsch ist?

Code: Alles auswählen

import pylab

from random import random

def random_steps(n):
    
 """simuliert n Schritte. Zufall entscheidet bei jedem Schritt ob er
    Startpunkt hin oder vom Startpunkt weg gemacht wird.
    Gibt den Abstand vom Startpunkt zurueck."""

pos = 0 
for k in xrange(n): 
    pos += 1 if random() > 0.5 else -1 
        return pos

def avg_distance(steps_to_take, repeats):

    
    """"Ruft random_steps repeats-mal auf, und laesst es jeweils
    steps_to_take-Schritte simulieren.
    Funktion gibt dann den Mittelwert der Antworten zurueck"""

    location_sum = 0

    for i in xrange(repeats):
        location_sum += random_steps(steps_to_take)
    avg_location = location_sum / (repeats * 1.)
    return avg_location

if __name__=='__main__':
    repeats = 20
    steps_list = range (500)
    distance_list = []
    for steps in steps_list:
        distance_list.append(avg_distance(steps, repeats))

        

pylab.plot(steps_list,distance_list)
pylab.xlabel('Steps taken') 
pylab.ylabel('Distance from the midpoint') 
pylab.show()  
Irgendwas stimmt mit der Position von dem ersten return nicht, ich weiss aber nicht was.

Oder habe ich schon wieder viel zu viele Fehler reingeschuggelt???
Bestimmt habe ich die Tipps total falsch verstanden.
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Ruecke mal die Zeilen 12 bis 15 um jeweils 4 Leerzeichen ein,
die gehoeren ja zur Funktion "random_steps()" und nicht
auf Modul-Ebene.
(Das hattest Du doch schon vorher richtig)
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

Wenn ich das mache, zeigt er mir wieder an, das die Position nicht richtig ist. :( So ein Mist.
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Ach so, den Kommentar natuerlich auch um 4 Leerzeichen
einruecken.
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

Wen ich das grüne wegnehme, dann geht es:

Code: Alles auswählen

import pylab 

from random import random 

def random_steps(n): 
    
 #"""simuliert n Schritte. Zufall entscheidet bei jedem Schritt ob er 
    #Startpunkt hin oder vom Startpunkt weg gemacht wird. 
    #Gibt den Abstand vom Startpunkt zurueck.""" 
    pos = 0 
    for k in xrange(n): 
        pos += 1 if random() > 0.5 else -1
        return pos 

def avg_distance(steps_to_take, repeats): 

    
    """"Ruft random_steps repeats-mal auf, und laesst es jeweils 
    steps_to_take-Schritte simulieren. 
    Funktion gibt dann den Mittelwert der Antworten zurueck""" 

    location_sum = 0 

    for i in xrange(repeats): 
        location_sum += random_steps(steps_to_take) 
    avg_location = location_sum / (repeats * 1.) 
    return avg_location 

if __name__=='__main__': 
    repeats = 20 
    steps_list = range (500) 
    distance_list = [] 
    for steps in steps_list: 
        distance_list.append(avg_distance(steps, repeats)) 

        

pylab.plot(steps_list,distance_list) 
pylab.xlabel('Steps taken') 
pylab.ylabel('Distance from the midpoint') 
pylab.show()  
Aber ich bekomme jetzt die Fehlermeldung:

Code: Alles auswählen

raceback (most recent call last):
  File "C:/Python25/Aufgabenzettel_9/1D_Random.py", line 34, in <module>
    distance_list.append(avg_distance(steps, repeats))
  File "C:/Python25/Aufgabenzettel_9/1D_Random.py", line 25, in avg_distance
    location_sum += random_steps(steps_to_take)
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'
Ich habe doch unten gar nix verändert.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Dauer: 9.29385 s
Dauer: 17.31616 s
Dauer: 0.45509 s
Wobei das letzte Python-C ist, hier der Source (bestimmt Vollmist, aber ich kann halt nix C :D):

Code: Alles auswählen

#include "/usr/include/python2.6/Python.h"
#include "stdlib.h"

static PyObject *random_steps(int n) {
    int pos, i;
    pos = 0;

    for(i=0; i<10000000; i++) {
        if(rand()%2) {
            pos++;
        }
        else {
            pos--;
        }
    }

    return Py_BuildValue("i", pos);
}

static PyMethodDef crand_methods[] = {
    {"random_steps", (PyCFunction)random_steps, METH_VARARGS, "C rand"},
    {NULL, NULL}
};

PyMODINIT_FUNC initcrand(void) {
    PyImport_AddModule("crand");
    Py_InitModule("crand", crand_methods);

    srand(time(NULL));
}
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Code: Alles auswählen

import pylab

from random import random

def random_steps(n):
    """simuliert n Schritte. Zufall entscheidet bei jedem Schritt ob er
    Startpunkt hin oder vom Startpunkt weg gemacht wird.
    Gibt den Abstand vom Startpunkt zurueck.
    """
    pos = 0
    for k in xrange(n):
        pos += 1 if random() > 0.5 else -1

    return pos

def avg_distance(steps_to_take, repeats):
    """
    Ruft random_steps repeats-mal auf, und laesst es jeweils
    steps_to_take-Schritte simulieren.
    Funktion gibt dann den Mittelwert der Antworten zurueck
    """

    location_sum = 0

    for i in xrange(repeats):
        location_sum += random_steps(steps_to_take)
    avg_location = location_sum / (repeats * 1.)
    return avg_location

if __name__== '__main__':
    repeats = 20
    steps_list = range (500)
    distance_list = []
    for steps in steps_list:
        distance_list.append(avg_distance(steps, repeats))

       

pylab.plot(steps_list,distance_list)
pylab.xlabel('Steps taken')
pylab.ylabel('Distance from the midpoint')
pylab.show() 
Das 'return pos' war innerhalb der for-Schleife.
So muesste es jetzt funktionieren.

(Sieht toll aus mit pylab.)
:wink:
yipyip
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

Super, ich bekomme zwar jetzt ein Bild, aber habe immernoch eine Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Python25\Aufgabenzettel_9\Random_ich_neu.py", line 34, in <module>
    distance_list.append(avg_distance(steps, repeats))
  File "C:\Python25\Aufgabenzettel_9\Random_ich_neu.py", line 25, in avg_distance
    location_sum += random_steps(steps_to_take)
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'

Kann das nicht einmal ohne irgendwelche Probleme bei mir laufen???
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Wieder irgendwo falsch eingerückt, vermutlich gibt random_steps nichts zurück (also None, welches sich nicht mit einem int zusammenzählen lässt).
Antworten