Seite 2 von 5

Re: Und nun ein C++-Problem...

Verfasst: 27.01.2010 15:53
von P7BB
Ok, danke, funktioniert jetzt ;)
math.h und windows.h sind dort bei den includes, da ich diese für eine ergänzung, die später folgen wird, benötige ;)

Edit:
Die mathematische Begründung für das mit der Wurzel ist übrigens bei diesem Beispiel zu erkennen:
loop = 25;
wurzel von loop = 5
loop / wurzel von loop + 1 = 0,...
wenn mal die wurzel von loop mit 1 addiert und loop durch das dann teilt, kommt immer eine zahl á la 0,... raus ;)

Nohcmal Edit:

Code: Alles auswählen

// Test.cpp

#include <iostream>
#include <windows.h>
#include <time.h>
#include <math.h>
#include <fstream>
using namespace std;

int main() {
 double loop;
 double i;
 double max;
 boolean prim;
 ofstream f("C:\\prim.txt", ios::app);

 cout << "Geben Sie die Zahl ein, bis zu der gezaehlt werden soll:" << endl;
 cin >> max;
 cout << "==================================================" << endl;

 f << "Primzahlen" << endl;

 for(loop = 1;loop <= max;++loop) {
  prim = true;
  for(i = 2;i < sqrt(loop);++i) {
   if(loop / i == ceil(loop / i)) {
    prim = false;
   }
  }
  if(prim == true) {
   cout << loop << endl;
   f << loop << endl;
  }
 }
}
Die Datei wird aber leider nicht erstellt... o.O wo ist der fehler?
ich habe auch schon versucht, die datei so zu beschreiben:

Code: Alles auswählen

fstream f;
...
f.open("C:\\prim.txt",ios::app));
f << loop << endl;
f.close();
Auch das geht nicht... :(

Edit3: Übrigens: Wenn ich sage, dass i mit 3 initialisiert wird und dann jede immer i = i + 2 bei jedem durchgang der for-schleife gerechnet wird, kommt es dazu, dass es auch teilweise gerade zahlen als primzahlen gibt. deshalb muss ich bei 2 initalisieren und immer ++i rechnen...

Edit 4:
Die effizienteste Lösung wäre vermutlich sowieso, wenn man alle bereits rausgefundenen primzahlen in ein array schreibt und einfach bei einer neuen zahl prüft, ob diese zahl ein vielfaches einer primzahl ist. wenn nicht, ist die zahl ebenfalls eine primzahl. ;) Das kombiniert mit der wurzel-geschichte und man hat ein extrem effektives programm, dass auf mehreren kernen sicherlich in 1-2 minuten über 150.000 primzahlen berechnen könnte (momentan ca. 80.000 zahlen in 5 minuten auf einem kern...)

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 12:24
von gn#36
lies doch mal das failbit aus. Vielleicht hast du an dem angegebenen Ort keine Schreibrechte oder so.

Edit 3 ist völliger Blödsinn. Du sollst ja auch nicht i in Zweierschritten erhöhen sondern die Primzahlen die du testen willst, also loop. Die 2 würde ich da einfach manuell ausgeben und dann nur noch jede ungerade Zahl. i muss natürlich weiterhin jede Primzahl kleiner als die Wurzel durchlaufen. Da würde ich übrigens auch nur einmal die Wurzel berechnen und nicht so wie du es jetzt hast in jedem Schleifendurchlauf. Wenn du wirklich sehr große Werte berechnen willst, dann macht das einen Unterschied ob du das Ding zwischenspeicherst oder in jedem Durchlauf neu berechnest.

Das Problem mit dem Zwischenspeichern der Primzahlen ist dass es irgendwann viel Speicher verschlingt. Du hast also wie so häufig die Wahl zwischen Geschwindigkeit und Speichereffizienz.

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 13:51
von P7BB
gn#36 hat geschrieben:lies doch mal das failbit aus. Vielleicht hast du an dem angegebenen Ort keine Schreibrechte oder so.

Edit 3 ist völliger Blödsinn. Du sollst ja auch nicht i in Zweierschritten erhöhen sondern die Primzahlen die du testen willst, also loop. Die 2 würde ich da einfach manuell ausgeben und dann nur noch jede ungerade Zahl. i muss natürlich weiterhin jede Primzahl kleiner als die Wurzel durchlaufen. Da würde ich übrigens auch nur einmal die Wurzel berechnen und nicht so wie du es jetzt hast in jedem Schleifendurchlauf. Wenn du wirklich sehr große Werte berechnen willst, dann macht das einen Unterschied ob du das Ding zwischenspeicherst oder in jedem Durchlauf neu berechnest.

Das Problem mit dem Zwischenspeichern der Primzahlen ist dass es irgendwann viel Speicher verschlingt. Du hast also wie so häufig die Wahl zwischen Geschwindigkeit und Speichereffizienz.
Ok, danke für die Tipps, ich werds direkt ändern ;)

Problem 1:
Wie lass ich denn die Fehlermeldung vom Programm anzeigen? Sowas hab ich bis jetzt ehrlich gesagt noch nie in einem Beispielsprogramm von meinem C++-Buch gesehen (habs aber auch noch nicht ganz durch ;) )
Achja: Berechtigungen hab ich. Ich hab das Programm sogar als Admin ausgeführt, aber dennoch kann ich keine Datei erstellen... :|

Problem 2:
Wenn ich zum Beispiel die Primzahlen über 1.000.000 berechne, wird das ergebnis dummerweise in wissenschaftlicher schreibweise ausgegeben. mit sprintf("f",loop) hat es aber nicht funktioniert :(
Dann hatte ich satt "f" "%c" und "%f" benutzt, aber danach sahen zahlen so aus: 2.00000000000000000000000012

Edit: Ich denke, es ist nicht ganz deutlich geworden, deshalb schreib ich es vorsichtshalber noch einmal deutlich: In eine bestehende Datei schreiben ist kein Problem. Nur das Erstellen einer Datei... :|
Das Problem #2 hat sich erledigt (glaub ich), als ich loop zu einem long int gemacht hab ;)

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 17:36
von gn#36
Du solltest mit integer und long integer arbeiten, aber irgendwann reicht das auch nicht mehr, dann brauchst du spezielle Bibliotheken (in Java ist da z.b. eine schon direkt integriert die sich BigInteger nennt, hiermit sind auch Berechnungen mit mehreren hundert Stellen möglich ohne Overflow) . Float ist keine Option, da nicht exakt. Ich habe bisher noch keine C++ BigInt Klassen gebraucht, aber es gibt sie garantiert und bei einer vernünftigen dürften Operationen wie + - * / % etc. auch weiterhin funktionieren, wenn sie gut ausgestattet ist auch ein paar grundlegende mathematische Dinge wie sqrt oder pow o.ä.

Was die Fehlerbehandlung angeht schau dir mal z.b. sowas hier an: http://www.willemer.de/informatik/cpp/fileop.htm

Wenn streams benutzt dann solltest du das auch durchgängig tun und nicht eine Zahl per sprintf oder ähnlichem erst noch umbauen (auch wenn es durchaus dennoch sinnvolle Anwendungen gibt. Such mal nach iomanip, hiermit kannst du formatierte Ausgaben per Stream realisieren. Für Integerausgaben ist übrigens %d, %i oder %u zuständig.

Eigentlich dachte ich dass das ios::app Flag die Datei auch erstellt, vielleicht gibt's da aber auch ein ios::create für (in manchen Implementierungen gibt's zumindest ein ios::nocreate).

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 17:55
von P7BB
gn#36 hat geschrieben:Du solltest mit integer und long integer arbeiten, aber irgendwann reicht das auch nicht mehr, dann brauchst du spezielle Bibliotheken (in Java ist da z.b. eine schon direkt integriert die sich BigInteger nennt, hiermit sind auch Berechnungen mit mehreren hundert Stellen möglich ohne Overflow) . Float ist keine Option, da nicht exakt. Ich habe bisher noch keine C++ BigInt Klassen gebraucht, aber es gibt sie garantiert und bei einer vernünftigen dürften Operationen wie + - * / % etc. auch weiterhin funktionieren, wenn sie gut ausgestattet ist auch ein paar grundlegende mathematische Dinge wie sqrt oder pow o.ä.

Was die Fehlerbehandlung angeht schau dir mal z.b. sowas hier an: http://www.willemer.de/informatik/cpp/fileop.htm

Wenn streams benutzt dann solltest du das auch durchgängig tun und nicht eine Zahl per sprintf oder ähnlichem erst noch umbauen (auch wenn es durchaus dennoch sinnvolle Anwendungen gibt. Such mal nach iomanip, hiermit kannst du formatierte Ausgaben per Stream realisieren. Für Integerausgaben ist übrigens %d, %i oder %u zuständig.

Eigentlich dachte ich dass das ios::app Flag die Datei auch erstellt, vielleicht gibt's da aber auch ein ios::create für (in manchen Implementierungen gibt's zumindest ein ios::nocreate).
Hi, danke für deine Hilfe ;)
Also bis auf das Datei-Erstellen Problem hat sich inzwischen alles erledigt.
In diversen Beschreibungen steht auch, dass ios::app auch eine Datei erstellt, bei mir aber leider nicht. :(

Edit:
Ok, das mit den Parametern hat sich auch erledigt ;) Bleibt das Problem mit dem Datei-Erstellen...

Aber ich hab noch direkt eine neue Frage:
Ich würde gerne aus meiner C++-Datei eine andere C++-Datei aufrufen (das ist noch nicht das Problem), aber mit Parametern, also z.b. so wie beim Compiler "g++ -o test.exe test.cpp" nur eben sowas wie "datei2.exe -parameter wert". Wenn ich bei Google gucke (etwa "C++ Programm mit Parametern" hab ich gegoogelt), bekomme ich nur Compiler-Hilfen...

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 18:00
von gn#36
Das geht ganz einfach. Je nach dem wie du das Programm aufrufen willst und unter welchem System gibt es da zwar ziemlich viele Möglichkeiten (z.b. je nach dem ob es den Programmfluss deines eigenen Programms bis zur Fertigstellung unterbrechen soll oder nicht, die simpelste Variante was du machen kannst ist aber system. Dem gibst du deine Befehlszeile und den Rest macht der Befehl.

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 18:11
von P7BB
Ok, gut.

Um das Argument auszulesen, benötigt man ja argv[1].

Code: Alles auswählen

[...]

int main(int argc, double argv[]) {
 double max;
 if(!argv[1]) {
  cout << "Wie viele Primzahlen sollen gesucht werden?" << endl;
  cin >> max;
  system("cls");
 } else {
  max = argv[1];
 }
}
Das funktioniert aber nicht. Wenn ich das Programm normal starte, schließt sich das Programm sofort wieder (wird aber korrekt ausgeführt, aber max wird mit 0 belegt). Wenn ich als argument "12345" anhänge, kommt das dabei raus: 4,8668e+011 oder etwas ähnliches. Oder das Programm schließt sich auch dann einfach ohne Grund...

Testweise hab ich mal argv[] ausgegeben:
123 wird zu 3608888

EDIT:
Auch das hat sicher erledigt ;)
int = atoi(string);

Nochmal edit:
Kann ich auch ein Programm mit Parametern starten und dann anschließend über das gestartete programm wieder informationen an das hauptprogramm weitergeben?

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 18:26
von gn#36
Das klappt nicht, weil du einen String auf diese Weise nicht in eine Zahl umwandeln kannst. Dafür brauchst du schon so Dinge wie atoi oder auch eleganter z.b. sowas:

Code: Alles auswählen

#include <string>
#include <sstream>
#include <iostream>
template <class T>
inline bool from_char(T& write_to, const char * read_from, std::ios_base& (*read_format)(std::ios_base&) = std::dec)
{
    std::string data(read_from);
    std::istringstream iss(data);
    return !(iss >> read_format >> write_to).fail();
}
Zur Erklärung kurz: der Text wird in einen String geschrieben, dieser wird dann in einen Stringstream umgewandelt (ist also effektiv danach ein Stream, also so ähnlich wie cin oder cout). Aus diesem Stream wird dann ähnlich wie das bei cin oder einem Filestream geht der Wert ausgelesen und automatisch korrekt konvertiert, egal um welchen genauen Typ es sich nun handelt, daher das template <class T> vor allem und als Datentyp das T für write_to. Da write_to als Referenz übergeben wird wirken sich Änderungen auf die Variable auch im aufrufenden Programmteil aus.

Benutzung dann so:

Code: Alles auswählen

int deinezahl;
from_char<int>(deinezahl, argv[1]);
Den Rückgabewert kannst du verwenden um zu schauen ob es geklappt hat, das praktische daran ist dass du das Teil für alle Typkonversionen machen kannst für die der >> Operator für den betreffenden Typ als Ausleseoperator aus dem Stream definiert ist, d.h. wenn du willst kannst du das auch für eigene Klassen oder ähnliches benutzen, du musst nur den >> Operator so überladen, dass dein Objekt aus einem Stream gelesen werden kann (was für so Dinge wie Speichern und Laden der Klasse aus einer Datei sowieso nicht unpraktisch ist).

Noch eine kurze Warnung: Wenn du das in einem größeren Projekt einsetzen willst in dem du mehr als nur eine Datei hast, dann kannst du diese Funktion nicht durch einen Prototypen ersetzen. Da der Compiler nicht wissen kann für welche Typen genau die Funktion kompiliert werden muss braucht er die komplette Funktion an allen Stellen wo sie benutzt wird, d.h. die komplette Funktion muss in den Header. Wenn du nur eine Datei hast musst du natürlich dafür keinen Header anlegen, aber du kannst sie nicht per Prototyp bekannt machen und dann erst später in deine Datei setzen, die Funktion gehört weit nach oben.

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 18:34
von P7BB
Danke. ;) Das mit atoi hatte ich gerade eben auch gefunden, aber erst dank deiner Erklärung wird mir überhaupt klar, wie man praktisch jeden typ in einen anderen typ umwandeln kann :)

Aber direkt die nächste Frage:
Kann ich auch ein Programm mit Parametern starten und dann anschließend über das gestartete programm wieder informationen an das Hauptprogramm weitergeben?
Ich bin mir nicht sicher, ob C++ automatisch alle Kerne benutzt, aber im Taskmanager sieht man immer nur einen Kern voll ausgelastet. Das könnte man ja entweder durch ein script verhindern, dass alle kerne benutzt, oder man startet für einen zahlenbereich von x nach y einfach ein neues programm, sodass bei meinem pc, einem 4 kern-prozessor 3 programme gleichzeitig die primzahlen berechnen und windows trotzdem noch etwas ansprechbar ist ;)
Falls ein kleines "script" für den zweck praktischer ist, würde ich diese lösung natürlich lieber hören, da sich dann ja nich ständig neue fenster öffnen und schließen :D

Re: Und nun ein C++-Problem...

Verfasst: 28.01.2010 22:41
von gn#36
C++ hat mit dem Prozessor erst mal nicht viel zu tun, ob ein Programm auf einem oder mehreren CPUs abläuft hängt von deinem Aufbau ab. Wenn du mehrere Kerne nutzen willst, dann musst du dein Programm parallelisieren. Hierfür gibt es auch wieder Bibliotheken mit denen das recht einfach ist, allerdings musst du dabei eine ganze Menge beachten wenn du willst dass das ganze hinterher noch funktioniert, denn dein Programm läuft nicht mehr so sequenziell ab wie du es gewohnt bist so dass einige Dinge nicht mehr so funktionieren wie vorher. Globale Variablen oder Dateiausgaben sind beispielsweise gefährlich, denn du kannst nie wissen wann welcher Thread genau was macht, diese werden nahezu zufällig unterbrochen.

Dein Programm oben lässt sich aber ganz leicht parallelisieren, denn du kannst ja jeden Kern ganz einfach andere Primzahlenbereiche berechnen lassen. Solange du das Programm nicht so umgebaut hast, dass es bisherige Ergebnisse weiternutzt kannst du die Ergebnisse in unterschiedliche Dateien schreiben lassen und hinterher aneinanderfügen, das wäre so ziemlich die einfachste Parallelisierung die man hier machen könnte. Wenn du Ergebnisse weiterverwenden willst wird es schon komplizierter, denn erstens bist du in der Wahl der parallel durchgeführten Operationen nicht mehr ganz so frei (denn wenn die Primzahlen zwischen 100 und 200 nur mit Hilfe der Primzahlen bis 15 (was ungefähr Wurzel(200) ist) berechnet werden können kannst du diese Zahlen erst berechnen sobald du eben diese Primzahlen bereits kennst), und zweitens entstehen durch das zwischenspeichern u.U. Race Conditions.

Ein Programm hat auch nichts mit "Fenster öffnen und schließen" zu tun. Die meisten Programme auf deinem Computer dürften im Hintergrund und ohne irgendwelche grafische Ausgabe ablaufen, wenn du mal einen Blick in deinen Task Manager wirfst.

Hier jetzt ausschweifender zu werden über IPC und Threads würde aber definitiv zu weit gehen, da solltest du dir entsprechende Tutorials und Anleitungen suchen um die grundlegenden Konzepte zu lernen, es gibt eine ganze Menge Bibliotheken und Möglichkeiten, aber das Grundlegende musst du dafür erst einmal verstanden haben wenn das ganze funktionieren soll. Solange du nicht weißt was Mutexe, Semaphoren, Race Conditions, Deadlocks und Critical Sections sind - im Bezug auf IPC und Threads - solltest du dich erst mal weiter schlau machen.

Aber wie schon gesagt, dieses Spezielle Problem lässt sich ja auch ganz einfach parallelisieren indem du das Programm mehrfach aufrufst und unterschiedliche Zahlenbereiche berechnen lässt. Du kannst also einfach einen zweiten Parameter einführen der die Untergrenze der zu berechnenden Zahlen angibt und schon kannst du das ganze auch mit einem Skript einfach mehrfach aufrufen. In Bash Syntax könnte das Skript dann so aussehen:

Code: Alles auswählen

#!/bin/bash
# Wir gehen davon aus dass das Programm so aufgerufen wird:
# prog untergrenze obergrenze dateiname
maximalwert="$1"
if [ "$maximalwert" == "" ]; then
	echo "Benutzung: $0 Maximalwert"
	exit 1;
fi

# Ganz einfach in 4 Teile teilen:
wert1=$(echo "$maximalwert/4" | bc);
wert2=$(echo "$wert1 * 2" | bc);
wert3=$(echo "$wert2 + $wert1" | bc);

# Aufruf:
prog 1 $wert1 __part0.txt &
prog $wert1 $wert2 __part1.txt &
prog $wert2 $wert3 __part2.txt &
# Theoretisch sollte der hier sowieso am längsten brauchen:
prog $wert3 $maximalwert __part3.txt
# Auf die Prozesse warten:
wait

# Zusammenfügen:
cat __part0.txt __part1.txt __part2.txt __part3.txt > ergebnis.txt
rm __part?.txt
Wie du das mit Windows batchfiles o.ä. realisierst musst du selbst schauen, damit kenne ich mich nicht so aus.