Random og Random, fru Blom!

Hvis du skal laga eit program som bruker ein randomfunksjon, så må du vera oppmerksom på at det fins fleire moduler (eller bibliotek) med randomfunksjonar, og då må du må vita kva du gjer. Her er noken eksempel som viser ka som kan skje hvis du ikkje veit det!

1. Moduler

Men først, ser vi på kva muligheter vi har.

Tilfellet randint

Det er kanskje ikkje så overraskande at metoden randint(a, b) fins både i random og numpy.random (og dermed pylab). Men det er overraskande at dei ikkje oppfører seg likt:

Eksempel 1:

Dette kan skapa problemer, og for å vera ekstra upedagogisk, så begynner eg med eit eksempel som du aldri skal gjera. Her importerer vi rubbel og bit frå begge modulane vha *-notasjonen. Og dette er grunnen til at vi ikkje liker dette. Sidan begge, som sagt, inneheld metoden randint, er det ikkje opplagt ka for ein av disse variantane som blir brukt. For å unngå å krasja programmet, så kjører vi begge metodane inne i "try - except". Det vil sei at vi prøver å kjøra koden i try-blokka, men hvis ikkje det går, kjøres koden under except.


# FY! Aldri gjer dette!
from pylab import *
from random import *

# Versjon 1
try:
    svar = randint(1, 7, size = 10) # kast med ti terninger
    print("svar=",svar)
except:
    print("Dette (versjon 1) funka definitivt ikkje!")

# Versjon 2
try:
    svar = randint(1,6)
    print("Dette (Versjon 2) funka - Kanskje!")
    print("svar =",svar)
except:
    print("Dette funka heller ikkje!")
	  

Kjøring:

Når vi kjører programmet, ser vi ka for ein metode som er blitt forsøkt kjørt. Når vi prøver versjon 1, så går ikkje det, og programmet skriv derfor ut at det ikkje funka. Men versjon 2 ser ut til å gå bra. Dette betyr at det er random.randint som er blitt kjørt. Det hadde du gjerne ein mistanke om også, sidan random blir importert sist. Det betyr at randint, som først var importert frå pylab, blir overskrive. Då blir syntaksen i versjon 1 feil (for den funkar bare på pylab/numpy), og den kan ikkje kjørast. Men spørsmålet er likevel om det var dette vi ville med programmet? Prøv gjerne å bytta om dei to importane, og sjå kva som skjer då.


Dette (versjon 1) funka definitivt ikkje!
Dette (Versjon 2) funka - Kanskje!
svar = 1 ?
	  

Eksempel 2

La oss sei at du bare skal kasta ein terning, og har lyst til å bruka den vanlige randint()-funksjonen, slik som over. Men av ein eller annan grunn (kanskje dette er ein del av eit lengre program, som skal gjera mange andre ting) så har du importert begge modulane på "fy-måten". Då kan det sjå slik ut:


from random import *
from pylab import *

for i in range(20):
    svar = randint(1,6)
    print(svar,end=" ")
      

Kjøring:


      5 2 4 1 2 5 2 5 5 5 3 1 2 3 5 2 4 1 5 5
      

Dette ser jo greit ut, ikkje sant? Men ved nærmare ettersyn ser vi at vi manglar seksarar. Dette kan jo vera ein tilfeldighet (!), sidan det er nettopp det vi jobbar med. Men kor stor er sjansen egentlig for at vi får 0 seksarar av 20 kast? (det kan du jo prøva å rekna på). Når vi har oppdaga dette, og har mistanke om at noko er feil, kan vi kjøra omigjen, og gjerne auka antal kast. Då vil det visa seg at det aldri dukkar opp ein einaste seksar. Så vi ser nærmare på import-setningane våre, og ser at vi importerer pylab sist. Det vil sei at det er den som gjeld. Og då forstår vi at denne koden bare genererer tilfeldige frå 1 til 6 - IKKJE til og med 6, som vi ville ha. Løysinga her er bare å ta bort den siste import, og då er problemet løyst.

Men det kunne jo fort skje at vi ikkje oppdaga at programmet vårt aldri genererte seksarar. Eksempel 2 viser at samme kode, med to forskjellige modular vil kunna oppføra seg forskjellig. Derfor er det viktig å både å lesa gjennom koden og å testa den grundig.

Eksempel 3

Så viser eg den anbefalte metoden (eller ein av dei anbefalte metodane) å gjera dette på. Her importerer vi fremdeles alt frå begge modulane, men når vi skal bruka metodane, må vi prefixa med enten "random." eller "numpy.random." Då har vi tilgang til begge randint-versjonane, og derfor må vi skriva eksplisitt kva versjon vi vil bruka.

Programmet simulerer ti terningkast på to forskjellige måtar. Sidan random.randint() ikkje har muligheten for å kasta fleire i slengen, så må vi gjera dette inne i ei for-løkke, og skriva ut ett kast om gongen. Med numpy gir vi inn antallet med "size"-parameteren. Det vi får ut då er ei liste med disse forskjellige kasta.


import random
import numpy

K = 10 # Antal terningar

# Terningkast med random-biblioteket:
for i in range(10):
    ettkast = random.randint(1,6)
    print(ettkast,end=" ")

# Terningkast med numpy-biblioteket: 
Kkast = numpy.random.randint(1, 7, size = K) 
print("\n",Kkast)

Kjøring:


5 3 1 4 2 1 4 2 2 2 
 [6 2 1 2 6 3 5 1 3 2]
		

Som vi ser: dette produserer 10 tilfeldige tal, men det skjer på forskjellige måtar. Den første gir oss eit og eit tal, som vi må generera og printa ut kvar for seg. Det neste lar oss generera alle ti med ei linje. Men det vi får ut her er ei liste med dei ti tala. 

Konklusjon: Hvis du skal kasta terningar, kan du gjerne bruka numpy sin randint(), men då må du vita at for å få med seksarar, så må du skriva randint(1, 7) . Men har du først valgt numpy, så treng du ikkje random.