Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[Rozdział 21] Losowanie bez powtórzeń - skrócenie kodu

Ostatnio zmodyfikowano 2016-12-16 20:17
Autor Wiadomość
Luteres
Temat założony przez niniejszego użytkownika
[Rozdział 21] Losowanie bez powtórzeń - skrócenie kodu
» 2016-12-15 02:19:00
Dzień dobry!
Robię właśnie zadanie z kursu i trafiłem na takie polecenie.
"Na początek sformułujmy nasz problem: Program ma nam wylosować 5 liczb bez powtórzeń. Liczby wylosowane mają być z przedziału od 1 do 10. "

I wymyśliłem takie coś (póki co nie przeszedłem do dalszej części kursu, chciałem zrobić to zadanie samemu)

C/C++
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int losuj( int t[], int ile, int start, int stop )
{
    srand( time( NULL ) );
    do
    {
        ile--;
        t[ ile ] = rand() %( stop - start + 1 ) + start;
       
    }
    while( 0 < ile );
   
}

void usun( int t[], int ile, int start, int stop )
{
    srand( time( NULL ) );
    do
    {
        ile--;
        if( t[ ile ] == t[ ile - 1 ] || t[ ile ] == t[ ile - 2 ] || t[ ile ] == t[ ile - 3 ] || t[ ile ] == t[ ile - 4 ] )
        do
        {
            t[ ile ] = rand() %( stop - start + 1 ) + start;
        }
        while( t[ ile ] == t[ ile - 1 ] || t[ ile ] == t[ ile - 2 ] || t[ ile ] == t[ ile - 3 ] || t[ ile ] == t[ ile - 4 ] || t[ ile ] == t[ ile + 1 ] || t[ ile ] == t[ ile + 2 ] || t[ ile ] == t[ ile + 3 ] || t[ ile ] == t[ ile + 4 ] );
       
    }
    while( ile > 0 );
   
}


int main()
{
    int tablica[ 5 ];
    losuj( tablica, 5, 1, 10 );
    usun( tablica, 5, 1, 10 );
    cout << tablica[ 0 ] << endl;
    cout << tablica[ 1 ] << endl;
    cout << tablica[ 2 ] << endl;
    cout << tablica[ 3 ] << endl;
    cout << tablica[ 4 ] << endl;
    return 0;
}

Wydaje mi się, że to działa, ale (z racji tego że małe liczby były) niektóre rzeczy na początku zrobiłem "na piechotę" - mam na myśli szczególnie to:
while( t[ ile ] == t[ ile - 1 ] || t[ ile ] == t[ ile - 2 ] || t[ ile ] == t[ ile - 3 ] || t[ ile ] == t[ ile - 4 ] || t[ ile ] == t[ ile + 1 ] || t[ ile ] == t[ ile + 2 ] || t[ ile ] == t[ ile + 3 ] || t[ ile ] == t[ ile + 4 ] );

I nie mam pojęcia jak to zapisać za pomocą jakiegokolwiek wzoru. Liczę także na podpowiedzi jak skrócić ten kod (bazując na wiadomościach, które do tej pory przyswoiłem, jeszcze nie robiłem pętli for) - nie chcę gotowej odpowiedzi tylko małej wskazówki, nakierowania.

Oczywiście jeśli widać w tym kodzie jakieś rażące błędy to prosiłbym o ich wskazanie. Dzięki!
P-154958
czaffik
» 2016-12-15 14:56:24
No to przynajmniej bez tej pętli for niestety za wiele nie zdziałasz. Też będzie istniało teoretycznie niebezpieczeństwo że pętla sprawdzająca powtarzanie się warunków nigdy się nie skończy bo nowo wylosowana liczba się powtórzy etc, ale jak przedział losowanych liczb będzie większy od liczby losowanych liczb to raczej powinno się udać uniknąć tego kataklizmu.

Zapis:
C/C++
while( t[ ile ] == t[ ile - 1 ] || t[ ile ] == t[ ile - 2 ] || t[ ile ] == t[ ile - 3 ] || t[ ile ] == t[ ile - 4 ] || t[ ile ] == t[ ile + 1 ] || t[ ile ] == t[ ile + 2 ] || t[ ile ] == t[ ile + 3 ] || t[ ile ] == t[ ile + 4 ] );

jest w zasadzie nieobliczalny bo wychodzisz w nim poza skale tablicy a tam mogą być zapisane jakieś śmieci, np jeśli ile = 2 to t[2] = t[-2] - a co się znajduje pod t[-2]???
Musisz przerobić pętlę for i w niej porównywać dany element z tymi które już do tej pory wylosowałeś, bo nawet zresztą po co ci wzór do tego zapisu skoro nie możesz go w nic zwinąć.
P-154975
carlosmay
» 2016-12-15 14:57:19
nie chcę gotowej odpowiedzi tylko małej wskazówki, nakierowania.
Korzystaj z dobrodziejstw pętli.
do { } while();
 przecież znasz.
std::srand()
 wywołuj tylko raz i najlepiej na początku programu.

Kod podziel na więcej funkcji zajmujących się tylko jedną czynnością, np:
int losuj( int start, int stop );
 zwraca wylosowaną liczbę z zadanego przedziału i nic więcej.
bool czyByla( int tab[], int rozmiar, int sprawdzana );
 sprawdza spośród liczb w tablicy jest sprawdzana.
void wypelnijTablice( int tab[], int rozmiar );
 z użyciem pętli wypełnia tablicę losowanymi liczbami,
sprawdzając uprzednio, czy liczba jest unikatowa.
void wypiszTablice( int tab[], int rozmiar );
 wypisuje zawartość tablicy.

Czym ma zajmować się funkcja
void usun( int t[], int ile, int start, int stop );
?
Nie rozumiem przeznaczenia tej funkcji.
Dobrym pomysłem jest zwracanie wartości logicznej
bool
 niosącej informację czy usuwanie się powiodło.

edit:
@czafik -
No to przynajmniej bez tej pętli for niestety za wiele nie zdziałasz.
Przecież pętle są zamienne. Za pomocą każdej z nich można zapisać ten sam kod.
P-154976
czaffik
» 2016-12-15 15:44:08
@carlosmay - w sumie można i użyć do while, wystarczy dodać zmienną iterującą po elementach, jednak for wydaje się ładniejszy w tym przypadku.
P-154982
Luteres
Temat założony przez niniejszego użytkownika
» 2016-12-15 22:46:33
void usun( int t[], int ile, int start, int stop )

Ta funkcja w zamyśle miała nadpisywać powtarzające się wyniki dopóki nie przestaną się powtarzać.

Dzięki za wskazówki, spróbuję wprowadzić więcej funkcji.
P-155021
Luteres
Temat założony przez niniejszego użytkownika
» 2016-12-16 18:21:59
Ok, poprzednie ćwiczenie jakoś zrobiłem i nawet wychodziło, teraz spróbowałem kolejnego i coś nie gra.
Poprzednio dotarłem do momentu gdzie liczby się losowały, ale się powtarzały, teraz po pierwszym wylosowaniu program przestaje odpowiadać (domyślam się, że jakaś pętla się dzieje, która do niczego nie prowadzi, ale nie potrafię jej zlokalizować).

Zmodyfikuj program z pierwszego zadania tak,
aby użytkownik musiał podać 10 liczb, a 8 z nich będzie losowanych bez powtórzeń. Sprawdź czy wyniki są poprawne.

C/C++
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int losuj( int tablica[] )
{
    return tablica[( rand() % 10 ) + 0 ];
}

void wypelnijTablice( int t[] ) // do testowania program sam wypelnia tablice
{
    int i = 0;
    do
    {
        t[ i ] = i + 1;
        i++;
    }
    while( i < 10 );
   
}

bool czyByla( int wylosowane[], int wylosowanych, int liczba )
{
    int i = 0;
   
    if( wylosowanych <= 0 )
         return false;
   
    do
    {
        if( wylosowane[ i ] == liczba )
             return true;
       
        i++;
    }
    while( i < 8 );
   
    return false;
}



int main()
{
    srand( time( NULL ) );
    int tablica[ 9 ];
    int liczba;
    int wylosowanych = 0;
    int wylosowane[ 7 ];
   
    wypelnijTablice( tablica );
   
    do
    {
        liczba = losuj( tablica );
        if( czyByla( wylosowane, wylosowanych, liczba ) == false )
        {
            wylosowane[ wylosowanych ] = liczba;
            cout << "Wylosowana liczba: " << wylosowane[ wylosowanych ] << endl;
            wylosowanych++;
        }
    }
    while( wylosowanych < 8 );
   
   
    return 0;
}

EDIT:
Już wiem..

while( i < 8 )
 ->
while( i < 7 )
P-155060
carlosmay
» 2016-12-16 20:17:28
magic numbers używaj jak najmniej, a najlepiej wcale.
P-155062
« 1 »
  Strona 1 z 1