| Das Pascal-Dreieck ist eine elegante und populäre Methode zur Darstellung von Binomial-Koeffizienten. | Diese Seite demonstriert einige Varianten zur Berechnung von Binomial-Koeffizienten. |
Algorithmen
|
Ausgewählte IT-Rezepte, Reihen-Entwicklung (Iteration) |
| Pasal-Dreieck | Definition, tabellierte Werte, Algorithmen |
| Binomial Koeffizienten | Live-Demonstration des Algorithmus |
| Kalkulation | Berechnung mit einem Kalkulations-Programm (LibreOffice, OpenOffice, MS-Excel, ...) |
| Basic | Berechnung mit Basic (LibreOffice-Basic, Visual Basic, VBA) |
| Javascript | Live-Berechnung eines Arrays von Binomial-Koeffizienten |
| C/C++ | Berechnung mit einem einfachen C-Programm |
| Perl | Rechnung mit sehr großen Zahlen und beliebiger Genauigkeit |
| PHP | Einfach oder mit beliebiger Genauigkeit |
| Lotto | Hypergeometrische Verteilung, Wahrscheinlichkeit von Stichproben oder Lotto-Tipps |
Pascal-Dreieck |
||||||||||||||||||||||||||||||||||||||||||||||
Quelle:
Wikipedia (modif.)
Blaise Pascal(1623-1662) war ein bedeutender französischer Mathematiker und Physiker. Schon im Alter von 16 Jahren verfasste er eine eindrucksvolle Arbeit über Kegelschnitte.Wegen seiner ausgezeichneten Arbeiten zum Thema Vakuum und zum Druck in Gasen und Flüssigkeiten wurde die internationale SI-Einheit des Drucks nach ihm benannt. Er entwickelte eine einfache mechanische Rechenmaschine (Pascaline), von der es heute noch 9 Exemplare gibt. Die Programmiersprache Pascal wurde (1972) nach ihm benannt. Sie ist einfach, klar und streng organisiert und war Vorbild für viele heute verbreitete Programmiersprachen. Bei Studien zur Wahrscheinlichkeit untersuchte er u.a. das nach ihm benannte Pascal-Dreieck (rechts) und die Gesetzmäßigkeiten der darin enthaltenen Zahlenwerte (Binomial-Koeffizienten). Gleiche oder ähnliche Dreiecks-Anordnungen waren schon früher in der indischen, persischen und chinesischen Kultur bekannt, allerdings ohne die weitreichenden Schlüsse und Anwendungen in der Wahrscheinlichkeits-Rechnung. |
Pascal-Dreieckin der klassischen Darstellung: Der Charme liegt im besonders einfachen und klaren Aufbau:• Anfang und Ende jeder Zeile sind mit =1 festgelegt. • Jede andere Zahl ist die Summe der beiden darüber liegenden Zahlen.
|
|||||||||||||||||||||||||||||||||||||||||||||
| Man kann unschwer erkennen, dass die größten Zahlenwerte entlang der senkrechten Mittel-Achse des Pascal-Dreiecks auftreten. | Die größten Zahlenwerte erfordern in jedem Algorithmus auch die meiste Rechenzeit. Deshalb werden die hier angegebenen Rechenzeiten, Grenzen usw. stets auf die Mitte einer Zeile bezogen. | |||||||||||||||||||||||||||||||||||||||||||||
Berechnung der Binomial-Koeffizienten |
|||||||||||
| Im gesamten Text dieser Seite bezeichnet die Formulierung (i|j) den Binomial-Koeffizienten 'i über j' | |||||||||||
Binomial-Koeffizient mit Fakultät-Funktion
Quelle:
Wikipedia
Diese Formel ist die meist-zitierte zur Berechnung eines Binomial-Koeffizienten.● Der 'Fakultät-Algorithmus' ist so einfach, dass man ihn mit jedem Kalkulations-Programm problemlos anwenden kann. In der hier verwendeten Formulierung:
(n|k) = n! / k! / (n-k)!
Ein weiterer Vorteil der Fakultät-Funktion: Sie rechnet - wie jede andere
ganzzahlige Funktion - absolut genau. Wenn man mit konventionellen Methoden die
Grenze der Genauigkeit überschreitet, dann kann man zumindest mit
→
BigNum-Methoden (Ziffern-Strings) auch große Werte absolut genau berechnen.
(n|k) = fakultät(n) / fakultät(k) / fakultät(n-k) ● Die → Fakultät-Funktion n! hat allerdings einen großen Nachteil: Sie wächst so rasch, dass man die Formel nur bis (170|*) verwenden kann. Damit ist die → Werte-Grenze des Variablen-Typs 'Double Precision' erreicht, der von allen gängigen Programmen und Programmiersprachen verwendet wird. Mit → BigNum-Methoden kann man diese Grenze auf Kosten langer Rechenzeit überschreiten. ♦ Details zur Fakultät-(Faktorielle)-Funktion Logarithmus-Variante:Mit einem kleinen Trick kann man die Formel für einen wesentlich größeren Werte-Bereich anwenden:(n|k) = exp( ln(n!) - ln(k!) - ln((n-k)!) )
●
Diese Formel funktioniert bis (1029|*) ● Man kann sie allerdings nur in Programmiersprachen anwenden, nicht in Kalkulations-Programmen - dort arbeitet sie ebenfalls nur bis (170|*)
Auf der Seite der →
Fakultät-Funktion wird u.a. der Euler-McLaurin-Algorithmus vorgestellt,
mit dem man ln(n!) auch für sehr große
Argumente n rasch und genau berechnen kann.
|
Binomial-Koeffizient mit Produkt-Folge
Quelle:
Wikipedia
Dieser Algorithmus bezeichnet eine endliche Produkt-Folge, d.h. eine genau
bekannte Anzahl von Elementen, die miteinander multipliziert werden.Das Ergebnis ist stets eine ganze Zahl. Wegen des begrenzten Werte-Bereichs der ganzzahligen Variablen-Typen muss man dennoch meist den Gleitkomma-Typ 'Double Precision' verwenden. Der Algorithmus arbeitet bis mit 'Double Precision' Variablen bis zu (1029|*) ● Die Berechnung mit einem Kalkulations-Programm ist theoretisch möglich, erfordert jedoch einen relativ großen Aufwand. ● Dieser 'Produkt-Algorithmus' ist ausgezeichnet zur Programmierung mit einer (beliebigen) Programmiersprache geeignet. Einzelne Binomial-Koeffizienten werden damit am schnellsten berechnet. ● Ein Nachteil ist die gegenüber der Fakultät-Formel geringere Genauigkeit: In jedem Schritt der Schleife wird eine Division durchgeführt. Dabei muss man Fehler in Kauf nehmen. Hauptsächlich der erste auftretende Fehler (bei kleinen j) bestimmt den Fehler des Resultats. ● Die Variante ist weniger gut geeignet, wenn man zahlreiche Binomial-Koeffizienten braucht, und/oder diese oft verwendet (d.h. laufend neu berechnet). |
||||||||||
Binomial-Koeffizient mit Pascal-DreieckAuf der Grundlage des Pascal-Dreiecks kann man einen ganz anders organisierten 'Pascal-Algorithmus' erstellen: Er verwendet mehrere hintereinander geschaltete Regeln (rechts) an Stelle einer Formel.Die Tabelle zeigt das Pascal-Dreieck in jener etwas verzerrten Darstellung, die heute meist zur Bezeichnung der Koordinaten verwendet wird. Die einzelnen Zahlen sind die Binomial-Koeffizienten für Zeile i und Spalte j Die Tabelle zeigt nur einen begrezten Ausschnitt bis j<=8 und kein 'symmetrisches' Dreieck wie im Kapitel oberhalb
Sie haben vermutlich die Verwendung von Javascript durch ihren Browser
abgeschaltet. Daher funktioniert weder die Live-Berechnung der
Binomial-Koeffizienten noch ihre Darstellung in einer Tabelle noch die
interaktive Anzeige des Algorithmus.
Live-Demonstration: Klicken sie in eine Zelle: Alle Zellen, aus denen der
Algorithmus diesen Wert berechnet, werden rot unterlegt.● Grün unterlegte Zellen enthalten Standard-Werte, die in der Informatik nicht 'berechnet' sondern einfach eingesetzt werden. ● Gelb unterlegte Zellen werden zuerst am Punkt j=i/2 der gleichen Zeile gespiegelt und erst dann weiter berechnet. ● Weiß unterlegte Zellen innerhalb des Dreiecks werden aus den beiden Nachbar-Zellen links oberhalb berechnet. ● Verbotene Bereiche außerhalb des Dreiecks werden nicht berechnet. Ein Klick darauf löscht alle eingetragenen Spuren. |
Regeln:
Die Formulierung (i|j) bezeichnet den
Binomial-Koeffizienten 'i über j' bzw.
die Zahl aus Reihe i und Spalte j
der Tabelle links.• Die folgenden Regeln werden in der angegebenen Reihenfolge angewendet. Wenn eine Regel zutrifft, dann werden alle weiteren Regeln ignoriert. ● Als Start zur Berechnung wählt man die gewünschte Zelle [i][j] und berechnet ihren Wert schrittweise (nach oben bzw. links) unter Anwendung der folgenden Regeln: ● Verbot: Die leeren Zellen (i|j>i) kann man nicht berechnen, bzw. setzt man diese Werte auf =0 Beispiel: Die Berechnung der rechten oberen Ecke der Tabelle (0|8)=0 ● Symmetrie: Die gelb unterlegten Zellen liegen in jeder Zeile symmetrisch zur Spalte j=i/2 und werden daher nicht berechnet, sondern von der 'linken Seite' der gleichen Zeile kopiert: Für j>i/2 wird (i|j)=(i|i-j) eingesetzt. Beispiel: (7|5) wird nicht berechnet, sondern aus (7|2)=21 kopiert. Wenn dieser Wert nicht bekannt ist, dann wird (7|2) berechnet. ● Spalte 1: Für alle Werte der grün unterlegten Spalte 1 gilt (i|1)=i Diese Zellen werden nicht 'berechnet' sondern der Wert von i eingesetzt. ● Spalte 0: Für alle Werte der grün unterlegten Spalte 0 wird der konstante Wert (i|0)=1 eingesetzt. ● Addition: Die Werte aller verbleibenden Zellen werden aus der Summe der beiden 'schräg links darüber' liegenden Zellen berechnet: (i|j) = (i-1|j) + (i-1|j-1)
Beispiel:
(4|2) = (3|2) + (3|1) = 3+3 = 6Die zur Addition benötigten Zellen werden selbst nach den gleichen Regeln berechnet, d.h. das Verfahren wird 'rekursiv' angewendet. |
||||||||||
|
Array von Binomial-Koeffizienten
♥
Dieser 'Pascal'-Algorithmus ist besonders effizient, wenn man ein 2dimensionales
Array von Binomial-Koeffizienten damit berechnet, z.B. alle Koeffizienten
von (0|0) bis (100|100), das sind
immerhin 10201 einzelne Koeffizienten.Das Array wird nur 1mal (rasch und systematisch) berechnet, danach kann man alle darin enthaltenen Koeffizienten beliebig oft ohne neue Berechnung verwenden. Für oftmalige Verwendung kann man das Array in einer Datei oder Datenbank speichern, und damit viel Rechenzeit sparen. |
Man kann den Algorithmus auch zur Berechnung einzelner Biniomial-Koeffizienten verwenden. Die Funktionen dazu sind erstaunlich kompakt, weil sie rekursiv programmiert werden, d.h. sich im Verlauf des Algorithmus selbst aufrufen. In diesem Fall werden jedoch viele Koeffizienten mehrfach berechnet, was die Effizienz bedeutend verringert. |
||||||||||
Rechnen mit beliebig großen ZahlenAlle modernen Programmiersprachen bieten spezielle Ergänzungs-Module zum Rechnen mit beliebig großen Zahlen. Diese werden als Strings verwaltet, d.h. als (beliebig lange) Ziffern-Ketten. Die Verarbeitung erfolgt Ziffern-weise, ähnlich wie man mit Papier und Bleistift rechnet. |
Diese Methoden brauchen wesentlich mehr Rechenzeit und sind nur für Sonder-Aufgaben geeignet. Alle 3 hier vorgestellten Algorithmen lassen sich bei Verwendung dieser Module zur Berechnung fast beliebig großer Binomial-Koeffizienten verwenden. ♦ Details: Große Zahlen in Kalkulations-Programmen (LibreOffice, MS-Excel, ...) und in Basic (LibreOffice-Basic, Visual Basic, VBA) |
||||||||||
Binomial-Koeffienzienten mit einem Kalkulations-Programm |
|||||||||||||
Mit jedem gängigen Kalkulations-Programm kann man Binomial-Koeffizienten berechnen,
z.B. mit LibreOffice,
OpenOffice, MS-Excel, ...Je nach Programm und Version sind mehrere Varianten möglich. |
● Kalkulations-Dateien sind zwar in alle Betriebssysteme und mindestens in das Programm LibreOffice portabel, die Portabilität hängt jedoch von den verwendeten Funktionen ab. |
||||||||||||
Funktion KOMBINATIONEN()Moderne Kalkulations-Programme bieten diese Funktion zur direkten Berechnung der Binomial-Koeffizienten.• Das ist die beste Lösung für den eigenen Gebrauch. • Nachteil: In älteren Programmen und -Versionen nicht enthalten, daher nicht verlässlich portabel. |
|
||||||||||||
Funktion FAKULTÄT()Alle gängigen Kalkulations-Programme bieten die Funktion FAKULTÄT()● Diese Lösung ist mit großer Sicherheit portabel, jedoch etwas aufwändiger zu programmieren. ● Sie funktioniert bis maximal (170|*) |
(i|j) = i! / j! / (i-j)!
|
||||||||||||
Basic (LibreOffice-Basic, Visual Basic, VBA)Bei Verwendung von Benutzer-definierten Basic-Funktionen ist es möglich, Binomial-Koeffizienten bis maximal (1029|514)=1.43E+308 zu berechnen. Mit Ziffern-Strings kann man sogar beliebig große Binomial-Koeffizienten berechnen. |
Im folgenden Kapitel werden ↓ Basic-Funktionen zu diesem Zweck vorgestellt. Auf einer eigenen Seite werden Basic-Funktionen zum Rechnen mit großen Zahlen vorgestellt. |
||||||||||||
Binomial-Koeffizienten mit Basic (LibreOffice-Basic, Visual Basic, VBA) |
|||||||||||||||||||||||
| Ein Interpreter der Programmiersprache → Basic ist in jedem gängigen Kalkulations-Programm enthalten (LibreOffice-Calc, OpenOffice-Calc, MS-Excel, ...). Man braucht keine zusätzlichen Hilfsmittel, um das Beispiel selbst zu programmieren, oder den Beispiel-Text in ein → Basic-Modul einzusetzen. |
●
Sowohl die Kalkulations-Dateien als auch der hier vorgestellte Basic-Quelltext ist
in alle gängigen Betriebssysteme und Kalkulations-Programme portabel. ♦ Details zur Verwendung von Basic-Funktionen mit Kalkulations-Programmen. |
||||||||||||||||||||||
Programmierung mit Fakultät-FunktionDie Funktion binom_fakt_1() formuliert den 'Fakultät-Algorithmus', der alternativ in jedem ↑ Kalkulations-Programm mit der Funktion FAKULTÄT() verwendbar ist.• Vorteil: Man kann binom_coef_1() als benutzer-definierte Funktion genauso verwenden wie die Funktion KOMBINATIONEN() moderner Kalkulations-Programme, und man kann die Funktion auch zur Berechnung weiterer Aufgaben durch andere Basic-Programme verwenden. • Nachteil: Die Fakultät-Funktion ist in Basic nicht verfügbar und muss ebenfalls als Basic-Funktion (hier: my_fact() ) eingesetzt werden. Hilfs-Funktion my_fakt()
Die Berechnung der →
Fakultät-Funktion ist ein Klassiker der IT-Ausbildung.
Hier wird eine rekursive Variante vorgestellt, die allerdings nicht schneller als
eine einfache Schleifen-Variante arbeitet und mehr (Stack)-Speicher beansprucht.
• Nachteil: Die Fakultät-Funktion erreicht schon mit 170! die Werte-Grenze, daher ist auch die Funktion binom_coef_1() nur bis zu (170|*) verwendbar. |
Berechnung der Binomial-Koeffizienten als Basic-Funktion:
Option Explicit
Function binom_coef_1(i As Integer, j As Integer) As Double binom_coef_1 = my_fact(i) / my_fact(j) / my_fact(i - j)
End FunctionFunction my_fact(ByVal n As Double) As Double
If n = 1 Then
End Function
my_fact = 1
Else
my_fact = n * my_fact(n - 1)
End If
|
||||||||||||||||||||||
Logarithmus der FakultätDie Basic-Funktion binom_coef_2() umgeht die großen Zahlenwerte der Fakultät-Funktion durch Verwendung der Logarithmen. Sie ist bis zu (1029|*) verwendbar.Die Hilfs-Funktion lnfact() berechnet den → natürlichen Logarithmus der Fakultät-Funktion: ln(n!) = ln(1) + ln(2) + ln(3) + ... + ln(n)
Diese Funktion berechnet Werte bis maximal n<32766 Man kann sie überall dort einsetzen, wo die Ergebnisse der Fakultät-Funktion multipliziert oder dividiert werden, also z.B. bei Berechnung von Binomial-Koeffizienten: (i|j) = exp( ln(i!) - ln(j!) - ln((i-j)!) )
|
Berechnung mit dem natürlichen Logarithmus der Fakultät-Funktion als
Basic-Funktion:
Function binom_coef_2(n As Integer, k As Integer) As Double
binom_coef_2 = Exp( lnfact(n) - lnfact(k) - lnfact(n - k) )
End FunctionFunction lnfact(ByVal n As Double) As Double Dim i As Integer Dim s As Double
s = 0
End Function
For i = 1 To n s = s + Log(CDbl(i))
Nextlnfact = s |
||||||||||||||||||||||
Produkt-FolgeDie basic-Funktion binom_coef_3() rechnet nach dem 'Produkt-Algorithmus' in einer einzigen Schleife ohne Hilfs-Funktion.Wie in fast jeder Produkt-Folge wird die Produkt-Variable (hier p) vor Beginn der Schleife auf p=1 gesetzt. In jedem Schleifen-Durchgang wird ein Element der Folge berechnet und mit dem aktuellen Wert von p multipliziert. Diese Variante funktioniert bis (1029|*) |
Berechnung des Binomial-Koeffizienten mit einer Produkt-Folge
als Basic-Funktion:
Function binom_coef_3(n As Integer, k As Integer) As Double
Dim j As Integer Dim p As Double
p = 1
End Function
For j = 1 To k p = p * CDbl((n + 1 - j)) / CDbl(j)
Nextbinom_coef_3 = p |
||||||||||||||||||||||
BigNum-MethodenWenn man nicht mit konventionellen Zahlen sondern mit → Ziffern-Strings rechnet, dann gibt es praktisch keine Werte-Grenze. Die Rechnung erfordert allerdings viel mehr Zeit.Das Beispiel rechts verwendet die auf eigenen Seiten vorgestellten Basic-Module → BigInt und → BigFlt zur Berechnung nach der Fakultät-Formel. An Stelle der Operatoren / und ! werden Funktionen verwendet. Diese Version rechnet auch mit großen Argumenten (langsam, aber) sehr genau und ist der Produkt-Schleife vorzuzieren, deren Genauigkeit schwieriger zu programmieren ist. |
Berechnung des Binomial-Koeffizienten mit Ziffern-Strings:
Function BigFlt_Binomial(n As Integer, k As Integer) As String
Dim rs, f1, f2, f3 As String
f1 = BigInt_Factorial(n)
End Function
f2 = BigInt_Factorial(k) rs = BigFlt_Div(f1, f2) f3 = BigInt_Factorial(n - k) rs = BigFlt_Div(rs, f3) BigFlt_Binomial = rs |
||||||||||||||||||||||
|
Anwendung in einem Kalkulations-Programm:
Das Beispiel demonstriert die meisten hier vorgestellten Varianten im
direkten Vergleich.• Beachten sie, dass immer j<=i sein muss, begrenzen sie evtl. den Eingabe-Bereich für die Zelle B2 Setzen sie j=(i/2) um den jeweils ungünstigsten Fall zu testen. • Alle Versionen funktionieren mit absoluter Genauigkeit bis n<=17, ab n>17 sind nur die ersten 15 Dezimalstellen zuverlässig. • Alle Versionen funktionieren (mit <=15 Stellen Genauigkeit) bis n<=170 • Einige Versionen funktionieren bis n<=1029 • Die BigNum-Version ist nur durch die verwendete Rechenzeit begrenzt. |
|
||||||||||||||||||||||
Binomial-Koeffizienten mit Javascript |
|
Live BerechnungDie Tabelle zur Vorstellung des ↑ Pascal-Algorithmus wurde von ihrem Browser unmittelbar nach dem Laden der Webseite Live berechnet. Die dazu aufgewendete Zeit ist normalerweise zu klein für eine Messung, jedenfalls unerheblich.● Ein Javascript-Interpreter ist in jedem gängigen Browser enthalten. Man braucht daher zur Ausführung der Programme keine zusätzlichen Resourcen. |
● Javascript-Programme sind portabel, d.h. die Webseiten inkl. der darin enthaltenen Javascript-Programme funktionieren auf allen gängigen Betriebssystemen und mit allen gängigen Browser-Programmen. ● Der Quelltext jeder Webseite besteht aus einfachem lesbarem Text. Man kann ihn daher mit jedem Text-Editor bearbeiten, z.B. auf Windows mit notepad.exe, besser mit Notepad++ (professionell, kostenfrei). |
|
Hier wird demonstriert, wie man eine größere Anzahl von
Binomial-Koeffizienten nur 1mal nach dem 'Pascal-Algorithmus' berechnet
und zur oftmaligen weiteren Verwendung in einem Array
(hier bca) speichert. Das Array bca wird von allen Javascript-Funktionen gemeinsam verwendet und daher global (außerhalb von Funktionen und Klassen) deklariert. Die Dimensionen (maximale Anzahl der Binomial-Koeffizienten) wird in den Variablen imax, jmax festgelegt. ♦ Details zu Arrays in Javascript Funktion bca_create() füllt das Array bca mit Daten. In zwei verschachtelten Schleifen werden alle Indices (Koordinaten) i und j durchlaufen. Für jedes Array-Element bca[i][j] wird eine von 5 Regeln angewendet. |
Berechnung der Binomial-Faktoren als
→
Javascript-Funktion:
var bca=new Array();
var imax=12; var jmax=8; function bca_create() {
var i=0; var j=0;
}
for(i=0;i<=imax;i++) {
bca[i]=new Array();
}
for(j=0;j<=jmax;j++) {
if(!j) {bca[i][j]=1;} // col 0
}
else if(j>i) {bca[i][j]=0;} // illegal else if(j==1) {bca[i][j]=i;} // col 1 else if(j>(i/2)) {bca[i][j]=bca[i][i-j];} // sym else {bca[i][j]=bca[i-1][j]+bca[i-1][j-1];} // add |
|
Regeln:
Die 4 Tests (if, else if) werden bis zum ersten
zutreffenden durchlaufen, danach werden alle weiteren ignoriert.Man kann die Anzahl der Regeln reduzieren, um einen streng mathematischen Algorithmus zu erhalten, der allerdings langsamer arbeitet. • Die 1. Bedingung prüft, ob j==0 ist: Wenn sie zutrifft, dann wird in die erste Spalte immer die konstante Zahl =1 eingetragen. • Die 2. Bedingung prüft, ob die Koordinaten mit j>i außerhalb des Pascal-Dreiecks fallen. Man könnte diese Array-Elemente einfach überspringen, die Eintragung von =0 ist jedoch zuverlässiger. • Die 3. Bedingung prüft, ob j==1 ist. Wenn sie zutrifft, dann wird in die 2. Spalte immer der Wert des Index =i eingetragen. |
• Die 4. und letzte Bedingung prüft, ob die Koordinaten in der rechten Hälfte des Pascal-Dreiecks j>(i/2) liegen: Diese ist symmetrisch und wird daher nicht berechnet sondern aus der linken Hälfte kopiert. Zu diesem Zeitpunkt müssen die Array-Elemente der linken Hälfte schon berechnet sein. ■ Die ersten 4 Regeln arbeiten besonders schnell und werden daher vorzugsweise angewendet. Nur dann, wenn keine davon zutrifft, wird die letzte Regel (else...) angewendet: • In diesem Fall werden die Werte der beiden links oberhalb liegenden Elemente addiert - die zu diesem Zeitpunkt schon berechnet sein müssen. |
Binomial-Koeffizienten mit C/C++ |
|
|
Hier wird zur Berechnung einzelner Binomial-Koeffizienten der 'Produkt-Algorithmus'
verwendet, zur Berechnung eines Arrays von Binomial-Koeffizienten der 'Pascal-Algorithmus'. Da die Verwendung mehrdimensionaler Arrays in C etwas aufwändiger ist, wird ein komplettes Programm vorgestellt, d.h. die Deklaration und Verwendung des Arrays inkl. Übergabe an Funktionen. Die Funktionen zeigen die enge Verwandtschaft aller modernen Programmiersprachen. Vergleichen sie die C-Version mit der ↑ Javascript-Version. Die Dimensionen der Array-Version werden zur Vereinfachung mit Compiler-Direktiven #define eingestellt, d.h. man kann sie zur Laufzeit nicht mehr ändern. Sie sind ohne weitere Maßnahmen bis imax=33 und jmax<=imax verwendbar. Die im Beispiel verwendeten Angaben erzeugen Koeffizienten-Arrays bis maximal i=10 |
Berechnung von Binomial-Koeffizienten mit
→
C-Funktionen:
#include <stdio.h>
#define imax 11 #define jmax 11 double binomial_coef(int,int); void bca_create(int[][jmax]); int main() {
int i,j;
}double bc; int bca[imax][jmax]; printf("Binomial-Koeffizienten\n\n"); // Einzeln
i=100; j=50;
bc=binomial_coef(i,j);
printf("(%d|%d) = %.5E\n",i,j,bc);// Array
bca_create(bca);
for(i=0;i<imax;i++) {
printf("\n");
}for(j=0;j<=i;j++) {
printf("(%d|%d)=%d\n",i,j,bca[i][j]);
}
return 0; double binomial_coef(int i,int j) {
int k;
}double p; p=1.0; for(k=1;k<=j;k++) {
p *= (double)(i+1-k) / (double)k;
}return p; void bca_create(int ia[][jmax]) {
int i,j;
}
for(i=0;i<imax;i++) {
for(j=0;j<jmax;j++) {
}
if(!j) {ia[i][j]=1;} // col 0
}
else if(j>i) {ia[i][j]=0;} // illegal else if(j==1) {ia[i][j]=i;} // col 1 else if(j>(i/2)) {ia[i][j]=ia[i][i-j];} // sym else {ia[i][j]=ia[i-1][j]+ia[i-1][j-1];} // add |
Einzelne Koeffizienten (i|j)Im Hauptprogramm main() wird zuerst die Berechnung eines einzelnen Binomial-Koeffizienten mit Funktion binomial_coef() demonstriert. Die Argumente i,j werden in dieser Minimal-Version im Programm vorgegeben.Der von Funktion binomial_coef() verwendete 'Produkt-Algorithmus' berechnet einzelne Binomial-Koeffizienten besonders schnell. |
|
Array von KoeffizientenFür weiterführende Aufgaben benötigt man oft (vorhersehbar) zahlreiche Binomial-Koeffizienten. In diesem Fall ist es günstiger, alle Koeffizienten bis zu einer vorgegebenen Grenze zu berechnen und in einem 2dimensionalen Array (hier bca[i][j] ) zu speichern. Mehrfach-Berechnungen werden damit weitgehend vermieden, und bei der nachfolgenden Anwendung erfolgt der Zugriff auf die gespeicherten Koeffizienten besonders schnell.Im Hauptprogramm main() wird ein 2dimensionales Array bca der Binomial-Koeffizienten deklariert, zur Berechnung an die Funktion bca_create() übergeben und anschließend mit der Standard → Ausgabe-Funktion printf() an der Konsole ausgegeben. Man sollte allerdings den vom Array belegten Speicherplatz nach Verwendung freigeben. Rechenzeit: Vergleichswert eines typischen PC für ein Array aller Binomial-Koeffizienten bis (30|30): ca. 20 µs = 0.02 ms |
|
|
Ausgabe
Rechts ein gekürztes Beispiel der Ausgabe, so wie vom vorgestellten
Demo-Programm an einer
→ Konsole (Linux-Shell, Windows cmd.exe ) erzeugt.Man kann alternativ oder zusätzlich die Ausgabe in eine Text-Datei programmieren, oder einfach die Ausgabe des fertigen Programms umleiten, z.B. auf Windows C:\> binomial_coef.exe >> C:\Daten\binomcoef.txt
|
Binomial-Koeffizienten
(100|50) = 1.00891E+029 (0|0) = 1 (1|0) = 1 ... (10|0) = 1 (10|1) = 10 (10|2) = 45 (10|3) = 120 (10|4) = 210 (10|5) = 252 (10|6) = 210 (10|7) = 120 (10|8) = 45 (10|9) = 10 (10|10) = 1 |
Binomial-Koeffizienten mit Perl |
|
Pascal-AlgorithmusDie rechts vorgestellte Funktion binomial_coef_1() berechnet einen einzelnen Binomial-Koeffizienten rekursiv nach dem oben angegebenen 'Pascal-Algorithmus'.Anwendung in einem Perl-Programm: $k = binomial_coef_1($i,$j);
Rechenzeit: (Vergleichswerte eines typischen PC) (10|5) in 0.18ms, (20|10) in 120ms, (30|15) in 96000ms Die Rechenzeiten steigen für größere Argumente so stark an, dass die Funktion in der Praxis kaum noch verwendbar ist. |
Berechnung eines Binomial-Koeffizienten mit
Perl
sub binomial_coef_1 {
my($i,$j)=@_;
}
my($k); $k=0; if($j<=$i) {
if($j>$i/2) {$j=$i-$j;}
}if($j==1) {$k=$i;} elsif(!$j) {$k=1;} else{ $k=binomial_coef_1($i-1,$j)+binomial_coef_1($i-1,$j-1);
}
return $k; |
Produkt-AlgorithmusDie rechts vorgestellte Funktion binomial_coef_2() berechnet einen einzelnen Binomial-Koeffizienten nach dem oben angegebenen 'Produkt-Algorithmus'.Anwendung in einem Perl-Programm: $k = binomial_coef_2($i,$j);
● Diese Variante rechnet für einzelne Binomial-Koeffizienten bis zu (1029|*) besonders schnell, allerdings nur mit einer maximalen Genauigkeit von 15 Dezimalstellen. Rechenzeit: (Vergleichswerte eines typischen PC) (10|5) in 0.003ms, (50|25) in 0.011ms, (100|50) in 0.02ms |
Berechnung eines Binomial-Koeffizienten mit Perl nach dem Produkt-Algorithmus:
sub binomial_coef_2 {
my($n,$k)=@_;
}
my($j,$p); $p=1.0; for($j=1;$j<=$k;$j++) { $p*=($n+1-$j)/$j;
}return $p; |
Array von Binomial-KoeffizientenDas rechts vorgestellte Perl-Programm berechnet alle Binomial-Koeffizienten bis zu einer vorgegebenen Grenze (hier: $imax,$jmax), die hier im Programm vorgegeben wird. Dazu wird der 'Pascal-Algorithmus' verwendet.Die Berechnung der Koeffizienten wird von der Funktion create_bca() durchgeführt. Die Ergebnisse werden in einem → Array (hier @bca) gespeichert. Das → Array @bca ist im Beispiel global, man braucht daher seine Adresse nicht an die Funktion zu übergeben. Als Beispiel für die Anwendung wird im Hauptprogramm ein einzelner Koeffizient aus dem Array entnommen und angezeigt. ● Diese Variante berechnet ein komplettes Array von Binomial-Koeffizienten besonders schnell, allerdings nur mit einer maximalen Genauigkeit von 15 Dezimalstellen. Für exakte große Zahlen verwendet man das ↓ Perl-Modul Math::BigInt Rechenzeit: (Vergleichswerte eines typischen PC): (10|10) in 0.08ms, (50|50) in 1.2ms, (100|100) in 4.6ms |
Berechnung eines Arrays von Binomial-Koeffizienten mit mit
Perl
#!/usr/bin/perl
use strict;my($imax,$jmax,$k,@bca); $imax = 10; $jmax = floor($i/2);
bca_create($imax,$jmax);
print "($i|$j) = $k\n";$k = $bca[$i][$j]; sub bca_create {
my($imax,$jmax)=@_;
}
my($i,$j,$bc); for($i=0;$i<=$imax;$i++) {
for($j=0;$j<=$jmax;$j++) {
}
if(!$j) {$bc = 1;} # col 0
}
elsif($j>$i) {$bc = 0;} # illegal elsif($j==1) {$bc = $i;} # col 1 elsif($j>($i/2)) {$bc = $bca[$i][$i-$j];} # sym else {$bc = $bca[$i-1][$j] + $bca[$i-1][$j-1];} # add $bca[$i][$j] = $bc; |
Produkt-Algorithmus für große ZahlenPerl bietet das kostenfreie Modul Math::Big zum Rechnen mit beliebig großen ganzen Zahlen. Diese werden bei der Ein- und Ausgabe als Ziffern-Strings verwaltetet. Das Modul arbeitet langsamer als Standard-Perl, berechnet jedoch fast beliebige Binomial-Koeffizienten mit absoluter Genauigkeit.Das Beispiel zeigt ein vollständiges Perl-Programm. Es demonstriert die Einbindung des Math::Big Moduls, die Funktion und seine Anwendung. Beachten sie bei der Anwendung, dass die Funktion einen String (!) zurückgibt. Zur übersichtlichen Ausgabe muss man den String in ein lesbares Format bringen, z.B. mit Funktion bint_to_sci() von der Seite zur → Fakultät-Funktion. Der Algorithmus verwendet Bruchzahlen (Rationale Zahlen), daher ist das Teil-Modul Math::BigRat wesentlich besser zur Rechnung geeignet als Math::Float Rechenzeit: (Vergleichswerte eines typischen PC) (10|5) in 0.70ms, (100|50) in 8.6ms, (1000|500) in 250ms, (10000|5000) in 20s |
Berechnung eines Binomial-Koeffizienten mit
Perl
nach dem 'Produkt-Algorithmus':
#!/usr/bin/perl
use strict;
use Math::BigRat;
my($i,$j,$bc); $i=1200; $j=600; $bc = binomial_coef_big($i,$j); printf("(%d|%d) = %s\n",$i,$j,$bc); sub binomial_coef_big {
my($n,$k)=@_;
}
my($j,$p); $p = Math::BigRat->new(1);
for($j=1;$j<=$k;$j++) {
$p->bmul($n+1-$j); $p->bdiv($j); return $p; |
Array für große ZahlenDas rechts vorgestellte Perl-Programm berechnet mit dem Modul Math:BigInt alle Binomial-Koeffizienten bis zu einer vorgegebenen Grenze (hier: $imax,$jmax), die hier im Programm vorgegeben wird. Dazu wird der 'Pascal-Algorithmus' verwendet.Die Berechnung der Koeffizienten wird von der Funktion create_bca_big() durchgeführt. Die Ergebnisse (Strings !) werden in einem → Array (hier @bca) gespeichert. Als Beispiel für die Anwendung wird im Hauptprogramm ein einzelner Koeffizient aus dem Array entnommen und (als String !) angezeigt. ● Diese Variante berechnet ein komplettes Array von Binomial-Koeffizienten mit absoluter Genauigkeit, dafür jedoch deutlich langsamer als die oben vorgestellte Version für gewöhnliche Gleitkomma-Zahlen. Rechenzeit: (Vergleichswerte eines typischen PC): (10|10) in 0.45ms, (50|50) in 13ms, (100|100) in 50ms, (1000|1000) in 8s, (2000|2000) in 50s |
Berechnung eines Arrays von Binomial-Koeffizienten mit mit
Perl
#!/usr/bin/perl
use strict;
use Math::BigInt;
my($imax,$jmax,$k,@bca); $imax = 10; $jmax = floor($i/2); bca_create_big($imax,$jmax); $bc = $bca[$i][$j]; printf("(%d|%d) = %s\n",$i,$j,$bc); sub bca_create_big {
my($imax,$jmax)=@_;
}
my($i,$j,$bc); for($i=0;$i<=$imax;$i++) {
for($j=0;$j<=$jmax;$j++) {
}
if(!$j) {$bc = 1;} # col 0
}
elsif($j>$i) {$bc = 0;} # illegal elsif($j==1) {$bc = $i;} # col 1 elsif($j>($i/2)) {$bc = $bca[$i][$i-$j];} # sym else {
$bc=Math::BigInt->new($bca[$i-1][$j]); $bc->badd($bca[$i-1][$j-1]); $bca[$i][$j] = $bc; |
Binomial-Koeffizienten mit PHP |
|
Pascal-AlgorithmusDie rechts vorgestellte Funktion binomial_coef_1() berechnet einen einzelnen Binomial-Koeffizienten rekursiv nach dem oben angegebenen 'Pascal-Algorithmus'.Anwendung in einem PHP-Programm: $k = binomial_coef_1($i,$j);
Für eine gegebene Variable $i erreicht die Rechenzeit mit $j=$i/2 ein Maximum. Der Algorithmus ist nur bis ca. $i<=30 brauchbar, danach steigen die Rechenzeiten unzumutbar an. |
Berechnung eines Binomial-Koeffizienten mit
PHP
<?php
function binomial_coef_1($i,$j) {
$k=0;
}if($j<=$i) {
if($j>$i/2) {$j=$i-$j;}
}if($j==1) {$k=$i;} elseif(!$j) {$k=1;} else{ $k=binomial_coef_1($i-1,$j)+binomial_coef_1($i-1,$j-1);
} return $k; ?>
|
Produkt-AlgorithmusDie rechts vorgestellte Funktion binomial_coef_2() berechnet einen einzelnen Binomial-Koeffizienten nach dem oben angegebenen 'Produkt-Algorithmus'.Anwendung in einem PHP-Programm: $k = binomial_coef_2($i,$j);
● Diese Variante rechnet für einzelne Binomial-Koeffizienten bis zu (1029|*) besonders schnell, allerdings nur mit einer maximalen Genauigkeit von 15 Dezimalstellen. |
Berechnung eines Binomial-Koeffizienten mit
PHP
<?php
function binomial_coef_2($n,$k) {
$p=1.0;
}
for($j=1;$j<=$k;$j++) { $p*=($n+1-$j)/$j;
}return $p; ?>
|
| Die → PHP-Module BC-Math und GMP rechnen nicht mit konventionellen Zahlen sondern mit Ziffern-Strings (Text aus den Ziffern '0' bis '9'). Das ist sehr langsam, bietet jedoch die Möglichkeit, mit fast beliebig großen Zahlen absolut genau zu rechnen. | PHP ist jedoch gegenüber ↑ Perl im Nachteil: Beide Module (GMP, BC-Math) rechnen nur mit ganzen Zahlen, daher ist der Produkt-Algorithmus ohne massive Änderungen nicht anwendbar. Der Pascal-Algorithmus ist für große Argumente einfach zu langsam. |
Hypergeometrische Verteilung: Lotto |
|||||||||||||||||||||||||||||||||||||||||
| Die hypergeometrische Verteilung beschreibt die Wahrscheinlichkeit, aus einer Menge von Objekten bei Entnahme einer Stichprobe solche mit bestimmten besonderen Eigenschaften zu erhalten. | Eine weniger exakte, aber anschauliche Definition: Diese Verteilung beschreibt die Chancen von Lotto-Gewinnen. | ||||||||||||||||||||||||||||||||||||||||
|
Die hypergeometrische Verteilung wird hier kurz vorgestellt, weil sie zur Berechnung
Binomial-Koeffizienten verwendet:
w(k) =( (M|k) * (N-M|n-k) ) / (N|n)
Bedeutung der Variablen:N ... gesamte Anzahl der Objekte (möglichen Werte) M ... Anzahl der Objekte mit der besonderen Eigenschaft n ... Umfang der Stichprobe (Anzahl der gezogenen Werte) k ... Anzahl der Treffer in der Stichprobe Zur Berechnung des Lotto-Haupttreffers vereinfacht sich die Formel: w(kmax) = 1 / (N|M)
Die Verwandtschaft dieser Verteilung mit dem Pascal-Dreieck ist historisch begründet:
Schon Blaise Pascal beschäftigte sich eingehend mit der Berechnung der Chancen
von Glücksspielen.
|
Beispiel: Berechnung der Wahrscheinlichkeit für das
österreichische Lotto '6 aus 45', das noch klassische einfache Regeln
verwendet: Aus insgesamt N=45 Zahlen werden M=6 gezogen und haben damit die 'besondere Eigenschaft' der Gewinn-Zahlen. Ein Tipp (Stichprobe) umfasst n=6 Zahlen und soll k=6 Treffer ergeben:
w(6) = ((6|6)*(45-6|6-6)) / (45|6) = 1*1/8145060 = 1.2277E-7
w(max=6) = 1 / (45|6) = 1.2277E-7 1/w = 1:8145060 |
||||||||||||||||||||||||||||||||||||||||
|
•
Ausgabe: Die berechnete Wahrscheinlichkeit wird in 3 verschiedenen Formaten
angezeigt: Als absoluter Wert 0..w..1 im
'wissenschaftlichen' Format, in % Prozent und
als Kehrwert 1/w • In den Zeilen 1...k...6 wird diese Formel verwendet: w(k) = ((6|k)*(45-6|6-k)) / (45|6)
Die Wahrscheinlichkeit für genau 1 Treffer beträgt 42%.• Zur Berechnung von k=0 wird die Summe der k(1)...k(6) berechnet und von 1 subtrahiert: w(0) = 1 - Σ(k(1)...k(6))
Die Wahrscheinlichkeit für 0 Treffer beträgt 40%• Die Summe Σ(k(0)...k(6))=1 muss genau =1 sein, denn einer dieser Fälle (0 Treffer ... 6 Treffer) tritt immer ein. • Für die Psychologie besonders interessant ist w(k>=1) = Σ(k(1)...k(6))
Die Wahrscheinlichkeit für mindestens 1 Treffer ist mit 60% motivierend hoch.
|
Beispiel: Berechnung der Wahrscheinlichkeit für
das österreichische Lotto '6 aus 45' für alle
möglichen 0...k...6
♣ In der Zeile Σ(w) werden die Wahrscheinlichkeiten für k=0...6 summiert, in der Zeile >=1 die Wahrscheinlichkeiten für k=1...6 |
||||||||||||||||||||||||||||||||||||||||
|
Das Beispiel rechts zeigt eine Basic (LibreOffice-Basic, Visual Basic VBA) Funktion
zur Berechnung der Wahrscheinlichkeit einer hypergeometrischen Verteilung. Mit dieser Funktion kann man u.a. die oben gezeigte Lotto-Tabelle mit jedem Standard Kalkulations-Programm bequem berechnen. Beispiel: Wahrscheinlichkeit für einen österreichischen Lotto 5er:
w(5 aus 45) = p_hypergeom(45;6;6;5) = 2.87E-05
w(6 aus 45) = p_hypergeom(45;6;6;6) = 1.23E-07 Die Funktion verwendet die ↑ Basic-Funktion binom_coef(), für die man jede der auf dieser Seite vorgestellten Varianten verwenden kann. |
Berechnung der Wahrscheinlichkeit einer hypergeometrischen Verteilung
als Basic-Funktion:
Function p_hypergeom( _
nges As Integer, mges As Integer, _
Dim w, w1, w2, w3 As Double
n As Integer, k As Integer) As Double
w = 0
End Function
If k <= n Then
w = binom_coef(mges, k)
End Ifw = w * binom_coef((nges - mges), (n - k)) w = w / binom_coef(nges, n) p_hypergeom = w |
||||||||||||||||||||||||||||||||||||||||
Lotto mit 2 RegelnPsychologisch wesentlich raffinierter sind Lotto-Systeme mit einer zusätzlichen Bedingung. Die 2. Regel ist betont einfach, z.B. im deutschen Lotto eine einzige Zahl aus 0...9 oder im Euro-Lotto 2 Zahlen aus 1...11Da diese zusätzliche Bedingung offensichtlich leicht zu erfüllen ist und nach Erfahrung auch relativ oft erfüllt wird, schenkt man ihr nur wenig Beachtung. Tatsächlich wird damit jedoch die gesamte Wahrscheinlichkeit bedeutend verringert, weil man die beiden einzelnen Wahrscheinlichkeits-Werte multiplizieren muss. |
Beispiel: Wahrscheinlichkeit für den Haupttreffer im Euro-Lotto:
w1 = p_hypergeom(50;5;5;5) = 4.72E-7
Die Wahrscheinlichkeit, mindestens eine der 7 Zahlen zu erraten, ist mit ca. 77%
verlockend groß, jene für den Haupttreffer mit ca.
w2 = p_hypergeom(11;2;2;2) = 1.82E-2 w = w1 * w2 = 8.58E-9 1/w = 1:117 Millionen
ca. 14mal geringer als beim österreichischen Lotto '6 aus 45'.Ungefähr gleiche Chancen würde ein Lotto-System '6 aus 69' ergeben: w = p_hypergeom(69;6;6;6) = 8.34E-9
|
||||||||||||||||||||||||||||||||||||||||
|
Ertrags-Chancen
Die Wahrscheinlichkeit eines Gewinns ist für jede
Zahlen-Kombination in jedem seriösen Lotto-System gleich groß und
(mit der hypergeometrischen Verteilung) berechenbar.Die Ertrags-Chancen sind jedoch anders verteilt: Viele SpielerInnen setzen auf kleine Zahlen, z.B. auf Monate (1..12) oder Monats-Tage (1..31). Wer einige Zahlen >30 tippt, hat zwar die gleichen Gewinn-Chancen, muss jedoch einen allfälligen Gewinn mit viel kleinerer Wahrscheinlichkeit teilen als bei Tipps auf Zahlen <15 |
Aus diesem Grund findet man zwar Statistiken der (gleich verteilten) gezogenen Zahlen, jedoch nicht der (sehr ungleich verteilten) getippten Zahlen. Man kann diese Beobachtung relativ einfach testen: Wenn bei einer Lotto-Ziehung nur Zahlen <31 gezogen werden, dann gibt es fast immer einen, meist mehrere Haupttreffer. Wenn mehrere Zahlen >31 gezogen werden, dann gibt es fast nie einen Haupttreffer sondern einen 'Jackpot' für die nächste Lotto-Runde. Wikipedia: Hypergeometrische Verteilung (de, en), Lotto (de, en) Arndt Brünner: Hypergeometrische Verteilung (Live-Berechnung) |
||||||||||||||||||||||||||||||||||||||||