C# :: Aufgabe #316
2 Lösungen

Abschätzung der Gewinnchancen eines KENO-Types
Anfänger - C#
von hollst
- 08.05.2020 um 22:39 Uhr
Zur Erinnerung, was ist KENO: Aus 70 Zahlen werden 20 Gewinnzahlen per Zufallsgenerator ermittelt.
Es gibt die KENO-Typen 2, 3 ... 10, d. h. man kann 2, 3 ... 10 Zahlen aus den Zahlen 1 ... 70 wählen.
Bei KENO-Typ N können 0, 1 ... N gewählte Zahlen in den 20 gelosten Gewinnzahlen enthalten sein
(je mehr, desto höher natürlich der Gewinn).
Die Programmieraufgabe bestehe darin, mittels stochastischer Simulation die Wahrscheinlichkeit dafür abzuschätzen,
wie hoch die Chance ist, bei KENO-Typ N, 0, 1, 2 ... oder gar N Richtige auf seinem Tippzettel zu haben?
Die exakten Werte könnt ihr u. a. z. B. auf
https://www.sachsenlotto.de/portal/spiele/keno/gewinnquoten.jsp
nachsehen.
Es gibt die KENO-Typen 2, 3 ... 10, d. h. man kann 2, 3 ... 10 Zahlen aus den Zahlen 1 ... 70 wählen.
Bei KENO-Typ N können 0, 1 ... N gewählte Zahlen in den 20 gelosten Gewinnzahlen enthalten sein
(je mehr, desto höher natürlich der Gewinn).
Die Programmieraufgabe bestehe darin, mittels stochastischer Simulation die Wahrscheinlichkeit dafür abzuschätzen,
wie hoch die Chance ist, bei KENO-Typ N, 0, 1, 2 ... oder gar N Richtige auf seinem Tippzettel zu haben?
Die exakten Werte könnt ihr u. a. z. B. auf
https://www.sachsenlotto.de/portal/spiele/keno/gewinnquoten.jsp
nachsehen.
Lösungen:
NET Core 3.x
C#-Code

using System; using System.Linq; namespace CS_Aufgabe_316_Keno { public enum KenoTypen { Typ2 = 2, Typ3, Typ4, Typ5, Typ6, Typ7, Typ8, Typ9, Typ10, } class Program { static void Main(string[] args) { const int AnzahlZiehungen = 1_000_000; const KenoTypen KenoTyp = KenoTypen.Typ8; var arrRichtige = Keno(KenoTyp, AnzahlZiehungen); Console.WriteLine($"KENO {KenoTyp}:\n"); for (int i = 0; i < arrRichtige.Length; i++) { Console.WriteLine($"{i,2}: {100 * arrRichtige[i] / (double)AnzahlZiehungen,8:F5} %"); } } static int[] Keno(KenoTypen kenoTyp, int anzahlZiehungen) { var anzahlGetippteZahlen = (int)kenoTyp; var arrRichtige = new int[anzahlGetippteZahlen + 1]; var listeGewinnzahlen = Enumerable.Range(1, 70).OrderBy(x => Guid.NewGuid()).Take(20); var listeGetippteZahlen = Enumerable.Range(1, 70).OrderBy(x => Guid.NewGuid()).Take(anzahlGetippteZahlen).ToList(); for (var i = 0; i < anzahlZiehungen; i++) { arrRichtige[listeGewinnzahlen.Intersect(listeGetippteZahlen).Count()]++; } return arrRichtige; } } }

#define nosimulation using System; using System.Linq; using System.Text; using static System.Console; using System.Diagnostics; using System.Numerics; namespace KenoConsole { /* Zur Erinnerung, was ist KENO: Aus 70 Zahlen werden 20 Gewinnzahlen per Zufallsgenerator ermittelt. Es gibt die KENO-Typen 2, 3 ... 10, d. h. man kann 2, 3 ... 10 Zahlen aus den Zahlen 1 ... 70 wählen. Bei KENO-Typ N können 0, 1 ... N gewählte Zahlen in den 20 gelosten Gewinnzahlen enthalten sein (je mehr, desto höher natürlich der Gewinn. Interessant ist bei KENO allerdings, dass es ab KENO-Typ 5 unwahrscheinlicher ist überhaupt keinen Treffer zu haben als z. B. einen oder zwei.). Die Programmieraufgabe bestehe darin, mittels stochastischer Simulation die Wahrscheinlichkeit dafür abzuschätzen, wie hoch die Chance ist, bei KENO-Typ N, 0, 1, 2 ... oder gar N Richtige auf seinem Tippzettel zu haben. Die exakten Werte könnt ihr u. a. z. B. auf https://www.sachsenlotto.de/portal/spiele/keno/gewinnquoten.jsp http://mathproblems.info/gam470/games/keno/prob-keno.html etwas anderes KENO, prizniopiell okay nachsehen. */ static class Program { static void Main() { Int64[][] erwartung // 1 : x laut https oben (0 == ohne Gewinnausschüttung) = new Int64[][] { // 0 1 2 3 4 5 6 7 8 9 10 new Int64[] { 0, 4 }, new Int64[] { 0, 0, 13 }, new Int64[] { 0, 0, 6, 48 }, new Int64[] { 0, 0, 4, 16, 189 }, new Int64[] { 0, 0, 0, 9, 50, 781 }, new Int64[] { 0, 0, 0, 6, 22, 169, 3383 }, new Int64[] { 0, 0, 0, 0, 13, 63, 619, 15464 }, new Int64[] {18, 0, 0, 0, 8, 31, 199, 2436, 74941 }, new Int64[] {26, 0, 0, 0, 0, 18, 86, 685, 10325, 387197 }, new Int64[] {39, 0, 0, 0, 0, 12, 44, 261, 2571, 47238, 2147181 } }; Int64[][] gewinn // laut https oben (0 == ohne Gewinnausschüttung) = new Int64[][] { // 0 1 2 3 4 5 6 7 8 9 10 new Int64[] { 0, 3 }, new Int64[] { 0, 0, 6}, new Int64[] { 0, 0, 1, 16 }, new Int64[] { 0, 0, 1, 2, 22 }, new Int64[] { 0, 0, 0, 2, 7, 100 }, new Int64[] { 0, 0, 0, 1, 2, 15, 500 }, new Int64[] { 0, 0, 0, 0, 1, 12, 100, 1000 }, new Int64[] { 1, 0, 0, 0, 1, 2, 15, 100, 10000 }, new Int64[] { 2, 0, 0, 0, 0, 2, 5, 20, 1000, 50000}, new Int64[] { 2, 0, 0, 0, 0, 2, 5, 15, 100, 1000, 100000 } }; #if !nosimulation //für Simulation CODE-ZEILE 1 zum Kommentar machen! Int64[][] KenoResults = new Int64[erwartung.Length][]; for (var i = 0; i < KenoResults.Length; i++) KenoResults[i] = new Int64[2 + i]; Random r = new Random(); Int64 anzahl = 0, max_anzahl = (Int64)1E+6; //besser (Int64)1E+9, aber Rechenzeit sicher > 1 Tag Stopwatch uhr = new Stopwatch(); "start simulation".MessageKey(); "simulation runs - have some patience".MessageLine(); uhr.Start(); while (anzahl < max_anzahl) { KenoClass KC = new KenoClass(r); for (var j = 0; j < KC.MyKenoResult.Length; j++) KenoResults[j][KC.MyKenoResult[j]] += 1; if (anzahl > 0 && anzahl % (Int64)1E+5 == 0) { uhr.Stop(); uhr.Elapsed.DurationString().MessageLine(); $"{anzahl.ToString("n0")} of {max_anzahl.ToString("n0")}".MessageLine(); KenoResults.Adjust(erwartung, anzahl, true).ArrayString().MessageLine(); KenoResults.Adjust(erwartung, anzahl, false).ArrayString().MessageLine(); uhr.Restart(); } anzahl++; } KenoResults.Adjust(erwartung, anzahl, true).ArrayString().MessageLine(); #endif "show exact solution".MessageKey(); int n = 10, m = 20, g = 70; //int n = 6, m = 6, g = 49; //int n = 15, m = 20, g = 80; NausMausG nmg = new NausMausG(n, m, g); nmg.Expectation.ArrayString().MessageLine(); for (BigInteger N = n; N > 1; N--) { WriteLine(); WriteLine($"N: {N,12}"); WriteLine(); BigInteger CombinationsSum = 0, ReturnSum = 0; for (BigInteger M = N; M >= 0; M--) { BigInteger Combinations = nmg.NoverK(m, M) * nmg.NoverK(g - m, N - M); BigInteger Return = 0; if ((N <= gewinn.Length) && (M < gewinn[(int)N - 1].Length)) Return = gewinn[(int)N - 1][(int)M]; ReturnSum += Return * Combinations; CombinationsSum += Combinations; string s1 = $"{nmg.Expectation[(int)(N - 1)][(int)M].ToString("n0"),15} "; string s2 = $"{Combinations.ToString("n0"),15} {Return.ToString("n0"),15} "; WriteLine(s1 + s2); } string s3 = $"CombinationsSum: {CombinationsSum.ToString("n0"),18} "; string s4 = $"ReturnSum: {ReturnSum.ToString("n0"),15} "; string s5 = $"{nmg.CommercialRounds(100 * ReturnSum, CombinationsSum)} %"; WriteLine(s3 + s4 + s5); } WriteLine("ready"); ReadKey(); } static void MessageLine(this string s) => WriteLine(s); static void Message(this string s) => Write(s); static void MessageKey(this string s) { ("by pressing a button " + s).MessageLine(); ReadKey(); } static string ArrayString(this Int64[][] a) { StringBuilder sb = new StringBuilder(); sb.AppendLine("stats"); for (var i = 0; i < a.Length; i++) { sb.Append($"{i + 1,2}: "); for (var j = 0; j < a[i].Length; j++) sb.Append($"{a[i][j],8} "); sb.AppendLine(); } sb.AppendLine(); return sb.ToString(); } static string ArrayString(this BigInteger[][] a) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); for (var i = 0; i < a.Length; i++) { sb.Append($"{i + 1,2}: "); for (var j = 0; j < a[i].Length; j++) sb.Append($"{a[i][j].ToString("n0"),8} "); sb.AppendLine(); } sb.AppendLine(); return sb.ToString(); } static Int64[][] Adjust(this Int64[][] a, Int64[][] ad, Int64 anzahl, bool bo_relativ) { if (anzahl == 0) anzahl = 1; Int64[][] result = new Int64[a.Length][]; for (var i = 0; i < result.Length; i++) { result[i] = new Int64[a[i].Length]; for (var j = 0; j < a[i].Length; j++) { if (bo_relativ) { if (ad[i][j] != 0) result[i][j] = 100 * ((Int64)Math.Round(1.0 * anzahl / a[i][j])) / ad[i][j]; } else if (a[i][j] > 0) result[i][j] = (Int64)Math.Round(1.0 * anzahl / a[i][j]); } } return result; } public static string DurationString(this TimeSpan ts) { string h = ts.Hours.ToString(); if (h.Length == 1) h = "0" + h; string m = ts.Minutes.ToString(); if (m.Length == 1) m = "0" + m; string s = ts.Seconds.ToString(); if (s.Length == 1) s = "0" + s; string ms = ts.Milliseconds.ToString(); while (ms.Length < 3) ms = "0" + ms; return $"Time elapsed: {h}:{m}:{s}.{ms}"; } } public class KenoClass { Int64[] WinningValues; Int64[][] MyKenoValues; public Int64[] MyKenoResult { private set; get; } Random r; public KenoClass(Random r) { this.r = r; this.WinningValues = new Int64[20]; for (var i = 0; i < WinningValues.Length; i++) { Int64 n = 0; while (n < this.WinningValues.Length) { Int64 value = r.Next(1, 71); if (!this.WinningValues.ToArray().Contains(value)) { this.WinningValues[n] = value; n++; } } } Array.Sort(this.WinningValues); this.MyKenoValues = new Int64[10][]; this.MyKenoResult = new Int64[this.MyKenoValues.Length]; for (var i = 0; i < this.MyKenoValues.Length; i++) { this.MyKenoValues[i] = new Int64[1 + i]; Int64 n = 0; while (n < this.MyKenoValues[i].Length) { Int64 value = r.Next(1, 71); if (!this.MyKenoValues[i].ToArray().Contains(value)) { this.MyKenoValues[i][n] = value; if (this.WinningValues.ToArray().Contains(value)) MyKenoResult[i]++; n++; } } Array.Sort(this.MyKenoValues[i]); } } public string KenoString() { StringBuilder sb = new StringBuilder(); sb.AppendLine("WinningValues"); for (var i = 0; i < WinningValues.Length; i++) sb.Append($"{WinningValues[i],2} "); sb.AppendLine(); sb.AppendLine("MyKenoVelues"); for (var i = 0; i < MyKenoValues.Length; i++) { sb.Append($"{i + 1,2}: "); for (var j = 0; j < MyKenoValues[i].Length; j++) sb.Append($"{MyKenoValues[i][j],2} "); sb.Append($" winnings: {this.MyKenoResult[i]}"); sb.AppendLine(); } sb.AppendLine(); return sb.ToString(); } } //Die exakte Lösung (public class NausMausG ) ist ein Extra public class NausMausG //Keno wird mitunter auch bezeichnet als 10aus20aus70 (in EU) oder 15aus20aus80 in US { int N, M, G; public BigInteger[][] Expectation; // 1 zu x public NausMausG(int n, int m, int g) { this.N = n; this.M = m; this.G = g; this.Expectation = new BigInteger[n][]; for (var i = 0; i < this.Expectation.Length; i++) this.Expectation[i] = new BigInteger[i + 2]; run(); } void run() { for (var i = 0; i < this.Expectation.Length; i++) for (var j = 0; j < this.Expectation[i].Length; j++) this.Expectation[i][j] = KenoChance(i + 1, j, G, M); } BigInteger Fak(BigInteger f) { BigInteger result = 1; for (BigInteger i = 2; i <= f; i++) result *= i; return result; } public BigInteger NoverK(BigInteger N, BigInteger K) => (Fak(N) / (Fak(K) * Fak(N - K))); BigInteger KenoChance(BigInteger N, BigInteger K, BigInteger All, BigInteger Winner) { BigInteger Nenner = NoverK(All, Winner); BigInteger Zaehler = NoverK(N, K) * NoverK(All - N, Winner - K); return CommercialRounds(Nenner, Zaehler); } //kaufmännische Runden basiert auf DIN-Norm 1333 public BigInteger CommercialRounds(BigInteger Z, BigInteger N) { BigInteger result = Z / N; if ((10 * (Z % N)) / N >= 5) result += 1; return result; } } }