C# :: Aufgabe #167

6 Lösungen Lösungen öffentlich

Der Leidensweg eines Betrunkenen durch einen Tunnel

Anfänger - C# von hollst - 07.03.2017 um 09:40 Uhr
Ein leicht angetrunkener Mann muss für seinen Nachhauseweg durch einen Tunnel der Länge L (z. B. = 10 m)
und der Breite B (z. B. = 5.5 m). Zum Glück ist der Tunnel mit quadratischen Terrazzoplatten ausgelegt, nach denen
er sich zu richten versucht. Die Platten haben eine Gräße von 0.5 x 0.5 m². Somit besteht der Weg aus hier z. B. 20 Reihen a 11 Platten.

Der Mann startet in der ersten Reihe auf der Mittelplatte. Er möchte durch den Tunnel gehen, indem er bei jedem
Schritt auf eine benachbarte Platte tritt. Leider hat er in seinem Zustand völlig die Richtungsorientierung verloren,
so dass sein Schritt rein zufällig in eine der acht möglichen Richtungen verläuf, unabhängig davon,
dass zwei Wände links und rechts den Weg versperren. Wenn der Mann gegen eine der Wände läuft, gilt sein Versuch
den Tunnes zu durchlaufen als gescheitert, da er bewußtlos zu Boden stürzt und liegen bleibt. Als gescheiterter Versuch gilt auch,
wenn sein Weg ihn nicht zum Tunnelausgang, sondern nach einigen Schritten oder schon bereits beim ersten zurück vor den Eingang führt.

Die Frage lautet: Wie groß ist die Wahrscheinlichkeit dafür, dass er den Tunnel mit einem einzigen Versuch schadlos durchquert?
Die Wahrscheinlichkeit (Erwartungswert) soll anhand genügend vielen Simulationen abgeschätzt werden.

Lösungen:

vote_ok
von daniel59 (2700 Punkte) - 09.03.2017 um 11:48 Uhr
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleBetrunkenDurchDenTunnel
{
    class Program
    {
        static readonly Random rnd = new Random();
        static void Main(string[] args)
        {
            double plateSize = 0.5;
            double length = 10;
            double width = 5.5;

            Console.Write("Wie lang ist der Tunnel? ");
            string s = Console.ReadLine();
            if (!double.TryParse(s, out length))
            {
                Console.WriteLine("\"{0}\" ist keine gültige Längenangabe", s);
                Console.ReadLine();
                return;
            }
            Console.Write("Wie breit ist der Tunnel? ");
            s = Console.ReadLine();
            if (!double.TryParse(s, out width))
            {
                Console.WriteLine("\"{0}\" ist keine gültige Längenangabe", s);
                Console.ReadLine();
                return;
            }
            Console.Write("Wie groß sind die quadratischen Platten? ");
            s = Console.ReadLine();
            if (!double.TryParse(s, out plateSize))
            {
                Console.WriteLine("\"{0}\" ist keine gültige Größenangabe", s);
                Console.ReadLine();
                return;
            }
            int trys = 10000000;
            Console.Write("Wie viele Versuche sollen gezählt werden? ");
            s = Console.ReadLine();
            if (int.TryParse(s, out trys))
            {
                if (trys <= 0)
                {
                    Console.WriteLine("Der Anzahl muss größer 0 sein");
                    Console.ReadLine();
                    return;
                }
            }
            else
            {
                Console.WriteLine("\"{0}\" ist keine gültige Angabe", s);
                Console.ReadLine();
                return;
            }

            double temp = length / plateSize;
            int platesY = temp % 1 == 0 ? (int)temp : (int)temp + 1;
            temp = width / plateSize;
            int platesX = temp % 1 == 0 ? (int)temp : (int)temp + 1;


            int success = 0;
            List<int> moves = new List<int>();
            for (int i = 0; i < trys; i++)
            {
                int m = 0;
                if (Simulate(platesX, platesY, out m))
                {
                    success++;
                    moves.Add(m);
                }
            }

            double p = (double)success / (double)trys;
            double avgMoves = moves.Average();

            Console.WriteLine("\nDie Wahrscheinlichkeit unbeschadet durch den Tunnel zu kommen beträgt {0}%", p * 100);
            Console.WriteLine("Durchschnittlich werden {0} Schritte benötigt", avgMoves, 2);
            Console.WriteLine("Min: {0}\tMax: {1}", moves.Min(), moves.Max());
            Console.ReadLine();
        }

        static bool Simulate(int platesX, int platesY, out int moves)
        {
            int posY = 0;
            int posX = platesX / 2;//Start in der Mitte
            int borderL = -1;
            int borderR = platesX;
            moves = 1;

            while (true)//posY < platesY && posX != borderL && posX != borderR)
            {
                switch (rnd.Next(0, 8))
                {
                    case 0://Geradeaus
                        posY++;
                        break;

                    case 1://Geradeaus links
                        posY++;
                        posX--;
                        break;

                    case 2://Geradeaus rechts
                        posY++;
                        posX++;
                        break;

                    case 3://links
                        posX--;
                        break;

                    case 4://rechts
                        posX++;
                        break;

                    case 5://Zurück
                        posY--;
                        break;

                    case 6://Zurück Links
                        posY--;
                        posX--;
                        break;

                    case 7://Zurück rechts
                        posY--;
                        posX++;
                        break;
                }
                if (posY == platesY)
                {
                    return true;
                }
                else if (posX == borderL || posX == borderR || posY < 0)
                {
                    return false;
                }
                moves++;
            }
        }
    }
}
1 Kommentar
2x
vote_ok
von Mexx (2190 Punkte) - 12.03.2017 um 23:29 Uhr
Die Wahrscheinlichkeit den Tunnel mit einem einzigen Versuch schadlos zu durchqueren liegt bei ~0,277%. Für die Berechnung wurden 100000 erfolgreiche Durchquerungen vorrausgesetzt.

Quellcode ausblenden C#-Code
using System;

namespace A167_LeidenswegEinesBetrunkenen
{
    class Program
    {
        const int benötigteErfolgreicheVersuche = 100000;
        const double tunnelLänge = 10;
        const double tunnelBreite = 5.5;
        const int plattenInLänge = (int)(tunnelLänge * 2);
        const int plattenInBreite = (int)(tunnelBreite * 2);
        static Random ran = new Random();
        static int posX;
        static int posY;
        static bool[,] raster = new bool[plattenInLänge + 2, plattenInBreite + 2];
        static bool erfolgreich = false;
        static long counter = 0;
        static int erfolgreicheVersuche = 0;

        static void Main(string[] args)
        {
            Console.WriteLine("Raster wird erstellt und Durchläufe ermittelt...\n");

            for (int i = 1; i <= plattenInLänge + 1; i++)
                for (int ii = 1; ii <= plattenInBreite; ii++)
                {
                    raster[i, ii] = true;
                }

            while (erfolgreicheVersuche < benötigteErfolgreicheVersuche)
            {
                posX = 1;
                posY = (int)(plattenInBreite / 2);
                counter++;

                while (posX < raster.Length - 1)
                {                    
                    int zufall = ran.Next(1, 5);
                    switch (zufall)
                    {
                        // Nach Rechts gehen
                        case 1:
                            posX += 1;
                            break;                            
                        // Nach Únten gehen
                        case 2:
                            posY -= 1;
                            break;
                        // Nach Links gehen
                        case 3:
                            posX -= 1;
                            break;
                        // Nach Oben gehen
                        case 4:
                            posY += 1;
                            break;
                        default:
                            break;
                    }

                    if (!raster[posX, posY])
                    {
                        break;
                    }
                    else if (posX == plattenInLänge + 1)
                    {
                        erfolgreich = true;
                        break;
                    }
                }

                if (erfolgreich)
                {
                    erfolgreich = false;
                    erfolgreicheVersuche++;
                }
            }

            Console.WriteLine();
            Console.WriteLine(string.Format("Für {0} erfolgreiche Durchquerungen wurden {1} Versuche benötigt. " + 
                "Das sind durchschnittlich {2} Versuche für eine erfolgreiche Durchquerung. Die Wahrscheinlichkeit " +
                "für eine erfolgreiche Durchquerung liegt damit bei ~{3}%", 
                new object[] { erfolgreicheVersuche, counter, counter / erfolgreicheVersuche, ((double)(100.0 / counter * erfolgreicheVersuche)).ToString("0.0000") }));

            Console.ReadKey();
        }
    }
}
3 Kommentare
2x
vote_ok
von hollst (6440 Punkte) - 13.03.2017 um 10:52 Uhr
Quellcode ausblenden C#-Code
using System;
using static System.Console;

namespace random_walker {
    class Program    {

        static void Main()        {

            int breite = 11, laenge = 20, max_trails = 100000;           
            bool bo_again = true;
            Random r = new Random();

            while(bo_again)            {
                int erfolgreich = 0;                
                for(var i = 0; i < max_trails; i++)                {
                    int x = 1 + breite / 2, y = 1;
                    bool bo_abbruch = false;
                    int steps = 0;
                    do                    {
                        int dir = r.Next(8);
                        steps++;
                        switch (dir)                        {
                            case 0: { y++;      break; }
                            case 1: { x--; y++; break; }
                            case 2: { x--; ;    break; }
                            case 3: { x--; y--; break; }
                            case 4: { y--;      break; }
                            case 5: { x++; y--; break; }
                            case 6: { x++;      break; }
                            case 7: { x++; y++; break; }
                        }
                        if (y == laenge + 1)
                            erfolgreich++;
                        bo_abbruch = (y == 0) || (x == 0) || (x == breite + 1) || (y == laenge + 1);
                    } while (!bo_abbruch);
                }
                ("erfolgreich: " + erfolgreich.ToString() + "  -> " + (100.0 * erfolgreich / max_trails).ToString("0.00") + " %").Message();

                "again? ESC exit".Message();
                ConsoleKeyInfo ki = ReadKey(true);
                bo_again = !(ki.Key == ConsoleKey.Escape);
            }
            "ready".EndMessage();
        }
    }

    public static class MyExtension    {
        public static void Message(this string s)
        { WriteLine(s); }

        public static void EndMessage(this string s)
        { s.Message(); ReadKey(); }
    }
}
3 Kommentare
vote_ok
von Rubbellos (70 Punkte) - 23.04.2017 um 15:21 Uhr
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Drunk
{
    class Program
    {
        static void Main(string[] args)
        {
            HashSet<int> Count = new HashSet<int>();
            WalkingGenerator DrunkMan = new WalkingGenerator();
            int Position = 3;
            int number = 0;
            Random Step = new Random();
            Console.WriteLine("Wieviel Durchläufe?");
            int Calcnumber = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine("Calculating");
            for (int j = 0; j < Calcnumber; j++)
            {
                while (DrunkMan.Check(Position))
                {
                    DrunkMan.Walk(ref Position, Step.Next(0, 8));
                    if (Position > 69)
                    {
                        Count.Add(number);
                        number += 1;
                    }
                }
                Position = 3;
            }
            float rate = (float)Count.Count / (Calcnumber/100);
            Console.WriteLine("Der Man hat eine Chance durchzukommen von {0}%", rate);
            Console.ReadLine();
        }
    }
    class WalkingGenerator
    {
        public void Walk(ref int Coord, int Step)
        {
                switch(Step) {
                    case 1:
                        Coord -= 1;
                        break;
                    case 2:
                        Coord += 1;
                        break;
                    case 3:
                        Coord += 7;
                        break;
                    case 4:
                        Coord -= 7;
                        break;
                    case 5:
                        Coord += 6;
                        break;
                    case 6:
                        Coord -= 6;
                        break;
                    case 7:
                        Coord += 8;
                        break;
                    case 0:
                        Coord -= 8;
                        break;
                }
        }
        public bool Check(int Coord)
        {
            if (Coord % 7 == 0 || Coord % 7 == 6 || Coord <= 0 || Coord >= 69)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
}
1 Kommentar
1x
vote_ok
von TomasWilson (150 Punkte) - 05.05.2017 um 17:55 Uhr
Meine Lösung kommt immer auf ~0.27%...
Quellcode ausblenden C#-Code
using System;

namespace DrunkenMan
{
    class Program
    {
        static void Main(string[] args)
        {
            DrunkenMan bob = new DrunkenMan();
            int wincount = 0;
            int playcount = 2500000;
            for(int i = 0; i < playcount; i++)
            {
                bob.reset();
                while (true)
                {
                    bob.randomStep();
                    if (bob.hasLost())
                    {
                        break;
                    }
                    if (bob.hasWon())
                    {
                        wincount++;
                        break;
                    }
                }
            }
            Console.WriteLine("Von {0} Versuchen, hat der Mann es {1} mal durch den Tunnel geschafft.",playcount,wincount);
            double perc = (double)wincount / (double)playcount * 100.0;
            Console.WriteLine("Dies entspricht einer Erfolgsquote von {0}%",perc);

            Console.ReadKey();
            
        }
    }

    class DrunkenMan
    {

        static Random rnd = new Random();
        int length = 1;
        int width = 6;

        public void randomStep()
        {
            switch (rnd.Next(1,9))
            {
                case 1:
                    length++;
                    width--;
                    break;
                case 2:
                    length++;
                    break;
                case 3:
                    length++;
                    width++;
                    break;
                case 4:
                    width--;
                    break;
                case 5:
                    width++;
                    break;
                case 6:
                    length--;
                    width--;
                    break;
                case 7:
                    length--;
                    break;
                case 8:
                    length--;
                    width++;
                    break;
            }
        }

        public void reset()
        {
            length = 1;
            width = 6;
        }

        public bool hasLost()
        {
            if (length < 1) return true;
            if(width < 1 || width > 11)
            {
                if (length <= 20) return true;
            }
            return false;
        }
        
        public bool hasWon()
        {
            if (length > 20) return true;
            return false;
        }
    }
}
vote_ok
von Pr0gr4mm3r (60 Punkte) - 02.06.2017 um 13:25 Uhr
Die aufgabe ist ja schon ein wenig älter aber hatte eine witzige idee das ganze besonders als übung fürmich ein wenig anschaulicher zu machen
habe das format natürlich etwas schrumpfen lassen und der code ist bestimmt nicht optimal aber funktioniert (bin sehr frisch im coding) habe auch selber dabei sehr viel neues kennen gelernt danke dafür ;) habe mein ergebnis so gut es geht auskommentiert und bilder von allen 3 "interessanten" ausgängen angehängt und hier ist der code:
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace drunkboy
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        bool tot = false;
        bool sieg = false;
        public MainWindow()
        {
            InitializeComponent();
        }
        //button der unseren Freund laufen lässt
        private void ComeB_Click(object sender, RoutedEventArgs e)
        {
            Random Rnd = new Random();
            //hier werden die richtungen jedes mal neu ausgewürfelt
            int Bewegungszahl = Rnd.Next(1, 9);
            

            int playercurrentrow = Grid.GetRow(Player);
            int playercurrentcolumn = Grid.GetColumn(Player);
            //ich überprüfe ob unser freund noch auf den beinen ist oder an seinem ziel angekommen
            if (tot == false && sieg == false)
            {
                //hier werden die bewegungen ausgeführt 1 bewegt nach oben links 
                if (Bewegungszahl == 1)
                {
                    Grid.SetColumn(Player, --playercurrentcolumn);
                    Grid.SetRow(Player, --playercurrentrow);
                }
                // 2 bewegt nach oben
                else if (Bewegungszahl == 2)
                {
                    Grid.SetRow(Player, --playercurrentrow);
                }
                // 3 oben rechts
                else if (Bewegungszahl == 3)
                {
                    Grid.SetColumn(Player, ++playercurrentcolumn);
                    Grid.SetRow(Player, --playercurrentrow);
                }
                else if (Bewegungszahl == 4)
                {
                    Grid.SetColumn(Player, ++playercurrentcolumn);
                }
                else if (Bewegungszahl == 5)
                {
                    Grid.SetColumn(Player, ++playercurrentcolumn);
                    Grid.SetRow(Player, ++playercurrentrow);
                }
                else if (Bewegungszahl == 6)
                {
                    Grid.SetRow(Player, ++playercurrentrow);
                }
                else if (Bewegungszahl == 7)
                {
                    Grid.SetColumn(Player, --playercurrentcolumn);
                    Grid.SetRow(Player, ++playercurrentrow);
                }
                //bis hier wo er letztendlich nach links geht
                else if (Bewegungszahl == 8)
                {
                    Grid.SetColumn(Player, --playercurrentcolumn);
                }
            }
            //wenn unser freund vor eine wand oder zum start läuft ist er "tot" in diesem fall ändert
            //sich sein aussehen und eine Textbox taucht auf die uns nochmal darauf hinweißt
            if (tot == true)
            {
                Textboxtot.Visibility = System.Windows.Visibility.Visible;
                Textboxtot.Text = "Ey man du bist Tot!!!";
            }
            //wenn unser freund es doch mal schaffen sollte und den tunnel durchquert taucht eine 
            //textbox auf und leider ist unser freund noch immer betrunken :P
            if (sieg == true)
            {
                Textboxtot.Visibility = System.Windows.Visibility.Visible;
                Textboxtot.Text = "Wo bin ich überhaupt?";
            }
            //hier überprüfe ich nur ob unser freund vor eine wand oder start läuft 
            if (playercurrentcolumn == 0)
            {
                Player.Source = new BitmapImage(new Uri(@"Drunk_smiley_tot.png", UriKind.Relative));
                tot = true;
            }
            else if (playercurrentrow == 0)
            {
                Player.Source = new BitmapImage(new Uri(@"Drunk_smiley_tot.png", UriKind.Relative));
                tot = true;
            }
            else if (playercurrentrow == 6)
            {
                Player.Source = new BitmapImage(new Uri(@"Drunk_smiley_tot.png", UriKind.Relative));
                tot = true;
            }
            //hier finden wir einfach nur die sieg bedingungen
            if (playercurrentcolumn == 6 && playercurrentrow == 1)
            {
                sieg = true;
            }
            if (playercurrentcolumn == 6 && playercurrentrow == 2)
            {
                sieg = true;
            }
            if (playercurrentcolumn == 6 && playercurrentrow == 3)
            {
                sieg = true;
            }
            if (playercurrentcolumn == 6 && playercurrentrow == 4)
            {
                sieg = true;
            }
            if (playercurrentcolumn == 6 && playercurrentrow == 5)
            {
                sieg = true;
            }
        }
        //dieser knopf startet einen neuen lauf und setzt alles wieder auf den standard zurück ;)
        private void NewGB_Click(object sender, RoutedEventArgs e)
        {
            Player.Source = new BitmapImage(new Uri(@"Drunk_smiley.png", UriKind.Relative));
            sieg = false;
            tot = false;
            Grid.SetColumn(Player, 1);
            Grid.SetRow(Player, 3);
            Textboxtot.Visibility = System.Windows.Visibility.Hidden;
        }
    }
}



es hat mir wahnsinnig viel spaß gemacht dies zusammen zu basteln hoffe ihr könnt auch etwas drüber lachen :P

PS: den XAML code habe ich mir mal gespart ist nicht wirklich spektakulär wenn dennoch gewünscht reiche ich ihn gern nach :)