Problem z losowością liczb

Problem z losowością liczb
Jul Bed
  • Rejestracja:około 6 lat
  • Ostatnio:około 4 lata
  • Postów:21
0

Cześć wszystkim,

Napisałem program, który generuje losową liczbę z danego przedziału a potem sam ma odgadnąć tę liczbę. Są dwa problemy:

  • program losuje zawsze 0 jako pierwszą liczbę, pomimo warunku że pierwszą liczbą jest 1. Nie wiem z czego to wynika. Zakomentowany mam nawet test losowości. Stworzyłem go, żeby sprawdzić czy zero się wylosuje, co się nigdy nie stało.

  • pomimo że zakres zmniejszyłem do (strzał, MAX) lub (MIN, strzał) program wciąż losuj liczby spoza tego zakresu.

Problem jest w funkcjach? Wiem, że mogę dodać osobne if'y ale ze strony logicznej wszystko powinno działać (tak mi się zdaje).

Kopiuj
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>

using namespace std;

// Variables
int wynik, strzal, proby = 0; 
bool winner = false;

int losuj(int minimum, int maksimum){
    return rand() % (maksimum - minimum + 1) + minimum;
}

int zgaduj(int minimum, int maksimum){
    return rand() % (maksimum - minimum + 1) + minimum;
}

int main(){

    srand( time ( NULL ) );

    /*for (int i=0; i<110; i++){        TEST LOSOWOSCI
        cout << losuj(1,100) << endl;
    }
    */
    losuj:
    wynik = losuj(1,100);
    cout << "Wylosowana zostala liczba, sprobuj ja odgadnac. Powodzenia!" << endl;

    do { 
        int stzal = zgaduj(1,100);
        cout << strzal;

        zgaduj:
        if (strzal>wynik){
            cout << " - Za wysoko" << endl;
            strzal = zgaduj(1, strzal);
            cout << strzal;
            proby++;
            goto zgaduj;

        } else if (strzal<wynik){
            cout << " - Za nisko" << endl;
            strzal = zgaduj(strzal, 100);
            cout << strzal;
            proby++;
            goto zgaduj;

        } else if (strzal == wynik) {
            cout << " - Brawo! Wylosowana liczba: " << wynik << endl << "Liczba prob: " << proby << endl;
            winner = true;
        }

    } while(winner == false);
    
    return 0;
}

edytowany 3x, ostatnio: cerrato
lion137
  • Rejestracja:około 8 lat
  • Ostatnio:2 minuty
  • Postów:4927
0

U mnie, kompilowane g++-9, losuj działa poprawnie. Popatrz też:
https://stackoverflow.com/questions/7560114/random-number-c-in-some-range


edytowany 1x, ostatnio: lion137
Jul Bed
Czasami działa poprawnie a czasami potrafi zrobić 130 prób :D
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Szczecin
2

Ten kod jest okropny, zaoraj go i napisz od nowa. Jeśli piszesz w C++ to nie pisz w koślawym C i nie używaj goto do sterowania przebiegiem programu.

Twój bezpośredni problem jest taki, że masz zmienne strzał i stżał: strzał jest globalna (a więc jej pierwszą wartością jest 0), a stżał nie jest, ale nie jest też w ogóle używana. Swoją drogą, nie używaj zmiennych globalnych - są fuj.

PS: zauważyłeś, że masz dwie funkcje o identycznym ciele?

Dorzucę jeszcze https://dsp.krzaq.cc/post/180/nie-uzywaj-rand-cxx-ma-random/ ale samo zastosowanie się do tego wiele tutaj nie pomoże.


Bartosz Wójcik
Napisz petycję do WG21 żeby Ci usunęli goto i uzasadnij to swoim "bo tak" :)
kq
goto ma swoje zastosowania. Ale nie takie.
Jul Bed
  • Rejestracja:około 6 lat
  • Ostatnio:około 4 lata
  • Postów:21
0
kq napisał(a):

Ten kod jest okropny, zaoraj go i napisz od nowa. Jeśli piszesz w C++ to nie pisz w koślawym C i nie używaj goto do sterowania przebiegiem programu.

Twój bezpośredni problem jest taki, że masz zmienne strzał i stżał: strzał jest globalna (a więc jej pierwszą wartością jest 0), a stżał nie jest, ale nie jest też w ogóle używana. Swoją drogą, nie używaj zmiennych globalnych - są fuj.

PS: zauważyłeś, że masz dwie funkcje o identycznym ciele?

Dorzucę jeszcze https://dsp.krzaq.cc/post/180/nie-uzywaj-rand-cxx-ma-random/ ale samo zastosowanie się do tego wiele tutaj nie pomoże.

  • Jest okropny bo się uczę. Dla mnie najważniejsze obecnie jest zrozumienie dlaczego coś działa a coś nie działa. Z tą wiedzą mogę dopiero optymalizować kod.
  • Mam tylko jedną zmienną "strzal".
  • Czym mam w takim razie zastąpić zmienne globalne? Mam robić osobny plik ze zmiennymi prywatnymi na kod, który ma zaledwie 100 linijek? Powrót to pierwszego myślnika - najpierw zrozumienie, potem optymalizacja.
  • wiem, że mam dwie te same funkcje. Skopiowałem pierwszą funkcję bo inaczej nie wylosuje liczby 'losowej' tylko liczbę większą albo równą liczbie wylosowanej, ponieważ pobierana jest z zegara (tak mi sie wydaje) .
Delor
Przypatrz się dobrze: int stzal = zgaduj(1,100); I wydaje Ci się co do wylosowanej liczby. Zmienne lokalne nie potrzebują osobnych plików. Punktem pierwszym powinno być: "Od początku nie uczymy się złych nawyków. Potem trudno je wyplenić."
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Szczecin
1

Jest okropny bo się uczę.

Z jakiego kursu/książki korzystasz? Zmienne globalne, goto - jeśli są w nim pokazywane na początku - świadczą, że jest to słaby kurs. Szczególnie goto.

Mam tylko jedną zmienną "strzal".

Kopiuj
int wynik, strzal, proby = 0;
// ...
int stzal = zgaduj(1,100);

Czym mam w takim razie zastąpić zmienne globalne?

Zmiennymi lokalnymi w odpowiednich funkcjach. Dobrym nawykiem jest używanie zmiennej w najmniejszym możliwym zakresie (czyli w najbardziej zagłębionych klamrach)

inaczej nie wylosuje liczby 'losowej' tylko liczbę większą albo równą liczbie wylosowanej, ponieważ pobierana jest z zegara (tak mi sie wydaje)

Po to masz parametry funkcji aby decydować w jakim zakresie szukać. Jeśli funkcje są identyczne to będą się identycznie zachowywać.


Jul Bed
- goto używam bo nie wiem czego innego używać - tego nie ma w moim kursie, pamiętam to po prostu z basha xD. Próbowałem wywołania funkcji w innej funkcji ale kompilator wyświetla błąd na temat zdefiniowania zmiennych globalnych (coś w tym stylu). Oczywiście przestane tego używać ale na razie pisze małe programy i jest to dosyć wystarczające. - to zwykła literówka :D - Wiem, że jest to lepsze rozwiązanie ale jakoś niewygodne dla mnie przy małych programach. - Ale skoro elementy mają być losowe to nie powinny sie zachowywać różnie?
Jul Bed
  • Rejestracja:około 6 lat
  • Ostatnio:około 4 lata
  • Postów:21
0
kq napisał(a):

Jest okropny bo się uczę.

Z jakiego kursu/książki korzystasz? Zmienne globalne, goto - jeśli są w nim pokazywane na początku - świadczą, że jest to słaby kurs. Szczególnie goto.

Mam tylko jedną zmienną "strzal".

Kopiuj
int wynik, strzal, proby = 0;
// ...
int stzal = zgaduj(1,100);

Czym mam w takim razie zastąpić zmienne globalne?

Zmiennymi lokalnymi w odpowiednich funkcjach. Dobrym nawykiem jest używanie zmiennej w najmniejszym możliwym zakresie (czyli w najbardziej zagłębionych klamrach)

inaczej nie wylosuje liczby 'losowej' tylko liczbę większą albo równą liczbie wylosowanej, ponieważ pobierana jest z zegara (tak mi sie wydaje)

Po to masz parametry funkcji aby decydować w jakim zakresie szukać. Jeśli funkcje są identyczne to będą się identycznie zachowywać.

Poprawiłem literówkę i pierwszy problem zniknął. Drugi problem wciąż istnieje. Oto spis z konsoli po uruchomieniu:

Kopiuj
Wylosowana zostala liczba, sprobuj ja odgadnac. Powodzenia!
14 - Za nisko
92 - Za wysoko
6 - Za nisko
56 - Za nisko
77 - Za wysoko
75 - Za wysoko
60 - Za nisko
80 - Za wysoko
32 - Za nisko
54 - Za nisko
99 - Za wysoko
79 - Za wysoko
73 - Za wysoko
6 - Za nisko
53 - Za nisko
89 - Za wysoko
63 - Brawo! Wylosowana liczba: 63
Liczba prob: 16

Jak widać wartości skaczą, co jest dziwne. Any ideas?

kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Szczecin
1
Kopiuj
            strzal = zgaduj(1, strzal);
Kopiuj
            strzal = zgaduj(strzal, 100);

Losujesz [1,strzal) i [strzal, 100), musisz pamiętać krańce zakresu z obu stron i je sukcesywnie zmniejszać.

Zamiast goto masz pętle - nawet ich używasz...


Jul Bed
Ok, zrozumiałem właśnie czego dokonałem. Cały czas zmieniałem granice nie zmniejszając ich. Głupota ale na takich rzeczach człowiek się uczy :D
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Szczecin
0

Tu masz "na zachętę" poprawny kod implementujący podobną grę (gracz podaje, komputer zgaduje):

Kopiuj
#include <iostream>
#include <random>
// z https://dsp.krzaq.cc/post/180
thread_local std::mt19937 gen{std::random_device{}()};
 
template<typename T>
T random(T min, T max) {
    using dist = std::conditional_t<
        std::is_integral<T>::value,
        std::uniform_int_distribution<T>,
        std::uniform_real_distribution<T>
    >;
    return dist{min, max}(gen);
}

int main()
{
    static constexpr auto min_value = 1;
    static constexpr auto max_value = 1000;
    int secret;
    std::cout << "Wprowadz sekretna liczbe\n";
    std::cin >> secret;
    secret = std::clamp(min_value, secret, max_value);
    std::cout << "Sekretna liczba: " << secret << '\n';

    int min = min_value;
    int max = max_value;
    while(true) {
        int guess = random(min, max);
        std::cout << guess << " - ";
        if(guess < secret) {
            std::cout << "       ZA MALO\n";
            min = guess + 1;
        } else if(guess > secret) {
            std::cout << "ZA DUZO\n";
            max = guess - 1;
        } else {
            std::cout << "BRAWO!\n";
            break;
        }
    }
}

https://wandbox.org/permlink/caNvWWYd05qkARyX


Jul Bed
Dzięki. Również za pomoc w dojściu do odpowiedzi.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.