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

[C++] Program wyszukujący niepowtarzające się liczby z danego zbioru

Ostatnio zmodyfikowano 2017-01-01 19:33
Autor Wiadomość
Jekon
Temat założony przez niniejszego użytkownika
Sukces!
» 2016-12-30 19:06:00
Bardzo dziękuję michalowi11 oraz carlosmay'owi za pomoc, jednak jestem jeszcze zielony i niestety niezbyt rozumiem zaproponowane przez Was programy.

Natomiast muszę OGROMNIE podziękować latajacejrybie za pomoc i wyczerpujące tłumaczenie, dzięki któremu wszystko zrozumiałem i rozwiązałem swój problem - program działa tak jak powinien. :)

Mam jednak kilka "technicznych" pytanek apropo Twojego kodu:

a) instrukcję
break;
 kojarzę głownie z funkcji (jeżeli to nią jest)
switch() { }
, a dokładniej z ostatniego warunku
default: /*...*/ break;
. I tu pojawia się moje pytanie: czy w pętli for pełni ona tak jak w switch'u tylko funkcję zaznaczenia końca "default'a" i jest tylko czymś kosmetycznym, czy może działa tak, że w przypadku "dojechania" do niej, natychmiastowo kończy pętle?

b) z ciekawości sprawdziłem działanie programu, gdyby neutralną wartością moich "bool'ów" było
true;
 (1) - program wypisywał by dobre liczby
if( z[ i ] == true ) { }
, a powtórzenie liczby skutkowałoby
z[ i ] = false;
. Odziwo program nie działał poprawnie, wyświetlał dziwne liczby lub wcale nic nie wyświetlał. Czym jest to spowodowane? Przecież "logicznie" nic to nie zmienia: 0=1, a 1=0. Równość jest zachowana.

Będę bardzo wdzięczny za wytłumaczenie! :)

Jeszcze raz dziękuję Ci za pomoc!
P-155711
latajacaryba
» 2016-12-30 22:08:15
Kojarzysz, bo faktycznie najczęściej właśnie tam występuje:
C/C++
switch( liczba )
case
: 1 cout << "liczba nr 1" << endl;
break;;
case: 2
cout << "liczba nr 2" << endl;
break;
Gdyby nie break, to dla zmiennej liczba = 1 program wyglądałby tak:

liczba nr 1
liczba nr 2
break przerywa dalsze wykonywanie case'ów. Ale nie tylko. W pętli dzięki break możemy natychmiast z niej wyjść
C/C++
int licznik = 0
while( 1 ) // 1
     licznik++;

if( licznik == 10 )
     break;

Jeśli zmienna licznik będzie równała się 10 to przerywamy pętle.
Natomiast ciekawostka:
1. zauważyłeś, że w warunku wykonywania się pętli jest po prostu cyfra 1? Jesli wiesz dlaczego pomiń aż do końca wcięć w tekscie.
------------------------------------------------------------------
    W C++ cyfry 1 i 0 służą również do określania nam, czy coś jest prawdą
true
, czy fałszem
false
. Tak jest np. przy zmiennej bool (to akurat wiesz :p )
   
C/C++
bool prawda = 1;
bool falsz = 0;
     I na tym również opierają się pętle oraz if'y. Przykład:
   
C/C++
int a;
if( a == 5 )
     Przetłumaczmy to sobie na ludzki ;)
    Jeśli prawdą jest, że zmienna a jest równa 5 to...
   
C/C++
while( a == 5 )
     Wykonuj pętle dopóki prawdą jest, że a jest równe 5.
    Możemy więc w uproszczeniu powiedzieć, że if'y i pętle opierają się na warunku, którego wartością jest prawda lub fałsz. A skoro '1' oznacza prawdę, to taki warunek możemy tam zawrzeć.
------------------------------------------------------------------
default
 określa co się stanie jeśli mamy taki program
C/C++
int a;
switch( a )
case 1
: cout << "a = 1";
break;
default:
cout << "liczba inna od 1";
break;
I a będzie równało się na przykład 4, czy -1. Możesz to nazwać "w każdym innym wypadku".
I tu pojawia się moje pytanie: czy w pętli for pełni ona tak jak w switch'u tylko funkcję zaznaczenia końca "default'a" i jest tylko czymś kosmetycznym, czy może działa tak, że w przypadku "dojechania" do niej, natychmiastowo kończy pętle?
Dokładnie. W przypadku dojechania do instrukcji
break;
 kończy się pętle. Ale tylko tą "najbliższą" czyli tutaj:
C/C++
while( 1 ) // petla nr1
{
    while( 1 ) //petla nr2
    {
        break;
    }
}
Zakończymy tylko pętle nr2
Co do
b) z ciekawości sprawdziłem działanie programu, gdyby neutralną wartością moich "bool'ów" było true;
 (1) - program wypisywał by dobre liczby if( z[ i ] == true ) { }
, a powtórzenie liczby skutkowałoby z[ i ] = false;
. Odziwo program nie działał poprawnie, wyświetlał dziwne liczby lub wcale nic nie wyświetlał. Czym jest to spowodowane? Przecież "logicznie" nic to nie zmienia: 0=1, a 1=0. Równość jest zachowana.
Jeśli wiesz, jak dokładnie działa nasz program - pomiń do wcięcia
-------------------------------------------------------------------------------
Program działał tak: każdy indeks w tablicy 'z' odpowiadał indeksowi o tym samym numerze w tablicy 'x'. z[6] odpowiada x[6] itd.
Na początku każdy element z miał wartość false; Jeśli liczba się powtarzała w elemencie tablicy 'x', to w odpowiadającemu mu elemencie tablicy 'z' wartość zmieniała się na true (można to uznać jako "prawda,ta liczba się powtarza się")
Na końcu, jeśli dany element tablicy 'z' miał wartość false (czyli fałsz, nie powtarza się), to wyświetlamy odpowiadający mu element tablicy 'x'
Przykład tylko na 4 liczbach
Użytkownik podaje liczby:
x[0] = 1 //odpowiadający element tablicy z - z[0]
x[1] = 2 // z[1]
x[2] = 1 // z[2]
x[3] = 3 // z[3]
Program sprawdza:
x[0]:
- nie równa się x[1] (nie sprawdzi nam czy x[0] == x[0] dzięki naszemu wcześniej omawianemu w poprzednim moim poście sprytnemu warunkowi) - z[0] pozostaje nadal false (fałsz, nie powtarza się)
- równa się! x[0] == x[2] - z[0] zmienia się na true
dalej nie sprawdzamy (break) bo wiemy, że się powtarza, więc po co sprawdzać dalej?
x[1]:
tutaj czysto, żadna liczba się nie powtarza (czyli z[1] = false)
x[2]:
- oho... x[2] == x[0], czyli zmieniamy z[2] na true(prawda, powtarza się),dalej nie sprawdzamy (break), bo po co?
x[3]
- tu czysto (z[3] nadal pozostaje false)
i nasz ukochany wypisujący if
C/C++
if( z[ a ] == false ) UWAGA - w kodzie zamiast 'a' jest 'i', ale musia ł em zmieni ć bo mi tekst formatowa ł o kiedy dawa ł em 'i' w nawiasy kwadratowe
     cout << "ta liczba sie nie powtarza: " << x[ a ] << endl;

-------------------------------------------------------------------------------
A teraz właściwa odpowiedź
C/C++
bool z[ 10 ] = { 0 };
takie coś oznacza: niech pierwszy element tablicy bool będzie miał wartość false.
Kompilator musiał coś wstawić do tej tablicy - uznał, że powstawia sobie wszędzie false. Jeśli chcesz mieć wszędzie true, to zrób tak
C/C++
bool z[ 10 ] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
Teraz, dla każdego elementu masz wartość true.
Ale powiem Ci, że sam kiedy pisałem poprzedni kod nie zwróciłem na to uwagi :D
Coś jest nie jasne - pisz, z chęcią pomogę :)
P-155718
carlosmay
» 2016-12-31 08:33:39
C/C++
#include <iostream>
#include <algorithm>
int main()
{
    std::cout << std::boolalpha;
    bool flags[ 10 ] { }; // false'owanie tablicy
    for( bool f: flags ) {
        std::cout << f << ' ';
    }
   
    std::fill( flags, flags + 10, true ); // wypełnienie tablicy true'ami
    for( bool f: flags ) {
        std::cout << f << ' ';
    }
}
Dużo lepiej czyta się kod, gdy stosuje się nazwane wartości logiczne
true
 oraz
false
.
Magiczna
1
 może oznaczać wszystko, więc jej znaczenia trzeba szukać w kodzie,
natomiast wartość logiczna od razu nam mówi z czym mamy do czynienia.
P-155725
Jekon
Temat założony przez niniejszego użytkownika
Ciąg dalszy
» 2016-12-31 17:57:49
Rozumiem to bardziej, lecz jeszcze mam kilka wątpliwości:

a)
default;
 jest już dla mnie jasny, ale chcę się upewnić czy dobrze rozumiem Twoją pętle
while( 1 ) { }
. Znaczy to, że pętle będzie wykonywana w nieskończoność (prawda zawsze będzie prawdą). Co każdy cykl do licznika dodajemy jeden -
licznik++;
. I teraz warunek
if( licznik == 10 ) { break; }
 - co znaczy, że po prostu gdy zmienna "licznik" będzie równa 10 to kończy pętle. Rozumiem to jako takie podrobienie pętli
for() { }
, gdzie zamiast środkowego argumentu (tego "rób dopóki") mamy kontrargument, który gdy zostanie spełniony kończy pętle. Trzeci argument - inkrementacja ++ jest po prostu zapisany jako zwykła komenda. Natomiast pierwszy argument jest niepotrzebny bo prawda niejako jest stworzona "od zawsze". Tak to rozumiem, ale czy dobrze? :D

b) nawiązując do pierwszego pytania, czyli gdyby było:
while( 0 ) { }
 pętla w ogóle by się nie wykonała? Mam tu pewien konflikt: raz tłumaczę to sobie "wykonuj coś tam dopóki fałsz będzie fałszem" - zawsze, a raz "wykonuj coś tam dopóki (fałsz) będzie prawdą" - nigdy. Bardziej jednak skłaniam się ku drugiej filozofii - jednak tu też prosiłbym Cię o potwierdzenie. :D

c) wracając do zer i jedynek oraz do tablic, uczono mnie że przy tworzeniu tablic np.
tablica[ 10 ]
 możemy do nich dopisać
= { x }
 gdzie "x" będzie wartością neutralną - wspólną początkową wartością dla WSZYSTKICH "x-ów" w tej tablicy. Czyli tak nie jest? Jest to tylko wartość początkowa dla 1. (0) tablicy? Dlaczego zatem w tym działającym programie gdzie mamy
bool z[ 10 ] = { 0 }
 jakoś wszystkie te zmienne logiczne mają wartość 0 (false)? :D Chyba że, w C++ każda stworzona nowa obojętnie jakiego typu zmienna jest jest od początku "niczym" - zerem. Jednak z własnej percepcji oraz z opowieści innych słyszałem, że początkowo program kojarzy je z jakimiś śmieciami w pamięci (liczby typu 84272932 itp.). Co jest tu prawdą?

d) takie małe czysto kosmetyczne pytanie - rozumiem zatem, że w C++ mogę zamiennie korzystać z false=0 i true=1 w przypadku wartości logicznej
bool
? Jeżeli tak to czy jest możliwe aby zapisać wartość 1 dla "x-a", który jest zmienną typu "int" tak:
x = true;
?

To na ten moment wszystkie moje wątpliwości. Mam nadzieję, że nie zamęczam Cię moimi pytaniami, ale chyba zawsze jak się cokolwiek zaczyna to ma się tysiące dociekliwych pytań. ;)
P-155747
carlosmay
» 2016-12-31 19:50:45
C/C++
#include <iostream>

bool is_single_num( int arr[], int size, int checked )
{
    unsigned count = 0;
    for( int i = 0; i < size; ++i ) {
        if( checked == arr[ i ] ) {
            ++count;
        }
    }
    return count == 1 ? true: false;
}

int main()
{
    const int size = 10;
    int arr[ size ] { 1, 2, 4, 2, 6, 5, 1, 7, 11, 4 };
    for( int i = 0; i < size; ++i ) {
        if( is_single_num( arr, size, arr[ i ] ) ) {
            std::cout << arr[ i ] << '\n';
        }
    }
}
To jest chyba najprostszy sposób.

Najlepszego w nowym roku.
P-155749
latajacaryba
» 2016-12-31 20:41:47
              a)
Rozumiem to jako takie podrobienie pętli for
C/C++
int licznik = 0;
while( 1 )
{
    licznik++;
    if( licznik == 10 )
         break;
   
}
Tak, coś takiego możesz porównać do podróbki pętli for. Założeniem tego przykładu było pokazać jak działa break, ale faktycznie jest to coś jak
for( int licznik = 0; licznik < 10; licznik++ )
 - licznik równy zero, wykonujemy dopóki licznik jest mniejszy lub równy 10 (w przykładzie z if'em: przerwij (break;) kiedy licznik jest równy 10)
Reasumując - jest tak jak mówisz. Pętla while(1) będzie się wykonywać w nieskończoność. Ale w jej ciele jest if, który wywołuje instrukcje break która przerywa pętle.
jako zwykła komenda
 mnie uczono na forum, żeby mówić "instrukcja", ale to tylko pierdółka.
             b)

nawiązując do pierwszego pytania, czyli gdyby było: while( 0 ) { }
 pętla w ogóle by się nie wykonała? Mam tu pewien konflikt: raz tłumaczę to sobie "wykonuj coś tam dopóki fałsz będzie fałszem" - zawsze, a raz "wykonuj coś tam dopóki (fałsz) będzie prawdą" - nigdy. Bardziej jednak skłaniam się ku drugiej filozofii - jednak tu też prosiłbym Cię o potwierdzenie.
Hmm... Może niezbyt jasno to wytłumaczyłem. W nawiasach if'a, pętli while oraz pętli do...while jest jakiś warunek, np:
C/C++
while( a == 5 ) //wykonuj dopoki a jest rowne 5
Przed każdym powtórzeniem ten warunek jest sprawdzany w ten sposób:
czy a jest równe 5?
Jeśli tak, zwraca true
Jeśli nie, false i pętla się kończy.
Skoro wpisujesz tam liczby 0 lub 1 (albo false/true - też tak możesz, bo przecież te liczby im odpowiadają) to warunek jakby jest podany na tacy - skoro prawda, to prawda! Jak fałsz, to fałsz.
To faktycznie trochę niejasne, ale myślę, że zrozumiesz :)
Jako formułkę wybierz sobie najlepiej to co napisałem - sprawdza warunek, a jeśli warunkiem jest 0/1 false/true to nic nie musi sprawdzać. Bo przecież coś takiego a==5 też ma wartość prawda lub fałsz - w zależności od tego jaką wartość ma 'a'. W każdym razie, pętla while(0) lub while(flase) się nie wykona.
             c)

wracając do zer i jedynek oraz do tablic, uczono mnie że przy tworzeniu tablic np. tablica[ 10 ]
 możemy do nich dopisać = { x }
 gdzie "x" będzie wartością neutralną - wspólną początkową wartością dla WSZYSTKICH "x-ów" w tej tablicy
Nie wiem kto Cię tego uczył, ale nie, tak nie jest :D
Może tak było kiedyś a masz starą książkę, ja uczę się od pół roku więc nie jestem tak obeznany w starych standardach. Aczkolwiek teraz musisz pisać w ten sposób:
int tab[ 10 ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
Jeśli wszystkie mają mieć wartość 5 to:
int tab[ 10 ] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
Jeśli masz ogromną tablicę, to robisz tak:
C/C++
const int rozmiar = 900000
int tab[ rozmiar ];
for( int i = 0; i < rozmiar; i++ )
{ tab[ i ] = 5; }
Co to za zmienna
const int
 zapytasz. Const oznacza, że po stworzeniu takiej zmiennej musimy od razu nadać jej wartość (chyba, że nie chcemy), w przeciwnym wypadku potem będzie to nie możliwe:
C/C++
const int a = 5;
// a = 6; nie mozliwe - blad
// cin>>a; blad
// a++: zmiana wartosci - blad
Ale po co jej użyłem? Cóż, spójrz na rozmiar tablicy - 900000. Wiesz, jak łatwo się pomylić kiedy musisz takiej liczby używać właśnie w pętlach itp? Dzięki temu mamy pewność, że się nie pomylimy, a const chroni przed zmianami.

Jest to tylko wartość początkowa dla 1. (0) tablicy
Tak. tab[10]={1,2,3}; - tylko dla trzech pierwszysch elementów (tab[0], tab[1] i tab[2])
Dlaczego zatem w tym działającym programie gdzie mamy bool z[ 10 ] = { 0 }
 jakoś wszystkie te zmienne logiczne mają wartość 0 (false)?
Kompilator MUSI coś do nich wstawić. Nie może być zmiennej bez wartości. Tak samo jest z int'em. Na takie wartości mówi się "śmieci", bo są to pozostałości w pamięci. Uruchom ten program:
C/C++
#include <iostream>
using namespace std;
int main()
{
    int tab[ 10 ];
    for( int i = 0; i < 10; i++ )
         cout << "to sa wlasnie smieci, tab[" << i << "] : " << tab[ i ] << endl;
   
}
Tak więc kompilator po prostu "powstawiał" sobie w elementy tablicy bool false. Dlaczego? Myślę,że to domyślne. Ale Tobie radzę zawsze określić w programie
bool a = false;
Ja tak robie, myślę, że inni też. Chyba, że nie potrzebujesz od razu jej używać, a przyjmować będzie ona jakąś wartość i dopiero potem będzie wykorzystywana.
Chyba że, w C++ każda stworzona nowa obojętnie jakiego typu zmienna jest jest od początku "niczym" - zerem
 Tak jak wspomniałem są tam śmieci, chyba, że takie liczby zadeklarujesz przed funkcją main() - ale wiele osób odradza tworzenia takich zmiennych, tzw. globalnych. Myślę, że to dlatego, że są one widoczne potem w innych plikach naszego programu i mogą przeszkadzać, szczególne takie o nazwie rozmiar/size itp., bo są często używane. W każdym razie zmienne tworzone poza funkcją main są zawsze inicjalizowane przez kompilator zerami, uruchom ten program:
C/C++
#include <iostream>
using namespace std;
int tab[ 10 ];
int main()
{
    for( int i = 0; i < 10; i++ )
         cout << "nad main() - smieci brak tab[" << i << "] : " << tab[ i ] << endl;
   
}
Tak więc:
w main() - śmieci.
Poza main() - zera
              d)
takie małe czysto kosmetyczne pytanie - rozumiem zatem, że w C++ mogę zamiennie korzystać z false=0 i true=1 w przypadku wartości logicznej bool
? Jeżeli tak to czy jest możliwe aby zapisać wartość 1 dla "x-a", który jest zmienną typu "int" tak: x = true;?
No dobra, tym trochę mnie zbiłeś z tropu :D
Myślałem, że 1 = true, ale nie można sobie wstawić true do int'a.
A tu proszę, niespodzianka:
C/C++
#include <iostream>
using namespace std;
int main()
{
    int a = true;
    cout << a;
}
Więc wszystkie znaki na niebie i ziemi mówią, że można, chociaż ja bym tego nie robił, i do prawdy/fałsza używał bool'a, a do liczb int'a, bo kiedyś w dużym programie się pogubisz, a kompilator nie uzna Ci tego jako błędu, więc będziesz szukał i szukał, a uwierz mi, nie ma chyba niczego gorszego.

Jakbyś czegoś jeszcze nie rozumiał, to pytaj, po to jest to forum :)
I programistycznego 2017 :))
P-155751
carlosmay
» 2016-12-31 21:31:50
for( int licznik = 0; licznik <= 10; licznik++ )
 - licznik równy zero, wykonujemy dopóki licznik jest mniejszy lub równy 10
Chyba trochę się zagalopawałeś. Mniejszy od 10 jest OK.
P-155752
latajacaryba
» 2016-12-31 21:39:21
@up
Racja, przeciez dla licznik równemu 10 sie już nie wykona (
if( licznik == 10 ) break;
)
Dlatego licznik <10
Dzieki, od razu poprawie, żeby nie wprowadzać w błąd ;)
P-155753
1 « 2 » 3
Poprzednia strona Strona 2 z 3 Następna strona