PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++ und verschachtelte Schleifen


Gast
2010-11-01, 22:14:59
Olà Leute,

ich hab ein kleines Problem. Folgende Aufgabe gilt es zu lösen:

Write a program perfect.cpp to test whether a given natural number n is perfect. A number is perfect, if it and only if it is eqal tu the sum of its prover divisors.
28 = 1 + 2+ + 4+ 7 + 14 is perfect.

Dazu habe ich auch ein nettes süßes Programm geschrieben.


int main ()
{

std::cout << "Please type in an integer you would like to test, if perfect: ";
unsigned long n;
std::cin >> n;

unsigned long i = 1;
unsigned long divsum = 0;

while (i != n)
{
if (n % i != 0)
{
i += 1;
}

else
{
divsum += i;
i += 1;
}
}

if (divsum == n)
{
std::cout << "The integer " << n << " is truly perfect! " ;
}


else
{
std::cout << "The integer " << n << " is not perfect! " ;
}


return 0;
}


Es funktioniert auch wunderbar. Nun sollen wir das Prog so modifizieren, dass es die Proper Number bis n = 50000 angibt. Nichts leichter als das, einfach bissi mit while schleifen und eintrittsbedingungen spielen. Jedoch will das Prog, egal was ich an der Syntax oder an der Anordnung schraube, einfach nicht in die inneren Schleifen rutschen. Eventuell hab ich ein kleines Syntaxhäppchen vergessen. Ich komm leider durch das Hochschulstudium aus der Pythonwelt (danke lieber PRG-1 Prof :( ) und bin immernoch am umdenken, wenn ich C++ coden muss.


// Author: Dogan Weber
// Program: celsius.cpp
// Converts Degree Fahrenheit in Degree Celsius.
// Informatik - Serie 4 - Scriptaufgabe 22
// Autor: Dogan Weber, PLE Student (Group Q)
// ETH# 10-917-029

# include <iostream>

int main ()
{

unsigned long n = 3;
unsigned long i = 1;
unsigned long divsum = 0;



while (n != 50001)

while (i != n)

if (n % i != 0)
{
i +=1;

}

else
{
divsum += 1;
i += 1;
}

if (divsum == n)
{
std::cout << n;
n += 1;
i = 1;
}






std::cout << "Calculation is done. Have a nice day.";


return 0;
}


Sprich, bis auf die äußerste while Schleife und bisschen anderen Variablen ist es im prinzip das selbe Programm. Im Prinzip ist das Programm richtig, jedoch komm ich einfach nicht auf den Haken.

Danke für eure kostbare Zeit,
Noobgast ++

TheGamer
2010-11-01, 22:22:55
Warum machst du keine geschwungenen Klammern beim while? In deinem ersten funktionierenden Programm machst du es ja auch.

robobimbo
2010-11-01, 22:44:03
lager die ermittlung in eine eigene funktion aus die nur für eine gegebene zahl true oder false zurückgibt. das kannst du ganz einfach mit dem code der schon läuft machen und testen.

und ruf die dann einfach in einer schleife auf - geht leichter durch den kopf durch :)

Tesseract
2010-11-02, 01:00:23
die brackets fehlen

und noch ein paar tips:

- lager das ganze in eine funktion aus und mach in der main nur input/output und funktionsaufrufe
- immer zuerst alle deklarationen, dann erst den restlichen code
- am ende einer zeile sollte ein std::endl hin sonst klebt alles zusammen
- versuch den code ein bischen kompakter zu halten:
while (i != n)
{
if (n % i != 0)
{
i += 1;
}

else
{
divsum += i;
i += 1;
}
}

ist beispielsweise das selbe wie

while (i < n) {
if (n % i == 0) divsum += i;
i++;
}

ShadowXX
2010-11-02, 01:08:22
- immer zuerst alle Deklarationen, dann erst den restlichen code

Das ist aber eher C als C++ style.

Wir haben damals (18-20 Jahre her) gelernt das man in C++ Deklarationen dort durchführt wo man Sie braucht (Und unsere neueren Dev Zugänge in der Firma bestätigen das auch bis heute so).

Tesseract
2010-11-02, 01:13:48
Das ist aber eher C als C++ style.
es geht mir um das hier:
std::cout << "Please type in an integer you would like to test, if perfect: ";
unsigned long n;
std::cin >> n;

unsigned long i = 1;
unsigned long divsum = 0;
das ist einfach unübersichlicher als

unsigned long n;
unsigned long i = 1;
unsigned long divsum = 0;

std::cout << "Please type in an integer you would like to test, if perfect: ";
std::cin >> n;

außerdem habe ich damit natürlich den entsprechenden sichtbarkeitsbereich gemeint und nicht den kompletten code und beim objektorientierten programmieren bleibt dir meist ohnehin nix anderes übrig.

Coda
2010-11-02, 01:27:40
Geschmackssache.

Neomi
2010-11-02, 02:31:31
Bevor in eine eigene Funktion ausgelagert wird, sollten erstmal die Fehler aus dieser Version ausgeräumt werden. Die fehlende Klammerung wurde ja schon genannt, aber da sind noch mehr. Erstmal müssen i und divsum innerhalb der äußeren Schleife initialisiert werden, nicht davor. Ansonsten funktioniert da nichts wie es soll. Als nächstes sollte nicht 1, sondern i auf divsum aufaddiert werden. Sonst steht in divsum nicht die Summe der Teiler, sondern die Zahl der Teiler. Außerdem wird innerhalb der äußeren Schleife n nur im Fall einer perfekten Zahl inkrementiert. Bei der ersten nicht perfekten Zahl (und das ist direkt beim ersten Test der Fall, nämlich bei der 3) wird quasi eine Endlosschleife daraus. Danach steht dann Redundanzvermeidung und Ausgliederung von Funktionalität an.

Tesseract
2010-11-02, 05:42:52
Erstmal müssen i und divsum innerhalb der äußeren Schleife initialisiert werden, nicht davor.

dann baut er doch erst eine endlosschleife oder hab ich dich falsch verstanden?
einfach < nehmen wie ich das in meinem codebeispiel eh gemacht habe.

Gast
2010-11-02, 09:47:25
Danke leute, das Prog läuft.



# include <iostream>
# include <IFM/integer>

int main ()
{

unsigned ifm::integer n = 1;
unsigned ifm::integer = 0;
unsigned ifm::integer i = 1;



while (n < 500001){

while (i < n == 1){

if (n % i != 0){
//std::cout << "If 1 : divsum = " << divsum;
i += 1;}s

else{
divsum += i;
i += 1; }
}
//std::cout << "End of inner While for n = " << n
//<< " analyse line: divsum = " << divsum << " n = " << n << "\n \n";

if (divsum != n){
//std::cout << n << " is not perfect \n";
divsum = 0;
i = 1;
n += 1;}
else{
divsum = 0;
i = 1;
n += 1;
std::cout << "n = " << n - 1 << " is perfect \n";
}
}


return 0;
}

Neomi
2010-11-02, 11:20:11
Sorry für die harten Worte, aber das Programm ist Schrott. Es kompiliert mit Ach und Krach (wenn man von den fehlenden divsum bei der Definition und einem eingestreuten s absieht) und liefert sogar das gewünschte Ergebnis (abgesehen davon, daß du bis 500000 statt 50000 prüfst), aber der Source dazu ist eine Katastrophe. Und das liegt wohl kaum daran, daß du normalerweise Python benutzt und nicht C++, denn die Probleme liegen sprachunabhängig beim Programmieren selbst.

Wenn du etwas für einen Schleifendurchlauf initialisierst, dann initialisiere das am Anfang des Schleifendurchlaufs. Jetzt hast du zwei Stellen (eigentlich sogar drei, aber dazu gleich noch was) statt einer einzigen. Also sehr schlecht zu pflegen. Und dann sind gleich zweimal (wobei die zweite Stelle die "Initialisierung" ist und gar nicht erst existieren dürfte) Befehle, die unabhängig von einer Bedingung sein sollen, innerhalb des If-Blocks und dann nochmal geklont innerhalb des Else-Blocks. Unbedingter Code gehört nicht so gehandhabt, sondern sollte einmal (!) außerhalb (!!) des If-Else-Konstrukts stehen. Daß du i<n mit 1 vergleichst, ist nicht nur unnötig, sondern kann auch zu Fehlern führen. Du vergleichst da nämlich einen Boolean mit einem Integer. Der Ausdruck i<n reicht völlig, ist leichter zu lesen und funktioniert auch, wenn mal irgendein Compiler aus true was anderes als 1 wandelt. Dann kommt noch dazu, daß du das n, das du ausgeben willst, erst änderst und dann zur Ausgabe die Änderung zurückrechnest. Warum nicht einfach vor einer Änderung ausgeben? Das eliminiert dann die zusätzliche potentielle Fehlerquelle und Rechenzeit (hier nicht wirklich relevant, bei komplexeren Problemen evtl. deutlich spürbar) durch das Zurückrechnen.

Unter Beibehaltung dieser IFM-Integer (keine Ahnung, warum du nicht die Standard-Integer nimmst, die würden hier völlig reichen, wahrscheinlich ist es eh nur ein typedef auf einen Standard-Integer) und noch ohne Auslagerung der Prüfung in eine eigene Funktion könnte der Code so aussehen:

#include <iostream>
#include <IFM/integer>

int main()
{
for (unsigned ifm::integer n = 1; n <= 50000; ++n)
{
unsigned ifm::integer divsum = 0;

for (unsigned ifm::integer i = 1; i < n; ++i)
if (n % i == 0)
divsum += i;

if (divsum == n)
std::cout << "n = " << n << "is perfect\n";
}

return 0;
}

Gast
2010-11-02, 13:32:03
Sorry für die harten Worte, aber das Programm ist Schrott. Es kompiliert mit Ach und Krach (wenn man von den fehlenden divsum bei der Definition und einem eingestreuten s absieht) und liefert sogar das gewünschte Ergebnis (abgesehen davon, daß du bis 500000 statt 50000 prüfst), aber der Source dazu ist eine Katastrophe. Und das liegt wohl kaum daran, daß du normalerweise Python benutzt und nicht C++, denn die Probleme liegen sprachunabhängig beim Programmieren selbst.

Wenn du etwas für einen Schleifendurchlauf initialisierst, dann initialisiere das am Anfang des Schleifendurchlaufs. Jetzt hast du zwei Stellen (eigentlich sogar drei, aber dazu gleich noch was) statt einer einzigen. Also sehr schlecht zu pflegen. Und dann sind gleich zweimal (wobei die zweite Stelle die "Initialisierung" ist und gar nicht erst existieren dürfte) Befehle, die unabhängig von einer Bedingung sein sollen, innerhalb des If-Blocks und dann nochmal geklont innerhalb des Else-Blocks. Unbedingter Code gehört nicht so gehandhabt, sondern sollte einmal (!) außerhalb (!!) des If-Else-Konstrukts stehen. Daß du i<n mit 1 vergleichst, ist nicht nur unnötig, sondern kann auch zu Fehlern führen. Du vergleichst da nämlich einen Boolean mit einem Integer. Der Ausdruck i<n reicht völlig, ist leichter zu lesen und funktioniert auch, wenn mal irgendein Compiler aus true was anderes als 1 wandelt. Dann kommt noch dazu, daß du das n, das du ausgeben willst, erst änderst und dann zur Ausgabe die Änderung zurückrechnest. Warum nicht einfach vor einer Änderung ausgeben? Das eliminiert dann die zusätzliche potentielle Fehlerquelle und Rechenzeit (hier nicht wirklich relevant, bei komplexeren Problemen evtl. deutlich spürbar) durch das Zurückrechnen.

Unter Beibehaltung dieser IFM-Integer (keine Ahnung, warum du nicht die Standard-Integer nimmst, die würden hier völlig reichen, wahrscheinlich ist es eh nur ein typedef auf einen Standard-Integer) und noch ohne Auslagerung der Prüfung in eine eigene Funktion könnte der Code so aussehen:


Danke für deine konstruktive Kritik. Es stimmt, ich habe bis jetzt nur im Zuge meines Studiums Programmieraufgaben gelöst und habe hier mit aller Gewalt versucht, While Schleifen ineinander zu verschachteln, und das noch in schlechtem Stil. Deine Tipps haben mir geholfen und ich werde die gemachten Fehler in Zukunft vermeiden.

Außerhalb des offiziellen C++ Standards, gibt es noch Literatur / Tutorials, die eben solche stilistischen / semantischen mätzchen konform zu vermeiden?

Grüße