Programmeringsspråket C

Slumptal

Om detta tillägg

Här förutsätter vi att du har läst alla delar fram till och med 5 för att förstå detta tillägg fullt ut. Du kan ta dig till godo vad detta tillägg ger redan efter del 2, men för att förstå är fram till och med del 5 är rekommenderat.

I många fall när man programmerar, speciellt när det lägger grundläggande spel, kan det vara bra att kunna generera slumptal. Det finns funktioner för detta bland standardbiblioteken. Eftersom det efterfrågats skriver jag ett tillägg kring hur man genererar slumptal. Anledningen att det är ett tillägg och inte kapitel är att det för vår del inte känns som en vital del av C, utan snarare några trevliga funktioner som följer med i standardbiblioteken.

Lite skitsnack om slumptal

Datorer är helt logiska och resonerande varelser. Således är det egentligen ganska motsägelsefullt att man vill be den om ett helt slumpartat tal. Och faktum är att du aldrig kommer få ett helt slumpmässigt tal ur standardbiblioteken som följer med C. Talen vi genererar här är vad som kallas pseudorandom på engelska, alltså låtsasslumpmässiga. Men det duger gott och väl för oss.

rand()

Funktioner för slumptalsgenerering finns i standardbiblioteket stdlib.h. Funktionen vi för tillfället är intresserade av heter rand(), och genererar ett tal mellan 0 och jättehögt. För att komma åt denna funktion måste vi inkludera stdlib.h. Följande kod genererar ett slumptal och skriver ut det.

#include <stdio.h>
#include <stdlib.h>
 
int main() {
    int slumptal;
 
    slumptal = rand();
 
    printf("Talet som slumpades ut var %i\n", slumptal);
 
    return 0;
}

Variabeln slumptal får här värdet som returneras av rand(), som är ett slumpmässigt tal. Om du provar att köra detta program flera gånger på rad, så kommer du märka att det blir ett lustigt resultat. Talet som genereras är samma varje körning! Och inte nog med det: betrakta följande loop som slumpar ut 7 tal och skriver ut alla.

int i, slumptal;
 
i = 0;
while (i < 7) {
    slumptal = rand();
    printf("Talet som slumpades ut var %i\n", slumptal);
    i++;
}

Testkör du denna loop kommer du upptäcka att talföljden blir samma varje gång du kör programmet. Vad är det för slumptal som alltid är samma? Detta beteende ger behovet att introducera två nya funktioner.

srand()

När rand() slumpar ut tal gör den det genom att utföra komplex matematik baserat på ett "frö" och tidigare slumpade tal. Detta innebär att om den får samma frö varje gång programmet körs (som var fallet i de båda tidigare programmen), så kommer första talet alltid bli samma. Och eftersom den slumpar baserat på fröet och tidigare tal, så kommer nästa tal också bli samma varje gång. Och den efter det också. Och så vidare.

Lösningen på detta är, som du kanske redan har gissat, att ge datorn ett nytt frö varje gång vi kör programmet. Hur kan vi garantera att fröet är nytt varje gång? Och vad i all världen kan vi använda för frö? Ett klassiskt trick är att använda ett frö som med största sannolikhet har ändrats sen förra gången programmet kördes: tiden.

I standardbiblioteket time.h finns funktionen time(), och om den får en nolla som argument, returnerar den dagens datum och tid räknat i antalet sekunder som har passerat sedan 1 Januari 1970. Detta tal kan vi använda som frö. Fröet sätts med funktionen srand(). srand() tar ett heltal som argument. Följande exempel visar detta.

#include <stdio.h>
#include <stdlib.h>    //för rand() och srand()
#include <time.h>    //för time()
 
int main() {
    int tiden, slumptal;
 
    tiden = time(0);
    printf("Datum och tid nu ar %i sekunder sedan 1 jan 1970\n", tiden);
    srand(tiden);
 
    slumptal = rand();
    printf("Talet som slumpades ut var %i\n", slumptal);
 
    return 0;
}

Nu inkluderar vi tre bibliotek, ett för utskrift och inmatning, ett för rand()- och srand()-funktionerna, samt ett för att få tillgång till tiden, som vi använder som frö. Vi sätter variabeln tiden till vad klockan är när programmet startas, sedan skriver vi ut detta för att verifiera att det har ändrats sen senaste körning. Efter detta sår vi tiden som frö för rand(). Resten av programmet är identiskt med första exemplet i detta tillägg. Var vänlig notera att fröet behöver bara sås en gång per program.

Begränsa slumptal

Vill du inte ha ett slumptal mellan 0 och jättehögt? Du kan begränsa antalet slumptal med modulo-operatorn. Modulo representeras av procent-tecknet (%) i C. Modulo utför en heltalsdivision mellan två tal och ger resten. Till exempel 14 % 4 = 2, eftersom 14 / 4 = 3, rest 2. Detta faktum kan man utnyttja för att aldrig få ett högre tal än något visst man bestämmer. Till exempel i exemplet nyss hade 14 kunnat vara vad som helst, man hade ändå fått ett resultat mellan 0 och 3. Detta kan vi utnyttja även i slumptalsgenereringen, som i följande kodexempel.

slumptal1 = rand() % 4;
slumptal2 = rand() % 67;

Eftersom jag här utför en modulo-operation får talet en gräns; slumptal1 är mellan 0 och 3, slumptal2 är mellan 0 och 66.

Varning: denna metod för att begränsa tal kommer många erfarna matematiker och programmerare och logiker och allt vad det är att klaga lite på, eftersom den innebär att sannolikheten att få lägre tal blir marginellt större. Det rör sig om mycket små siffror här, men de är trots allt siffror. Det finns ett bättre sätt att begränsa, men för dina behov duger modulo-varianten. (För den nyfikne och/eller matematiske: (int) (rand() / ((double) RAND_MAX) * 4) ger ett tal mellan 0 och 3. Utnyttjar grundläggande matematik och typecasting, något du inte gått igenom än, om du läst till del 5.)

På samma sätt går det att få en annan nedre gräns på slumptalen med enkel matematik. Betänk vad rand() % 4 kan anta för värden. 0, 1, 2 och 3. Vad kan då rand() % 4 + 3 anta för värden? (Notera att prioriteten på modulo är högre än addition.) Jo, just precis, 0+3, 1+3, 2+3 samt 3+3. Eller med andra ord, 3, 4, 5, 6. Nu har man alltså slumpat fram tal mellan 3 och 6. Så om man vill simulera ett tärningskast kan man alltså använda sig av rand() % 6 + 1 för att få tal mellan 1 och 6.

Slumpmässiga tal kan med fördel användas för små enkla spel, som kan gå ut på att gissa tal på olika sätt. Temat talgissning går att variera ad nauseam.

Copyleft kqr & slaeshjag 2009, 2012 some rights reserved