Random.Next inaczej działa w debugerze a inaczej w programie

0

Przerabiam przykład z książki. na podstawie tego co jest napisane w książce, stworzyłem program, który:

-tworzy instancję klasy zawierającej kilka tablic, każda składa się z kilku elementów
-tworzy instancję klasy Random i za jej pomocą wybiera losowy element z każdej tablicy
-przekazuje wylosowany element z każdej tablicy do stringa, w którym są one dodawane i wyświetlane w etykiecie
-etykiet jest kilka, zatem po uruchomieniu programu mam kilka zestawów losowo dobranych elementów

Program działa znakomicie i zgodnie z założeniami. Tyle, że nie rozumiem, po co tworzona jest instancja klasy zawierającej te tablice i chciałem sprawdzić, czy da się pominąć tę instrukcję (ponieważ uważam, że jest zbędna). Napisałem program i... w każdej etykiecie pojawiają mi się prawie za każdym razem takie same wartości, w sensie:

etykieta 1: aabbc
etykieta 2: aabbc
etykieta 3: aabbc
etykieta 4: aabbc

Wartości te zmieniają się np. na cbcab, ale w każdej etykiecie prawie zawsze się pokrywają. Czasem zadziała tak, że jedna z etykiet jest inna od reszty, ale zdarza się to rzadko. Ponieważ nie mogłem zrozumieć skąd się to bierze, to przepuściłem program przez debuggera i okazało się, że w debuggerze program działa prawidłowo! I tak jest za każdym razem. Program zachowuje się tak, jakby Random nie nadążał ze zmianą swoich wartości i przez to liczba odpowiedzialna za losowanie była taka sama dla każdego losowania.

To jest poprawnie działający kod tworzący instancję klasy MenuMaker (wg mnie niepotrzebnie)

kod klasy zawierającej tablice i metodę odpowiedzialną za losowanie

    class MenuMaker
    {
        public Random Randomizer = new Random();

        string[] Meats = { "Pieczona wołowina", "Salami", "Indyk", "Szynka", "Karkówka" };
        string[] Condiments = {"żółta musztarda", "przyprawa", "brązowa musztarda",
                                "musztarda miodowa", "majonez", "sos francuski"};
        string[] Breads = {"chleb ryżowy", "chleb biały", "chleb zbożowy", "pumpernikiel",
                            "chleb włoski", "bułka"};

        public string GetMenuItem()
        {
            string randomMeat = Meats[Randomizer.Next(Meats.Length)];
            string randomCondiments = Condiments[Randomizer.Next(Condiments.Length)];
            string randomBreads = Breads[Randomizer.Next(Breads.Length)];

            return randomMeat + ", " + randomCondiments + ", " + randomBreads + ".";
        }

     }

-to jest kod podpięty do przycisku tworzący zawartość etykiet

        private void button1_Click(object sender, EventArgs e)
        {
            MenuMaker menu = new MenuMaker();

            label1.Text = menu.GetMenuItem();
            label2.Text = menu.GetMenuItem();
            label3.Text = menu.GetMenuItem();
            label4.Text = menu.GetMenuItem();
            label5.Text = menu.GetMenuItem();
            label6.Text = menu.GetMenuItem();
        }
 

A to kod programu, który nie działa prawidłowo (poza debuggerem) - ma tę samą klasę MenuMaker, różni się tylko okodowaniem przycisku

private void button1_Click(object sender, EventArgs e)
        {
            label1.Text = MenuMaker.GetMenuItem();
            label2.Text = MenuMaker.GetMenuItem();
            label3.Text = MenuMaker.GetMenuItem();
            label4.Text = MenuMaker.GetMenuItem();
            label5.Text = MenuMaker.GetMenuItem();
            label6.Text = MenuMaker.GetMenuItem();

        } 
1

wywołując "new Random()" tworzysz nowy generator liczb pseudolosowych przyjmujący jako ziarno aktualną godzinę
tzn. że wszystkie "generatory" utworzone w tej samej sekundzie będą kolejno generować te same liczby
resztę sobie dopowiedz

1

no to już jest spore naprowadzenie, tylko dlaczego program zachowuje się poprawnie kiedy tworzę instancję klasy MenuMaker? instancja jest utworzona raz, a potem dzieje się (wg mnie) dokładnie to samo co w drugim przypadku - tzn. wiem, że nie dzieje się, skoro mam różne wyniki, ale nie rozumiem dlaczego... czy to chodzi o to, że w związku z utworzeniem instancji klasy dochodzą jakieś dodatkowe kroki, które wydłużają czas przetwarzania instrukcji, co sprawia, że czas ma większą szansę się zmienić?

tworząc instancję klasy, tworzysz generator liczb RAZ i go wykorzystujesz ponownie. Nie mogę napisać czemu Twój kod nie działa bo go nie załączyłeś, ale prawdopodobnie tworzysz nowy obiekt typu Random za każdym razem

0

Pokaż jak wygląda GetMenuItem() po przerobieniu na metodę statyczną, bo tam tam prawdopodobnie masz błąd.

0

ok, wklejam pełny kod

Program działający poprawnie
Plik Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Niechlujny_Janek
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MenuMaker menu = new MenuMaker();

            label1.Text = menu.GetMenuItem();
            label2.Text = menu.GetMenuItem();
            label3.Text = menu.GetMenuItem();
            label4.Text = menu.GetMenuItem();
            label5.Text = menu.GetMenuItem();
            label6.Text = menu.GetMenuItem();
        }
    }
}

i jego klasa

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Niechlujny_Janek
{
    class MenuMaker
    {
        public Random Randomizer = new Random();

        string[] Meats = { "Pieczona wołowina", "Salami", "Indyk", "Szynka", "Karkówka" };
        string[] Condiments = {"żółta musztarda", "przyprawa", "brązowa musztarda",
                                "musztarda miodowa", "majonez", "sos francuski"};
        string[] Breads = {"chleb ryżowy", "chleb biały", "chleb zbożowy", "pumpernikiel",
                            "chleb włoski", "bułka"};

        public string GetMenuItem()
        {
            string randomMeat = Meats[Randomizer.Next(Meats.Length)];
            string randomCondiments = Condiments[Randomizer.Next(Condiments.Length)];
            string randomBreads = Breads[Randomizer.Next(Breads.Length)];

            return randomMeat + ", " + randomCondiments + ", " + randomBreads + ".";
        }

    }
}

A to program działający niepoprawnie

Plik Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace paplacz
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            label3.Text = Talker.TworzMenuInator();
            label4.Text = Talker.TworzMenuInator();
            label5.Text = Talker.TworzMenuInator();

        }
    }
}
 

I jego klasa

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace paplacz
{
    class Talker
    {
        public static string TworzMenuInator()
        {
            Random Randomizer = new Random();

            string[] Meats = { "Pieczona wołowina", "Salami", "Indyk", "Szynka", "Karkówka" };
            string[] Condiments = {"żółta musztarda", "przyprawa", "brązowa musztarda",
                                "musztarda miodowa", "majonez", "sos francuski"};
            string[] Breads = {"chleb ryżowy", "chleb biały", "chleb zbożowy", "pumpernikiel",
                            "chleb włoski", "bułka"};

            string randomMeat = Meats[Randomizer.Next(Meats.Length)];
            string randomCondiments = Condiments[Randomizer.Next(Condiments.Length)];
            string randomBreads = Breads[Randomizer.Next(Breads.Length)];

            return randomMeat + ", " + randomCondiments + ", " + randomBreads + ".";

        }
    }
}
1

No to odpowiedź podał @KrzywyPomidor.
Przy każdym wywołaniu metody tworzysz nową instancjęRandom, a dzieje się to tak szybko, że każdy Random dostaje taką samą wartość jako seed i zwraca takie same wartości.

0

dzięki wszystkim za szybką odpowiedź. zaczaiłem na czym polegał błąd w moim rozumowaniu. sądziłem, że random tworzy JEDNĄ liczbę z zakresu 0...1 (jakoś tak zapamiętałem z czasów, kiedy uczyłem się pisać programy na ATARI) i ta liczba jest używana do wybierania losowo elementów z podanego zakresu - a to nie pasowało mi do tego, że poprawne rozwiązanie polegające na stworzeniu tylko jednej instancji ma dawać różne wyniki (no niemożliwe, aby raz wylosowana liczba x mnożona przez te same liczby dawała różne wyniki). poszperałem po sieci o zasadzie działania klasy Random i doczytałem, że tworzona jest nie jedna liczba, a cała tablica liczb, z których kolejne wartości są pobierane za pomocą Random.Next().

Dla dociekliwych: potrzeba około 10ms przerwy pomiędzy kolejnymi wywołaniami funkcji Random, aby stworzone zostały różne tablice.

1 użytkowników online, w tym zalogowanych: 0, gości: 1