[C++] Hausaufgabe römische Zahlen

Hi Leute!

Ich soll ein Programm mit C++ schreiben für das folgende Aufgabenstellung gegeben ist:

Schreiben Sie ein Programm, das eine einzugebende natürliche Zahl in römischer Darstellung ausgibt. Die römischen Ziffern sollen in einem konstanten String gespeichert werden. Die syntaktischen Regeln sind: Keine Ziffer außer ‚M’ darf mehr als dreimal hintereinander stehen. Das heißt, ein vierfaches Vorkommen wird durch Subtraktion vom nächst höherem passenden Wert ersetzt. Subtraktion geschieht durch Voranstellen des kleineren Wertes. So wird 4 nicht zu IIII, sondern zu IV, und 9 wird nicht zu VIIII, sondern zu IX.

Nachdem ich 6 Klausuren geschrieben habe die alle auch nicht nur im entferntesten was mit Informatik zu tun hatten muss ich das jetzt auch noch bis zum 15ten August hinkriegen und bin zu allem Überfluss vom 29.07 bis zum 07.08 im Urlaub. Ich hab so´n bischen Muffe das ich das nicht mehr schaffe und würde mich deshalb riesig über Hilfe freuen, da ich nicht wüsste wen ich sonst fragen kann.

Ich finde nicht mal einen Lösungsansatz ... außer einer Wertzuweisung für die Zeichen I - M hab ich heute in 4 Stunden nichts geschafft und die Übungen die wir im Semester gemacht haben bringen mich nicht weiter. Ich komm mir richtig dumm vor. In der Aufgabenstellung verstehe ich z.B. nicht was mit dem Speichern in einem konstanten String gemeint ist. Die Wikipedia Seite der römischen Zahlendarstellung habe ich mir natürlich angeschaut.

Ich muss auch gestehen das mir als Maschinenbaustudent die Programmiererei nicht wirklich liegt. :confused:

Es ist mit Sicherheit nicht meine Absicht mir hier eine vorgefertigte Lösung ab zu holen. Damit würde ich auch auf die Schnauze fliegen weil ich zu dem Programm auch Stellung nehmen muss. Aber ein paar zielführende Hinweise und ein Lösungsansatz würden mir enorm weiterhelfen!

MfG und thx (allein schon für´s Lesen)

Raschi
 
In der Aufgabenstellung verstehe ich z.B. nicht was mit dem Speichern in einem konstanten String gemeint ist.
Nun, ich setze voraus, dass Du (wie in der Aufgabenstellung beschrieben) weißt wie römische Zahlen aufgebaut sind. Sie werden durch Zeichen wie "I", "V", "M", ..., repräsentiert.

Zahlen wie 0, 1, 2, 3, ..., <n-1> werden üblicherweise in einem Datentyp wie Integer gespeichert, der für ganze Zahlen konzipiert ist.

Zeichen (und somit auch römische Symbole) hingegen müssen in Form einer Zeichenkette gespeichert werden und das ist ein String.

Ist der schwierige Teil für Dich die Programmiersprache C++ oder das generelle Vorgehen?
 
Hi Hackse,
klar weiß ich wie römische Zahlen aufgebaut sind. Ich benutz die meistens wenn ich irgendetwas durchnummerieren muss. C++ an sich ist auch nicht so das Problem, mache ich eigentlich sogar ganz gerne.
Es ist schon eher das generelle Vorgehen und die Denkweise die mir Probleme bereitet. Zugegebenermaßen lag meine Hauptpriorität im laufenden Semester auch nicht auf Informatik. Ich war zwar immer in den Übungsstunden, hab mich dann zu Hause allerdings nicht mehr damit auseinandergesetzt.
Ich weiß wie ich Ziffern oder Zeichen die ganzzahlige Werte haben sollen deklarieren muss und ich weiß auch wie ich mit Dezimalzahlen umgehen muss. Ich weiß auch was Modulo und was ne Schleife ist. So die grundsätzlichen Dinge halt.
Was mir bei der Aufgabe aber wie gesagt fehlt ist überhaupt erst mal ein Ansatz.

Das hier ist halt alles was ich heute auf die Beine gestellt habe, und das ist nicht viel:

#include "stdafx.h"
#include "stdio.h"

void main(void)
{ // Deklarationen
int nz, rz, I, V, X, L, C, D, M;

// Wertzuweisung
I = 1;
V = 5;
X = 10;
L = 50;
C = 100;
D = 500;
M = 1000;

// Aktionen
// natürliche Zahl eingeben
printf ("Umwandeln von natuerlichen in roemische Zahlen!\n\n\n");
printf ("Umzuwandelnde natuerliche Zahl eingeben: ");
scanf ("%d", &nz);
printf ("\n\n");

// römische Zahl ausgeben
printf ("Roemische Zahlendarstellung: ");
printf ("\n\n\n");
}
 
Eine mögliche Lösung wäre das Problem in zwei Teilschritte zu zerlegen. Das ist zwar sicher nicht die schnellste Lösung, aber sie sollte leicht nachvollzierbar sein.
Step 1: Die römische Zahl falsch zusammensetzen, soll bedeuten ohne Darstellungen in der Art IV, sondern stupide mithilfe von Konstrukten a la IIII. Um das zu erhalten kann man von oben beginnen, die vorgegebene Zahl durch 1000 dividieren und den Vorkommateil als Anzahl für die Ms verwenden, danach Anzahl Ms x 1000 abziehen. Dann kann man wieder von vorne beginnen, nur dieses mal mit 500, usw.
Step 2: Den bereits berechneten String nach falschen Kombinationen durchforsten und diese gegebenenfalls ersetzen.

Ich weiß jetzt nicht wie sehr das C++ und nicht C sein soll, aber falls es objektorientiert sein soll würde es sich anbieten, die gesamte Zahl als Objekt darzustellen. Dieses könnte wieder entweder einzelne Zeichen-Objekte enthalten, oder eine Liste von Zeichengruppen, welche wiederum selber römische Zeichen-Objekte enthalten. Ich vermute stark, dass sich in so einer Darstellung das finden von falschen Zeichenketten eleganter bewerkstelligen lässt. Was da die beste Alternative ist kann ich dir aus dem Stegreif heraus nicht beantworten, da müsste man ein bisschen experimentieren.

mfg benediktibk
 
kk, ich denk mal drüber nach. Als erstes würde ich allerdings gerne klären was er mit diesem konstanten String Ding meint. Ein String ist doch einfach nur ´ne Zeichenfolge, oda? Die römische Zahl die am Ende rauskommt ist doch ´ne Zeichenfolge ... wir haben immer nur sehr einfach gehaltene Konsolenanwendungen gemacht, da wurde nie irgendetwas gespeichert.
Das schnall ich nicht, in meinem Mitschriften steht dazu auch nix.

Meint ihr denn das die Zeichen die ich da erstmal so deklariert habe grundsätzlich ausreichen um daraus dann das eigentliche Programm zu schreiben oder muss das noch ausführlicher?
 
Zuletzt bearbeitet:
Bitte versuche Deine Lösungen so zweckmäßig und effizient wie möglich zu halten.

Als erstes würde ich allerdings gerne klären was er mit diesem konstanten String Ding meint.
Wie bereits erwähnt sollen in einem String die römischen Symbole gespeichert werden, auch wenn dies zur Lösung der Aufgabe nicht nötig ist.

Ich entwerfe Dir auf die Schnelle folgenden, rekursiven Algorithmus (in Ansi C) zur Problemlösung:

Code:
#include <stdio.h>
#include <stdlib.h>

#define M 1000
#define CM 900
#define D  500
#define CD 400
#define C  100
#define XC 90
#define L  50
#define XL 40
#define X  10
#define IX 9
#define V  5
#define IV 4
#define I  1

const int MAX=20000;

int rom(int);

int main(int argc, char **argv) {
  unsigned long n;
  if (argc != 2) {
  fprintf(stderr, "%s ZAHL\n", argv[0]); 
  return 1;
 }
  n=atol(argv[1]);
  if (n>MAX) {
    fprintf(stderr, "MAX: %d\n", MAX);
    return 1;
  }
  printf ("--> %-5.0lu : ", n);
  rom((int) n);
  puts("");
  return 0;
}

int rom(int n) {
  if (n<1) return 0;
  if (n>=M)  { printf ("M");  n=rom(n-M);  }
  if (n>=CM) { printf ("CM"); n=rom(n-CM); }
  if (n>=D)  { printf ("D");  n=rom(n-D);  }
  if (n>=CD) { printf ("CD"); n=rom(n-CD); }
  if (n>=C)  { printf ("C");  n=rom(n-C);  }
  if (n>=XC) { printf ("XC"); n=rom(n-XC); }
  if (n>=L)  { printf ("L");  n=rom(n-L);  }
  if (n>=XL) { printf ("XL"); n=rom(n-XL); }
  if (n>=X)  { printf ("X");  n=rom(n-X);  }
  if (n>=IX) { printf ("IX"); n=rom(n-IX); }
  if (n>=V)  { printf ("V");  n=rom(n-V);  }
  if (n>=IV) { printf ("IV"); n=rom(n-IV); }
  if (n>=I)  { printf ("I");  n=rom(n-I);  }
  return n;
}
... kompiliere mein Programm ...
Code:
gcc -Wall -o rom rom.c
... und lasse es mit einigen Zahlen laufen ...

Code:
for k in 1 4 53 112 287 999 1001 4630 5000 1099 15345 20000; do ./rom $k; done

--> 1     : I
--> 4     : IV
--> 53    : LIII
--> 112   : CXII
--> 287   : CCLXXXVII
--> 999   : CMXCIX
--> 1001  : MI
--> 4630  : MMMMDCXXX
--> 5000  : MMMMM
--> 1099  : MXCIX
--> 15345 : MMMMMMMMMMMMMMMCCCXLV
--> 20000 : MMMMMMMMMMMMMMMMMMMM

Noch Fragen?
 
Zuletzt bearbeitet:
Vielen Dank Hackse! :thumb_up:
Fragen hab ich bestimmt, kann momentan nur noch keine formulieren.
Werde mich da jetzt mal mit auseinandersetzen und melde mich später nochmal.
 
Zu dem Thema gibt's hier im Programmieraufgaben-Forum übrigens einen Thread: http://www.hackerboard.de/programmieraufgaben/25075-roemische-zahlen.html

Da ist auch die eine oder andere C-Lösung dabei, eine Variante ohne Rekursion könnte z.B. so aussehen:
Code:
#include <stdio.h>
#include <stdlib.h>

char *rom[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
int   dec[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};

void decToRom(int d)
{
    int i;
    for (i = 0; i < 13; i++) {
        while (d >= dec[i]) {
            printf("%s", rom[i]);
            d -= dec[i];
        }
    }
    printf("\n");
}

int main(int argc, char **argv)
{
    if (argc < 2) {
        printf("%s ZAHL\n", argv[0]);
        return 1;
    }
    decToRom(atol(argv[1]));
    return 0;
}
 
Hatte ich wohl gesehen, allerdings konnte ich damit nicht viel Anfangen. Das ist alles viel zu Professionell. Wie gesagt, ich bin kein Informatiker und damit ich meine Leistungspunkte bekomme muss ich das Programm was ich abgebe auch erklären können. Deshalb habe ich das Problem nochmal von vorne aufgerollt ... hoffe das ist ok und wiederspricht nicht den Forenregeln.
Ich glaube ich bin aber gerade auf einem ganz guten Weg.

Eine Frage ist da gerade allerdings aufgetauch. Wie schon erwähnt wird das ganze eine einfache Win32-Konsolenanwendung. Wenn so ein Programm durchgelaufen ist wird man am Ende ja immer aufgefordert eine Taste zum beenden zu drücken. Kann man da auch eine Funktion einbauen das das Programm beim drücken einer bestimmten Taste nicht beendet wird sondern von vorne startet?
 
Also ich bin mir ja noch etwas unsicher aber ich glaube das schlimmste hab ich geschafft:

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"

#define M 1000
#define CM 900
#define D 500
#define CD 400
#define C 100
#define XC 90
#define L 50
#define XL 40
#define X 10
#define IX 9
#define V 5
#define IV 4
#define I 1

void main(void)
{ // Deklarationen
int n;

// Aktionen

// natürliche Zahl eingeben
printf ("Umwandeln von natuerlichen in roemische Zahlen!\n\n\n");
printf ("Umzuwandelnde natuerliche Zahl eingeben: ");
scanf ("%d", &n);
printf ("\n\n");

// römische Zahl ausgeben
printf ("Roemische Zahlendarstellung: ");

if (n<I) { printf ("N");}
else
while (n>=I)
{
if (n>=M) { printf ("M"); n=n-M; continue;}
if (n>=CM) { printf ("CM"); n=n-CM; continue;}
if (n>=D) { printf ("D"); n=n-D; continue;}
if (n>=CD) { printf ("CD"); n=n-CD; continue;}
if (n>=C) { printf ("C"); n=n-C; continue;}
if (n>=XC) { printf ("XC"); n=n-XC; continue;}
if (n>=L) { printf ("L"); n=n-L; continue;}
if (n>=XL) { printf ("XL"); n=n-XL; continue;}
if (n>=X) { printf ("X"); n=n-X; continue;}
if (n>=IX) { printf ("IX"); n=n-IX; continue;}
if (n>=V) { printf ("V"); n=n-V; continue;}
if (n>=IV) { printf ("IV"); n=n-IV; continue;}
if (n>I) { printf ("I"); n=n-I; continue;}
if (n=I) { printf ("I"); break;}
}
printf ("\n\n\n");
}

Alle Zahlenwerte die ich ausprobiert habe wurden auf jeden Fall richtig ausgespuckt, aber viell. fällt ja noch jemandem ein Fehler auf.
Ansonsten hätte ich noch 2 Fragen. Einmal die bereits gestellte:

Kann man da auch eine Funktion einbauen das das Programm beim drücken einer bestimmten Taste nicht beendet wird sondern von vorne startet?
Und des weiteren frage ich mich jetzt wie ich es umsetzten kann das wenn keine natürliche Zahl eingegeben wird, z.B. eine negative oder eine Dezimalzahl oder von mir aus auch ein Buchstabe, eine Fehlermeldung ausgegeben wird ?

Auf jeden Fall nochmals meinem herzlichsten Dank Hackse, dein Code hat mir den Denkanstoß gegeben der nötig war! :thumb_up:
 
Wenn du eine Dezimalzahl eingibst, dann werden aufgrund des von dir gewählten Typs Integer die Nachkommastellen einfach abgeschnitten.
Überprüfe einfach, ob die Zahl > 0 ist und wenn nicht dann muss die Person nochmal eine Zahl eingeben (am besten in einer Schleife, damit man auch beliebig oft was falsches eingeben kann).

Die Buchstaben als falsche Eingaben sind in der Tat ein Problem, da C nunmal nicht Typsicher ist werden die eingegebenen Buchstaben einfach in einen Integer "umgewandelt". Ich glaube Microsoft hat dort doch auch eine Typsichere Variante für scanf() soviel ich weiß. Entweder die benutzen oder umständlich den Wert nicht als Integer sondern als String/Char-Array einlesen und dort, falls Buchstaben drin vorkommen, eine Fehlermeldung ausgeben.
 
Hab ich gemacht! Zumindest deinen Vorschlag mit der Schleife und es funktioniert auch ... bedingt.

// natürliche Zahl eingeben
printf ("Umwandeln von natuerlichen in roemische Zahlen!\n\n\n");
printf ("Umzuwandelnde natuerliche Zahl eingeben: ");
scanf ("%d", &n);
while (n<0)
{ if (n<0) {printf ("\nERROR!\n\n\nNeue Eingabe (keine negativen Zahlen): "); scanf ("%d", &n); continue;}
if (n>0) {break;}}
printf ("\n\n\n");

Aber wenn ich jetzt einen Buchstaben eingebe wird daraus eine Endlosschleife. Vielleicht schaue ich mir das nach meinem Urlaub nochmal an. Ich denke aber mal die Aufgabenstellung ist erfüllt, von daher werde ich jetzt erst mal in ruhe mit meinen Urlaubsvorbereitungen beginnen :)

THX und mit besten Grüßen!

Raschi
 
Wenn du mit break arbeiten willst, kannst du auch einfach eine Endlosschleife machen:
Code:
 while(1) {
               ...
               if(n > 0) /* Wenn n größer 0 ist, ist alles in Ordnung */
               break;
          }
Dazu, die Anwendung neu zu starten, einfach nach dem Taste drücken die Funktion rekursiv neu starten:
Code:
  printf("Beliebige Taste druecken\n");
  getch();
  main(); /* Funktion wieder rekursiv aufrufen */
Oder, um das Ganze ein wenig eleganter zu gestalten, schiebst du den gesamten Eingabe-Code in eine extra-Funktion, die du dann bequem aus der main-Funktion aufrufen kannst.
 
Und des weiteren frage ich mich jetzt wie ich es umsetzten kann das wenn keine natürliche Zahl eingegeben wird, z.B. eine negative oder eine Dezimalzahl oder von mir aus auch ein Buchstabe, eine Fehlermeldung ausgegeben wird?
In meinem Source wird atol() bereits verwendet:
Code:
n=atol(argv[1]);
... und sieh Dir mal das hier an:
atol - C++ Reference
Kann man da auch eine Funktion einbauen das das Programm beim drücken einer bestimmten Taste nicht beendet wird sondern von vorne startet?
Du möchtest an der Stelle keine Funktion einbauen. Effizient ist eine fussgesteuerte Iteration, z.B. mit der Prüfung auf Eingabe von "j" zum erneuten ausführen und ungleich "j" für den Abbruch. Sieh' Dir diesbez. die C++-Referenz von cin.get() an:
istream::get - C++ Reference
Auf jeden Fall nochmals meinem herzlichsten Dank Hackse, dein Code hat mir den Denkanstoß gegeben der nötig war! :thumb_up:
Ich komme aus der Mathematik / theoretischen Informatik, daher ist meine Lösung rekursiv. Du musst gucken, ob Dein Prof. damit einverstanden ist. In meiner Lösung werden außerdem keine römischen Symbole in Form von Strings gespeichert, sondern einfach auf <stdout> ausgegeben. Ich habe das Programm auf die Schnelle runtergeschrieben. Somit fungiert es maximal als Prototyp.
 
Zurück
Oben