C# :: Aufgabe #349 :: Lösung #1

3 Lösungen Lösungen öffentlich
#349

Ermittlung der Anzahl fairer Würfel

Fortgeschrittener - C# von hollst - 03.02.2021 um 13:01 Uhr
Zwei Würfel A und B nennt man fair, wenn im statistischen Mittel Würfel A gegenüber B genauso oft gewinnt wie umgekehrt. Das ist bei den normalen Spielwürfeln, deren sechs Spielflächen jeweils mit den Zahlen von 1 bis 6 belegt sind, in der Regel der Fall, wenn kein Würfel gezinkt ist (sei hier vorausgesetzt).

Wir nehmen jetzt den Fall an, dass die insgesamt zwölf Spielflächen der zwei Würfel nicht zweimal mit jeweils von 1 bis 6, sondern jeweils mit sechs unterschiedlichen Zahlen aus dem Intervall von 1 bis 12 belegt sind. D. h. hat man sich bei Würfel A für sechs verschiedenen Zahlen aus 1 ... 12 entschieden, so wird B mit den verbleibenden sechs Zahlen belegt. Damit gibt es sehr viele Fälle, bei denen keine zwei faire Würfel entstehen würden, bspw. wenn Würfel A mit 1 ... 6 und Würfel B mit 7 ... 12 belegt wären. In diesem Beispiel gewänne Würfel B immer gegen Würfel A. Würde man allerdings Würfel A mit 1, 2, 3, 10, 11, 12 und Würfel B mit 4 ... 9 belegen, so hätte man wieder ein faires Würfelpaar. Neben der gerade genannten zwei Flächenbelegungen gibt es allerdings weitere, die ebenfalls fair sind.

Die Programmieraufgabe bestehe darin, mittels Simulation alle Flächenbelegungen für zwei Würfel mit den Zahlen von 1 bis 12 zu ermitteln, bei denen die Würfel fair sind.
#1
3 Kommentare
1x
vote_ok
von JKooP (18090 Punkte) - 09.02.2021 um 19:28 Uhr
NET 5.x; C# 8.x

Hier eine ineffiziente Simulation durch Zufallswerte.
Der Wert für die Durchläufe (100000) der Schleife hätte auch kleiner gewählt werden können,
da das Ergebnis ab ca. 2000 Durchläufen konstant bleibt.
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;

namespace CS_NET5_Aufgabe_349_Anzahl_Fairer_Wuerfel
{
    class Program
    {
        static void Main(string[] args)
        {
            var counter = 0;
            var dicUnique = new Dictionary<string, string>();
            var lstStringFirstSix = new List<string>();

            for (int i = 0; i < 100_000; i++)
            {
                var lstAll = Enumerable.Range(1, 12).OrderBy(x => Guid.NewGuid()).ToList();
                var lstFirstSix = lstAll.Take(6).OrderBy(x => x);
                var lstLastSix = lstAll.TakeLast(6).OrderBy(x => x);

                if (lstFirstSix.Sum() == lstLastSix.Sum()) // == 39
                {
                    var stringFirstSix = string.Join("-", lstFirstSix);
                    var stringLastSix = string.Join("-", lstLastSix);

                    if (!dicUnique.ContainsKey(stringFirstSix) && !dicUnique.ContainsValue(stringFirstSix))
                    {
                        dicUnique.Add(stringFirstSix, stringLastSix);
                        counter++;
                        lstStringFirstSix.Add(stringFirstSix);
                    }
                }
            }

            var lstFirstSixSorted = lstStringFirstSix.OrderBy(x => x);

            foreach (var l in lstFirstSixSorted)
                Console.WriteLine($"W1: [{l}]\tW2: [{dicUnique[l]}]");

            Console.WriteLine($"\nAnzahl fairer Würfelpaare: {counter}");
        }
    }
}

Kommentare:

hollst

Punkte: 13980

761 Aufgaben
132 Lösungen
117 Kommentare

#1
10.04.2021 um 20:15 Uhr
Also, mit
Quellcode ausblenden C#-Code
var lstAll = Enumerable.Range(1, 12).OrderBy(x => Guid.NewGuid()).ToList();

zufällige Sequenzen erzeugen ...

War mir neu, besten Dank für den Trick.
post_arrow
678 0

hollst

Punkte: 13980

761 Aufgaben
132 Lösungen
117 Kommentare

#2
15.04.2021 um 17:02 Uhr
Aber auch extrem langsam, wie mit folgendem Code testbar:

Quellcode ausblenden C#-Code
using System;
using static System.Console;
using System.Linq;
using System.Diagnostics;
using System.Text;

int n = 52;
int[] temp = Enumerable.Range(1, n).ToArray();
int[] input = new int[n];

temp.CopyTo(input, 0);
input.ArrayToMyString().Wl();

int imax = (int)1e+6;
Stopwatch sw = new();

int[] Result_Guid = new int[n];
int[] Result_Shuffle = new int[n];

sw.Start();
for (var i = 0; i < imax; i++)  {
    Result_Guid = n.lstAll();
}
sw.Stop();
sw.Elapsed.Dauer().Wl();

"Result_Guid.ArrayToMyString()".Wl();
Result_Guid.ArrayToMyString().Wl();

temp.CopyTo(input, 0);

Random r = new();
sw.Restart();
for (var i = 0; i < imax; i++)  {
    temp.CopyTo(input, 0);
    Result_Shuffle = input.Shuffle(r);
}
sw.Stop();
sw.Elapsed.Dauer().Wl();

"Result_Shuffle.ArrayToMyString()".Wl();
Result_Shuffle.ArrayToMyString().Wl();

"ready".Rk();

public static class Extensions  {
    public static void Wl(this string s) => WriteLine(s);

    public static void Rk(this string s)  {
        s.Wl(); ReadKey();
    }

    public static int[] lstAll(this int n) => Enumerable.Range(1, n).OrderBy(x => Guid.NewGuid()).ToArray();

    public static int[] Shuffle(this int[] x, Random r)  {
        int[] result = new int[x.Length];
        for (var i = 0; i < x.Length; i++)  {
            int temp = x[i];
            int index = r.Next(i, x.Length);
            result[i] = x[index];
            x[index] = temp;
        }
        return result;
    }

    public static string Dauer(this TimeSpan ts) =>
        String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
        ts.Hours, ts.Minutes, ts.Seconds,
        ts.Milliseconds / 10);

    public static string ArrayToMyString(this int[] a)  {
        StringBuilder sb = new();
        for (var iy = 0; iy < a.Length; iy++)
            sb.Append($"{a[iy].ToString(),4}");
        return sb.ToString();
    }
}
post_arrow
680 0

JKooP

Punkte: 18090

680 Aufgaben
227 Lösungen
19 Kommentare

#3
15.04.2021 um 19:28 Uhr
Das ist richtig. Ist hauptsächlich für kleine Datenmengen wie z.B. Lottogeneratoren gedacht.
Quellcode ausblenden C#-Code
Enumerable.Range(1, 49).OrderBy(x => Guid.NewGuid()).Take(6);

Hier spart man sich im Gegensatz zu Random die Prüfung, ob eine Zahl schon gezogen wurde.
Ich nutze gerne LINQ, aber es ist im direkten Vergleich einfach zu langsam.
Erst werden die Zahlen in eine Collection geschrieben, dann werden die Schlüssel erzeugt
und am Ende erfolgt der Sortiervorgang gemäß Schlüssel. Das kostet natürlich Zeit.
Bei C muss man für den Einzeiler schon ein wenig mehr schreiben :)


#include <time.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#define NUMBERS 6
#define LOTTERY_TYPE 49
void main() {
unsigned int arr[NUMBERS] = { 0, 0, 0, 0, 0, 0 };
srand(time(NULL));
unsigned int i = 0;
while (i < NUMBERS) {
bool t = false;
unsigned int r = rand() % LOTTERY_TYPE + 1;
for (unsigned int k = 0; k < NUMBERS; k++) {
if (arr[k] == r) {
t = true;
break;
}
}
if (t == true) continue;
arr[i++] = r;
printf("%d ", r);
}
}

post_arrow
683 0
Bitte melden Sie sich an um eine Kommentar zu schreiben.
Kommentar schreiben