Die Funktion sample() zum Erzeugen von Stichproben zu selbstdefinierten diskreten Zufallsvariablen

Die Funktion sample() wird verwendet, um Stichproben zu erzeugen. Sie l├Ąsst sich so konfigurieren, dass man die Wahrscheinlichkeitsverteilungen von beliebigen selbstdefinierten diskreten Zufallsvariablen einsetzen kann. Zudem kann man das Ziehen mit beziehungsweise ohne Zur├╝cklegen realisieren.
Noch keine Stimmen abgegeben
Noch keine Kommentare

Einordnung des Artikels

Zum besseren Verst├Ąndnis werden Kenntnisse ├╝ber Zufallsvariablen vorausgesetzt, wie sie etwa in Grundbegriffe der Wahrscheinlichkeitsrechnung: Diskrete und stetige Zufallsvariablen vermittelt werden.

Ein typischer Fehler in der Anwendung der Funktion sample() wird in Der h├Ąufigste Fehler bei der Anwendung der Funktion sample() besprochen. Er beruht darauf, dass der Aufruf von sample() an die Funktion sample.int() weitergereicht wird, wenn die Stichprobe aus einer einelementigen Menge gezogen wird ÔÇô und dann zu unerwartetem Verhalten von sample() f├╝hrt. Auf den ersten Blick ist es kaum vorstellbar, wie diese Situation eintreten kann, dort wird ein Beispiel besprochen, das zeigt, dass man sehr leicht in diese Falle tappen kann.

Einf├╝hrung

Die Basis-Pakete von R erlauben den Umgang mit einer Vielzahl von Wahrscheinlichkeitsverteilungen; einige davon wurden in Wahrscheinlichkeitsverteilungen in R besprochen, weitere findet man unter

?Distributions

Zumindest f├╝r Verteilungen von Zufallsvariablen X, die endlich viele Werte annehmen, wird eine einfache M├Âglichkeit vorgestellt, wie man sie mit Hilfe eines Dataframes modellieren kann. Auf Zufallsvariablen mit abz├Ąhlbar vielen Werten (wie etwa die Poisson-Verteilung) kann man dieses Modell nicht ├╝bertragen.

M├Âchte man eine Simulation eines Zufallsexperimentes mit dieser Zufallsvariable X durchf├╝hren ÔÇô also Zufallszahlen erzeugen, die gem├Ą├č der Verteilung von X "ausgew├╝rfelt" werden ÔÇô, kann man die Funktion sample() einsetzen. So kann man auch mit Verteilungen arbeiten, die nicht in ?Distributions enthalten sind.

Es soll ausdr├╝cklich betont werden, dass die Modellierung einer Zufallsvariable als Dataframe nicht zwingend erforderlich ist, um die Funktion sample() anzuwenden. Sie ist aber dann zu empfehlen, wenn die Zufallsvariable mehrmals ben├Âtigt wird und nicht nur eine Stichprobe erzeugt werden soll.

In diesem Kapitel wird:

  • zun├Ąchst die oben erw├Ąhnte Modellierung einer Zufallsvariablen durch ein Dataframe beschrieben,
  • dann die Funktion sample() und vor allem die Bedeutung ihrer Eingabewerte vorgestellt.
  • Dazu werden typische Anwendungsbeispiele gezeigt.
  • Insbesondere wird gezeigt, wie man Simulationen mit Hilfe von replicate() wiederholen kann, ohne Schleifen zu implementieren.

Modellierung einer Zufallsvariable als Dataframe

Die hier beschriebene Modellierung einer Zufallsvariable durch ein Dataframe ist identisch mit der Beschreibung in Grundbegriffe der Wahrscheinlichkeitsrechnung: Die Zufallsvariable. Dort wird auch der mathematische Hintergrund erl├Ąutert, insbesondere der Zusammenhang zwischen Zufallsvariablen und einer Wahrscheinlichkeitsverteilungen.

Um mit Zufallsvariablen und ihren Verteilungen zu arbeiten, ben├Âtigt man ein m├Âglichst einfaches R-Objekt, in dem die Eigenschaften der Zufallsvariable abgelegt werden. Man kann dann ÔÇô je nach gew├╝nschter Anwendung ÔÇô leicht darauf zugreifen und geeignete Service-Funktionen selber implementieren.

Es wird hier ein einfacher Zugang gew├Ąhlt, der immer dann anwendbar ist, wenn eine Zufallsvariable eine Wertemenge mit endlich vielen reellen Werten besitzt: Die Zufallsvariable wird durch ein Dataframe modelliert, das zwei Spalten besitzt:

  1. Eine Spalte value f├╝r die m├Âglichen Werte der Zufallsvariable.
  2. Eine Spalte prob f├╝r die Wahrscheinlichkeiten der entsprechenden Werte.

Es ist klar, dass die Summe aller Wahrscheinlichkeiten eins ergeben muss, dies und weitere Anforderungen an das Dataframe werden weiter unten diskutiert.

Die Eigenschaften von Dataframes wurden in Dataframes in R: der Datentyp data frame und Dataframes in R: Anwendungen ausf├╝hrlich erkl├Ąrt; die n├Âtigen Kenntnisse dar├╝ber werden hier vorausgesetzt.

Erzeugen von Zufallsvariablen

Das folgende Skript zeigt, wie eine Zufallsvariable als Dataframe erzeugt wird, die eine gezinkte M├╝nze beschreibt. Dabei werden die Ergebnisse des Zufallsexperimentes durch 0 und 1 modelliert (anstelle von Kopf und Zahl); die Wahrscheinlichkeiten daf├╝r, dass die M├╝nze 0 oder 1 zeigt, sollen 0.4 beziehungsweise 0.6 betragen:

values <- c(0, 1)
probs <- c(0.4, 0.6)

coin <- data.frame(value = values, prob = probs)
print(coin, digits = 2)
#   value  prob
# 1     0 0.40
# 2     2 0.60

Zeile 1 und 2: Die Werte und die entsprechenden Wahrscheinlichkeiten werden definiert.

Zeile 4: Aus den Werten der Zufallsvariable und den zugeh├Ârigen Wahrscheinlichkeiten wird ein Dataframe mit 2 Spalten gebildet. Damit man sp├Ąter leichter auf die Spalten zugreifen kann, werden ihnen Namen gegeben. Man kann sogar einen Schritt weitergehen und fordern, dass die Werte und Wahrscheinlichkeiten mit diesem Namen im Dataframe abgelegt werden m├╝ssen (siehe weiter unten bei is.randomVariable() ). Man k├Ânnte nat├╝rlich die Zeilen 1, 2 und 4 zu einer Anweisung zusammenfassen, da man die Objekte values und probs im Folgenden nicht mehr ben├Âtigt.

Zeile 5: Die Ausgabe zeigt das Dataframe.

Da die Zufallsvariable coin lediglich zwei Werte besitzt, k├Ânnte man sie auch mit einer Binomialverteilung modellieren; aber man kann sich leicht Beispiele vorstellen, die sich nicht auf Verteilungen aus den Basis-Paketen zur├╝ckf├╝hren lassen.

├ťberpr├╝fen, ob eine Zufallsvariable vorliegt

In den folgenden Kapiteln werden einige Simulationen und Berechnungen mit Hilfe von Zufallsvariablen durchgef├╝hrt, die als Dataframes gespeichert werden. Damit ein Dataframe eine Zufallsvariable beschreiben kann, m├╝ssen mehrere Bedingungen erf├╝llt sein. Es ist daher sinnvoll eine Funktion zu implementieren, die ├╝berpr├╝ft, ob ein Dataframe diese Bedingungen erf├╝llt. Die Bedingungen lauten:

  1. Es wird die Konvention vereinbart, dass die Werte und Wahrscheinlichkeiten ├╝ber die Namen value und prob angesprochen werden. Daher muss das Dataframe zwei Spalten mit diesen Namen besitzen. (Welche die erste und welche die zweite Spalte ist unerheblich, wenn die Spalten nur ├╝ber die Namen angesprochen werden.)
  2. Die genannten Spalten m├╝ssen identische L├Ąnge besitzen. Diese Abfrage sollte eigentlich ├╝berfl├╝ssig sein, da man kein anderes Dataframe erzeugen kann.
  3. Keine der Einzel-Wahrscheinlichkeiten in der Spalte prob darf negativ sein.
  4. Die Summe der Einzel-Wahrscheinlichkeiten muss 1 ergeben. Diese Bedingung kann Schwierigkeiten verursachen, wenn mit den Wahrscheinlichkeiten Berechnungen ausgef├╝hrt werden (zum Beispiel wenn Zufallsvariablen miteinander verkn├╝pft werden). Denn durch Rundungsfehler kann es zu kleinen Abweichungen kommen, die man tolerieren kann. Es ist daher besser eine Schranke ╬Á vorzugeben (etwa in der Gr├Â├čenordnung 10-8) und nur zu ├╝berpr├╝fen, ob die Summe der Wahrscheinlichkeiten um weniger als ╬Á von 1 abweicht.
  5. Die Werte in der Spalte value m├╝ssen eindeutig sein.

Das folgende Skript zeigt eine m├Âgliche Implementierung f├╝r die Funktion is.randomVariable() ; in den cat() -Befehlen kann man geeignete Debug-Ausgaben einf├╝gen oder sie ganz weglassen.

is.randomVariable <- function(rv, eps = 1e-8){
  stopifnot(is.data.frame(rv))
  result <- TRUE
  
  if( !( identical(names(rv), c("prob", "value")) || identical(names(rv), c("value", "prob")) ) ){
    cat("names \n")
    result <- FALSE
  }
  if( !identical(length(rv$value), length(rv$prob)) ){
    cat("length != \n")
    result <- FALSE
  }
  if(any(rv$prob < 0)){
    cat("neg. Prob. \n")
    result <- FALSE
  }
  if( abs( sum(rv$prob) - 1 ) > eps ){
    cat("sum Prob \n")
    result <- FALSE
  }
  if( !identical(rv$value, unique(rv$value)) ){
    cat("Eind. \n")
    result <- FALSE
  }
  return(result)
}

Eingabewerte von is.randomVariable() sind das Dataframe, das eine Zufallsvariable rv (rv = random variable) repr├Ąsentieren soll und die Schranke eps.

Man h├Ątte in allen if-Zweigen mit einem return-statement return result die Funktion vorzeitig verlassen k├Ânnen. Der Vorteil der gew├Ąhlten Implementierung ist, dass alle Verletzungen der Bedingungen an eine Zufallsvariable untersucht und Warnhinweise ausgegeben werden.

Aufgabe: Definieren Sie einige Zufallsvariablen nach dem Vorbild coin oben und testen Sie, ob die Funktion is.randomVariable() den Wert TRUE liefert oder welche der Bedingungen verletzt ist.

Erzeugen von Stichproben mit sample()

Simulationen mit Zufallsvariablen, die durch ein Dataframe gegeben sind

Im Paket base gibt es die Funktion sample(), die verwendet werden kann, um Stichproben zu erzeugen. Sie besitzt 4 Eingabewerte, die sich bis auf replace = FALSE selbst erkl├Ąren:

sample(x, size, replace = FALSE, prob = NULL)

Der R├╝ckgabewert von sample() ist ein Vektor der L├Ąnge size, der aus Elementen von x besteht, die gem├Ą├č den Wahrscheinlichkeiten prob "ausgew├╝rfelt" werden.

# Dataframe coin wie oben
sample(x = coin$value, size = 10, replace = TRUE, prob = coin$prob)
# [1] 0 1 1 0 0 1 1 0 0 1

Oder mit einer kleinen Auswertung ÔÇô man l├Ąsst mit Hilfe von table() anzeigen, wie oft die Komponenten von x in der Stichprobe enthalten sind:

s <- sample(x = coin$value, size = 1000, replace = TRUE, prob = coin$prob)
table(s)
# s
# 0   1 
# 377 623

Das folgende Beispiel modelliert einen gezinkten W├╝rfel, bei dem die 6 mit erh├Âhter und die 1 mit erniedrigter Wahrscheinlichkeit erscheint:

values <- (1:6)
probs <- c(1, 2, 2, 2, 2, 3) / 12

# gezinkter W├╝rfel:
d <- data.frame(value = values, prob = probs)
print(d, digits = 2)
#   value  prob
# 1     1 0.083
# 2     2 0.167
# 3     3 0.167
# 4     4 0.167
# 5     5 0.167
# 6     6 0.250

# Stichproben:
sample(x = d$value, size = 10, replace = TRUE, prob = d$prob)
# [1] 2 1 3 2 5 4 5 6 5 5

s <- sample(x = d$value, size = 1000, replace = TRUE, prob = d$prob)

# absolute H├Ąufigkeiten
table(s)
# s
# 1   2   3   4   5   6 
# 88 158 171 177 138 268

# relative H├Ąufigkeiten
table(s) / 1000
# s
# 1     2     3     4     5     6 
# 0.088 0.158 0.171 0.177 0.138 0.268 

# Mittelwert:
sum(s) / 1000
# [1] 3.923

Die Skripte erwecken wom├Âglich den Eindruck, dass man sample() mit einem Dataframe konfigurieren muss ÔÇô das ist nat├╝rlich nicht der Fall. Man kann die aufrufenden Argumente x und prob auch direkt setzen. Da in vielen Anwendungen x-Werte wie x = (1:6) vorkommen, kann man dies durch x = 6 abk├╝rzen. Das folgende Skript zeigt obige Simulation mit einer Stichprobe der L├Ąnge 1000 f├╝r einen Laplace-W├╝rfel, der nicht eigens als Dataframe modelliert wird:

s <- sample(x = 6, size = 1000, replace = TRUE, prob = rep(x = 1/6, times = 6))

table(s)
# s
# 1   2   3   4   5   6 
# 162 188 158 181 148 163 

table(s) / 1000
# s
# 1     2     3     4     5     6 
# 0.162 0.188 0.158 0.181 0.148 0.163 

sum(s) / 1000
# [1] 3.454

Man erkennt, dass der Mittelwert jetzt n├Ąher bei 3.5 liegt. Abbildung 1 zeigt die Auswertung der Simulationen im Stabdiagramm.

Abbildung 1: Darstellung der mit sample() erzeugten Simulationen (mit jeweils 1000 W├╝rfen) im Histogramm; aufgetragen sind die absoluten H├Ąufigkeiten der Augenzahlen. (Links der gezinkte W├╝rfel, rechts der Laplace-W├╝rfel.)Abbildung 1: Darstellung der mit sample() erzeugten Simulationen (mit jeweils 1000 W├╝rfen) im Histogramm; aufgetragen sind die absoluten H├Ąufigkeiten der Augenzahlen. (Links der gezinkte W├╝rfel, rechts der Laplace-W├╝rfel.)

Wird f├╝r x eine nat├╝rliche Zahl n vorgegeben, ist der default-Wert f├╝r size gleich n:

s <- sample(x = 6, replace = TRUE, prob = rep(x = 1/6, times = 6))
s
# [1] 1 1 3 6 5 5
table(s)
# s
# 1 3 5 6 
# 2 1 2 1

Das Argument replace von sample()

Das Argument replace von sample() erm├Âglicht

  1. die Simulation von Ziehen ohne Zur├╝cklegen mit replace = FALSE beziehungsweise
  2. Ziehen mit Zur├╝cklegen mit replace = TRUE .

Damit ist Folgendes gemeint:

  1. Ziehen ohne Zur├╝cklegen: Beim Erzeugen einer Stichprobe wird die erste Komponente s1 aus der Grundmenge x entnommen. Um die zweite Komponente zu ziehen, wird s1 aus der Grundmenge entfernt und ein Element aus der verkleinerten Grundmenge gezogen. Und so weiter. Aber dann kann die Stichprobe h├Âchstens so viele Komponenten besitzen wie x. Ist size gr├Â├čer als die L├Ąnge von x, erh├Ąlt man eine Fehlermeldung (siehe Beispiel unten).
  2. Ziehen mit Zur├╝cklegen: Jetzt stehen bei jedem Zug alle Elemente von x zur Verf├╝gung; das Argument size kann beliebige Werte annehmen (siehe Beispiele oben).

Dass das Argument replace hei├čt, sollte man so verstehen: im Falle von replace = TRUE wird nach jedem Zug die Ausgangssituation wiederhergestellt, also die eigentlich verkleinerte Grundmenge x wieder durch das ├╝rspr├╝ngliche x ersetzt.

Beispiel: Ziehen ohne Zur├╝cklegen mit dem gezinkten W├╝rfel

Der W├╝rfel ist mit den Zahlen 1 bis 6 beschriftet. Beim Ziehen ohne Zur├╝cklegen kann eine einmal gezogene Zahl nicht nochmal erscheinen. Das folgende Skript realisiert dies f├╝r:

  • size = 5 : es werden 5 verschiedene Zahlen gezogen (Zeile 4).
  • size = 6 : die Stichprobe ist eine Permutation von (1, 2, ..., 6) (Zeile 8).
  • size = 7 : da f├╝r den siebten Zug keine Zahl mehr zur Verf├╝gung steht, werden auch die ersten 6 Z├╝ge nicht realisiert; es wird eine Fehlermeldung ausgegeben (Zeile 12).
# Dataframe d wie oben

# size = 5:
sample(x = d$value, size = 5, replace = FALSE, prob = d$prob)
# [1] 6 4 2 5 3

# size = 6:
sample(x = d$value, size = 6, replace = FALSE, prob = d$prob)
# [1] 2 3 6 4 1 5

# size = 7:
sample(x = d$value, size = 7, replace = FALSE, prob = d$prob)
# Error in sample.int(length(x), size, replace, prob) : 
#   cannot take a sample larger than the population when 'replace = FALSE'

Beispiel: Ziehung von Lottozahlen

Es soll folgende Simulation des Lottospiels "6 aus 49" durchgef├╝hrt werden:

  • Ein Spieler gibt einen Lotto-Tip ab.
  • Es werden so lange neue Lottozahlen gezogen bis der Spieler zum ersten Mal 4 Richtige oder mehr als 4 Richtige hat (anstelle der 4 kann man auch jede andere Zahl von 1 bis 6 einsetzen).
  • Die Folge der erzeugten Lottozahlen soll ausgewertet werden:
    • Wie oft wurde gespielt?
    • Wie oft wurden "0 Richtige", "1 Richtige", ..., "6 Richtige" erzielt?

L├Âsungsvorschlag:

Die Funktion numberOfSuccesses() vergleicht einen abgegebenen Lotto-Tip mit den tats├Ąchlich gezogenen Zahlen actual und berechnet die Anzahl der "Richtigen" (M├Ąchtigkeit des Mengendurchschnitts):

# Anzahl der Richtigen 
numberOfSuccesses <- function(tip = (1:6), actual){
  stopifnot( identical(length(tip), length(actual)) )
  return( length( intersect(tip, actual) ) )
}

Die eigentliche Simulation erfolgt in der Funktion simulation(), siehe folgendes Skript. Sie erzeugt so lange neue Lotto-Tips bis die Anzahl der "Richtigen" die Anzahl successes erreicht oder ├╝berschreitet. Damit die Simulation reproduzierbar ist, kann man seed f├╝r den Zufallsgenerator setzen (Zeile 4). Die eigentlich relevanten Eingabewerte f├╝r die Simulation sind:

  1. Die Anzahl der "Richtigen" successes; die Simulation soll abbrechen, wenn diese Zahl erreicht oder ├╝berschritten wird (bei successes = 0 muss keine Ziehung stattfinden).
  2. Der abgegebene Lotto-Tip tip.
# so lange wiederholen bis successes 
# oder mehr Treffer erreicht werden; Auswertung
simulation <- function(successes = 4, tip = (1:6), seed = 42){
  set.seed(seed)
  # Anzahl der Treffer im Tip:
  n.succ <- 0
  # Auswertung, Z├Ąhler:
  cnt = rep(x = 0, times = 7)

  while(n.succ < successes){
    n.succ <- numberOfSuccesses(tip = tip, actual = sample(x = 49, size = 6))
    cnt[n.succ + 1] <- cnt[n.succ + 1] + 1
    # print(cnt)
  }
  
  # Auwertung:
  cat("Anzahl der Ziehungen: ", sum(cnt), "\n")
  cat("H├Ąufigkeiten der Trefferzahlen:\n")
  print(cnt)
}

Zeile 10 bis 14: Das Kernst├╝ck der Simulation ist die while-Schleife. In ihr werden immer wieder neue Lottozahlen gezogen und gez├Ąhlt, wie viele Richtige der Tip hat. Die Schleife wird wiederholt, solange die Anzahl der Richtigen kleiner ist als successes (Zeile 12).

Zeile 11: Die Ziehung der Lottozahlen wird mit Hilfe von sample() realisiert; dazu werden aus den Zahlen von 1 bis 49 genau 6 Zahlen ausgew├Ąhlt. Da replace = FALSE (default-Wert) kann eine Zahl nicht mehrfach gezogen werden.

Zeile 8 und 10: Die Variable cnt dient der Auswertung der Simulation. Sie z├Ąhlt wie oft "0 Richtige", "1 Richtige" und so weiter vorkommen. Dazu wird cnt als Vektor der L├Ąnge 7 vorbereitet und alle Komponenten gleich null gesetzt (Zeile 8). In der Schleife wird die entsprechende Komponente um 1 erh├Âht (Zeile 12); am Ende wird cnt ausgegeben (Zeile 18). Die Gesamtzahl der Ausspielungen erh├Ąlt man, indem man die Trefferzahlen aus cnt summiert (Zeile 17).

Das folgende Skript zeigt den Aufruf von simulation() und eine typische Auswertung:

simulation(successes = 4)
# Anzahl der Ziehungen:  263 
# H├Ąufigkeiten der Trefferzahlen:
#   [1] 130 102  26   4   1   0   0

Das Argument prob von sample()

Die bisher besprochenen Beispiele werfen einige Fragen auf:

  1. Bei der Modellierung einer Zufallsvariable als Dataframe wurde argumentiert, dass die Summe der Wahrscheinlichkeiten eventuell ein wenig von 1 abweichen kann, insbesondere wenn die Wahrscheinlichkeiten durch vorherige Berechnungen entstanden sind (Fortpflanzung von Rundungsfehlern). Wie reagiert die Funktion sample(), wenn sich die Wahrscheinlichkeiten nicht zu 1 addieren?
  2. Wie werden die neuen Wahrscheinlichkeiten nach Entfernen eines Elementes aus x berechnet, wenn sample() mit replace = FALSE verwendet wird? Es wird sich zeigen, dass dies mit der Antwort auf die erste Frage zusammenh├Ąngt.

Im folgenden Beispiel wird die gezinkte M├╝nze von oben mit offensichtlich unsinnigen Wahrscheinlichkeiten definiert und mit ihr eine Simulation durchgef├╝hrt. Zum Vergleich wird die identische Simulation mit "korrekt" definierten Wahrscheinlichkeiten durchgef├╝hrt:

set.seed(42)
s <- sample(x = c(0, 1), size = 1000, replace = TRUE, prob = c(4, 6))
table(s)
# s
# 0   1 
# 377 623 

set.seed(42)
s <- sample(x = c(0, 1), size = 1000, replace = TRUE, prob = c(0.4, 0.6))
table(s)
# s
# 0   1 
# 377 623

Man erkennt, dass das Argument prob nicht den Anforderungen einer Wahrscheinlichkeit gen├╝gen muss. Denn die Zahlenwerte werden als Gewichte interpretiert, die nicht auf 1 normiert sein m├╝ssen. Erst wenn die Interpretation als Gewichte versagt, erh├Ąlt man eine Fehlermeldung:

s <- sample(x = c(0, 1), size = 1000, replace = TRUE, prob = c(0, 0))
# Error in sample.int(length(x), size, replace, prob) : 
#   too few positive probabilities

Aber diese Interpretation erkl├Ąrt, wie die Wahrscheinlichkeiten neu berechnet werden, wenn beim Ziehen ohne Zur├╝cklegen, also replace = FALSE , ein Element aus x entfernt wird: Es wird einfach das zugeh├Ârige Gewicht aus dem Vektor prob entfernt.

Wiederholung von Simulationen mit replicate()

In vielen Anwendungen soll eine Stichprobe nicht nur einmal sondern ├Âfters erzeugt werden; die Auswertung geschieht dann ├╝ber die Gesamtheit der Stichproben. Naheliegend ist es, die Stichprobe innerhalb einer Schleife zu erzeugen und geeignete Variablen vorzubereiten, die die Ergebnisse der Stichproben abspeichern und die sp├Ąter ausgewertet werden.

Oft kann man die Schleifen durch den geschickten Einsatz der Funktion replicate() vermeiden. Sie wurde in Die Familie der apply-Funktionen in R Teil 3: Weitere mit apply() verwandte Funktionen erl├Ąutert und wird hier nicht beschrieben; stattdessen wird ein typisches Beispiel gezeigt, wie man replicate() einsetzen kann.

Beispiel: Z├Ąhlen der Fixpunkte von Permutationen

Das folgende Listing zeigt die Zahlen von 1 bis 7 sowie 3 Permutationen der Zahlen von 1 bis 7. Eine Permutation eines Vektors enth├Ąlt alle Komponenten des Vektors, sie k├Ânnen aber in beliebiger Reihenfolge neu angeordnet werden.

1 2 3 4 5 6 7
# Permutationen:
1 2 7 4 6 5 3
4 6 1 5 3 7 2
3 5 2 4 7 1 6

Die erste Permutation enth├Ąlt 3 Fixpunkte; damit ist gemeint, dass genau 3 Zahlen an der selben Stelle stehen wie im urspr├╝nglichen Vektor (n├Ąmlich die Zahlen 1, 2 und 4). Die zweite Permutation enth├Ąlt keinen Fixpunkt, die dritte Permutation enth├Ąlt einen Fixpunkt. Und unter dem Blickwinkel der Fixpunkte betrachtet, kann man die erste Zeile des Listings als eine Permutation von (1:7) mit 7 Fixpunkten auffassen.

Es ist ein bekanntes Problem der Kombinatorik zu fragen, wie viele Permutationen von (1:n) es gibt, die genau 0, 1, 2, ..., n Fixpunkte besitzen. Es soll hier nicht dieses Abz├Ąhlproblem behandelt werden, sondern es soll gezeigt werden, wie man dazu eine einfache Simulation und Auswertung schreibt.

Eine Permutation von (1:n) wird mit Hilfe von sample() erzeugt, wobei man ihre default-Werte verwenden kann:

n <- 7
sample(x = n)
# 7 6 2 5 3 1 4

Um die Anzahl der Fixpunkte einer Permutation zu bestimmen, kann man die Funktion fixedPoints() einsetzen, die sich leicht implementieren l├Ąsst:

fixedPoints <- function(p){
  return( sum( p == (1:length(p)) ) )
}

Mit Hilfe von replicate() lasen sich nun beliebig viele Permutationen erzeugen und jeweils die Fixpunkte z├Ąhlen:

v <- replicate(n = 1000, expr = fixedPoints(sample(x = 7)))

Der Vektor v hat die L├Ąnge 1000 und seine Komponenten geben an, wie viele Fixpunkte die entsprechenden Permutationen haben; er l├Ąsst sich leicht auswerten:

table(v)
# v
#   0   1   2   3   4   5 
# 359 367 201  58  10   5

Die folgende Abbildung 2 zeigt die Auswertung von 1000 Permutationen von (1:7) beziehungsweise (1:21) .

Abbildung 2: Auswertung von jeweils 1000 Permutationen: gez├Ąhlt wird, wie viele Fixpunkte sie enthalten. Die relativen H├Ąufigkeiten sind auf der y-Achse aufgetragen.Abbildung 2: Auswertung von jeweils 1000 Permutationen: gez├Ąhlt wird, wie viele Fixpunkte sie enthalten. Die relativen H├Ąufigkeiten sind auf der y-Achse aufgetragen.