[Diese Seite ist Teil der Homepage www.2n-1.de]

Berechnung der römischen Zahlendarstellung


Wer sich die Aufstellung der römischen Zahlen auf meiner Webseite über Zahlendarstellungen angesehen hat, wird sich vielleicht gefragt haben, nach welchen Algorithmus ich diese zahlreichen römischen Zahlen ermittelt habe. Schließlich habe ich nicht über 4000 römische Zahlen von Hand eingetippt. Andererseits beinhalten gewöhnliche Programmiersprachen keine Option zur Ausgabe der Zahlen in römischer Darstellung.

Ich habe eine Routine zur Berechnung der römischen Schreibweise einer Zahl selbst programmiert. Der folgende Screenshot zeigt diese Methode in der Programmiersprache MAGIK, einer sehr stark an Smalltalk angelehnten Sprache, die von dem aus der Universitätsstadt Cambridge in Großbritanniern stammenden Softwareunternehmen Smallworld Systems (heutiger Name: GE Smallworld Systems) im Jahr 1988 in Ermangelung einer plattformunabhängigen Standartisierung von Smalltalk entwickelt wurde, um darin eine unter verschiedenen Betriebssystemen verfügbare Datenbankanwendung für räumliche Daten (Spatial Data) zu programmieren, das Smallworld GIS.

Wer bereits mit anderen Programmiersprachen gearbeitet hat, wird sich in die Syntax recht schnell einfinden. Besonderheiten der Sprache MAGIK sind:

Schlüsselwörter, d.h. festgelegte Sprachkonstrukte, werden durch einen Unterstrich am Anfang gekennzeichnet. Das Zeichen "<<" (Doppelpfeil nach links) kennzeichnet eine Variablenzuweisung, wie sie in anderen Sprachen durch ":=" oder "=" definiert wird. Mit Plus oder Minus, also "+<<" oder "-<<", wird daraus das In- oder Dekremieren einer Variablen. Eine Schleife (loop-endloop) ohne Iterationsbedingung (for-over) ist eine Endlosschleife. Schleifen können unabhängig davon, ob es Endlosschleifen sind oder nicht, mit dem Schlüsselwort leave verlassen werden. Mit dem Schlüsselwort continue wird der aktuelle Schleifendurchlauf abgebrochen und mit den nächsten Schritt fortgefahren. Mit dem Schlüsselwort return schließlich wird die gesamte MAGIK-Methode direkt verlassen und ein Rückgabewert geliefert.

Im Sinne der Objektorientierung sind auch Prozeduren Objekte, genauso wie z.B. Zahlen und Zeichenketten. Deshalb kann man sie einfach durch Hinschreiben definieren und lokalen Variablen zuweisen.

   
Im Gegensatz zu prozeduralen Sprachen muß bei objektorientierten Sprachen zwischen einzelnen Zeichen (Characters) und Zeichenketten (Strings bzw. Character-Vectors) unterschieden werden. Denn beide haben ganz verschiedene Methoden: Ein einzelnes Zeichen kann z.B. nach seinem ASCII-Code gefragt werden oder kann Auskunft darüber geben, ob es ein Klein- oder Großbuchstabe, ein Ziffer oder ein Sonderzeichen ist. Eine Zeichenkette dagegen kann Auskunft darüber geben, wie lang sie ist, kann Teile von sich ausschneiden oder sich in Teile zerlegen. Als Elemente enthält die Zeichenkette einzelne Zeichen.

In der Sprache MAGIK ist die Unterscheidung zwischen Zeichen und Zeichenketten syntaktisch so umgesetzt, daß einzelne Zeichen durch das Prozentzeichen als Präfix definiert werden, während (wie in den meisten Programmiersprachen üblich) Zeichenketten in Hochkommata eingefaßt werden. So bezeichnet also %A den Großbuchstaben A, während "A" die Zeichenkette der Länge Eins ist, die als Element den Großbuchstaben A enthält.

Bei der unten wiedergegebenen Methode, die zu einer gegebenen Zahl die römische Darstellung als Zeichenkette liefert, ist bemerkenswert, daß gar keine Methodenaufrufe vorkommen. Mit Ausnahme der Tatsache, daß Prozeduren (weil es Objekte sind) lokalen Variablen zugewiesen und durch runde Klammern (die eine Kurzform des Methodenaufrufs ".invoke()", zu deutsch "führe Dich aus", sind) gestartet werden, ist die Methode also weniger objektorientiert, sondern mehr prozedural gehalten.

 

Berechnungsmethode als Screenshot

Wie man sieht, definiere ich zunächst zwei Prozeduren, die über die Wertigkeiten der römischen Ziffern und die Ziffern selbst (als Großbuchstaben) iterieren. Die Prozedur ziffern iteriert in aufsteigender Reihenfolge, die Prozedur ziffern2 in absteigender Reihenfolge. Für die zu berechnende Zeichenkette definiere ich eine Variable r. In einer großen Schleife wird bei jedem Schritt diese Zeichenkette um römische Ziffern ergänzt und die als Variable zahl übergebene umzurechnede Zahl um den entsprechenden Betrag vermindert. Ist die Zahl schließlich auf Null gesunken, wird die ermittelte römische Darstellung der Zahl als Zeichenkette zurückgeliefert.

Bei jedem Schritt prüfe ich zunächst, ob an dieser Stelle die Subtraktion zweiter römischer Ziffern sinnvoll ist. Wenn die Variable zahl aktuell den Wert 47 hat, soll ja nicht die Ausgabe r um den Buchstaben X ergänzt und danach mit 37 weitergerechnet werden, sondern es soll die Ausgabe r um die Buchstabenkombination VL ergänzt und mit der Zahl 7 weitergerechnet werden.

Dazu durchlaufe ich mit der Variablen nr1 als Kandidaten für das, was zu subtrahieren ist, die Wertigkeiten der römischen Ziffern in aufsteigender Reihenfolge, wobei c1 der jeweils entsprechende Buchstaben ist. Diese Schleife kann ich abbrechen, wenn ich zu einer Ziffer gekommen bin, die doppelt so viel wert ist wie der noch zu untersuchende Wert in der Variablen zahl. Als Kandidaten für den zu addierenden Buchstaben, von dem c1 subtrahiert werden soll, durchlaufe ich in absteigender Reihenfolge die Wertigkeiten der römischen Ziffern mit der Variablen nr2. Die zugehörigen Buchstaben stehen in der Variablen c2. Diese Schleife muß ich abbrechen, wenn der zu addierende Wert gleich oder größer dem doppelten des zu subtrahierenden Wertes ist. Denn es sollen die Fünfer-Werte nicht von den darüberliegenden glatten Werten abgezogen werden. Sonst würde es zu dem Kuriosum kommen, daß z.B. die Zahl 5 statt durch V durch die Subtraktion VX dargestellt würde.

Ausgedrückt mit den Variablen c1 und c2 durchlaufe ich mit den beiden Schleifen also folgende Kombinationen römischer Ziffern in dieser Reihenfolge: IM, ID, IC, IL, IX, IV, VM, VD, VC, VL, XM, XD, XC, XL, LM, LD, CM, CD. Der Wert dieser Kombinationen ist jeweils nr2-nr1. Am einfachsten ist es natürlich, wenn der Inhalt der Variablen zahl exakt einer dieser Werte ist. Dann habe ich die darzustellende Zahl komplett abgearbeitet und kann direkt aus der Methode herausspringen.

Andernfalls muß ich ermitteln, ob es Sinn macht, die dargestellten Kombinationen zu verwenden. Dies ist dann der Fall, wenn es danach nur noch mit kleineren Ziffern weitergeht. Anders ausgedrückt: In dem Wert, der sich aus der aktuell zu betrachtenden Zahl plus der in der römischen Darstellung zu subtrahierenden Ziffer ergibt, darf die zu addierende Ziffer nur noch einmal enthalten. Denn z.B. soll die Zahl 19 nicht als IXX, sondern als XIX dargestellt werden. Ich darf aber nicht einfach nur testen, ob nr2-nr mindestens so groß wie zahl ist. Sonst würde z.B. bei der Zahl 6 festgestellt, daß die Subtraktion IV (5) paßt und danach mit 2 weitergerechnet. Letztlich würde dann die 6 als IVII dargestellt. Statt dessen muß ich testen, ob die aktuelle Zahl plus der zu subtrahierenden Ziffer einen glatten Wert bezüglich der zu addierenden Ziffer ergibt. Damit die Subtraktion IV Sinn macht, muß die aktuelle Zahl plus Eins ein glatter Wert bezüglich der Fünf sein.

Die aktuell zu betrachtende Zahl muß dabei aber nicht derart einfach sein. So soll z.B. bei der Zahl 1986 festgestellt werden, daß eine Subtraktion keinen Sinn macht, wenn aber der Algorithmus bei der Zahl 986 angelangt ist, soll festgestellt werden, daß die Subtraktion LM (1000-50 ergibt 950) sehr wohl Sinn macht, um danach mit der Zahl 36 weiterzurechnen. Letztlich soll ja 1986 als MLMXXXVI dargestellt werden. Nach der oben skizzierten Regel sollte ich also feststellen, daß 986 plus 50 (also 1036) ein glatter Wert bezüglich der Zahl Tausend (römisch M) ist. Ich darf also den Begriff "glatter Wert" nicht wörtlich nehmen, sondern muß bei der Betrachtung ggf. abrunden. Aber nicht beliebig abrunden, sondern abrunden auf das nächste Vielfache einer römischen Ziffer, die kleiner oder gleich der subtrahierten Ziffer, und die echt kleiner als die addierte Ziffer ist. Die Rolle dieser Ziffer übernimmt in einer Schleife die Variable nr3. Diejenigen Schleifendurchläufe, bei denen nr3 echt größer als nr1 ist oder bei denen nr3 größer als gleich nr2 ist, werden dabei übersprungen. Das Abrunden wird mittels der Ganzzahldivision "div" und anschließender Multiplikation durchgeführt. Bei dem Abrunden muß genau die Zahl nr2 getroffen werden. Dann wird die Ziffernfolge c1+c2 in der römischen Zahl ergänzt und die entsprechende Wertigkeit nr2-nr1 von der noch zu betrachtenden Zahl abgezogen.

Die Feststellung einer römischen Ziffer, die nach dem Additionsverfahren hinzugezogen werden soll, ist dagegen recht einfach: Es wird einfach die höchstwertigste Ziffer, die größer oder gleich der Zahl ist, zu der Darstellung ergänzt und ihr Wert von der noch zu betrachtenden Zahl abgezogen.

Man kann sich fragen, ob man die Methode nicht wesentlich vereinfachen kann, indem man nicht nur die einzelnen römischen Ziffern, sondern auch deren Subtraktionskombinationen als Konstanten in Form von Iteratorprozeduren hinterlegt. Grundsätzlich ist dazu anzumerken, daß natürlich jeder Algorithmus, der nur auf einer endlichen Eingabemenge angewendet wird, oder der Rechenschritte tut, die nur endlich viele Varianten haben, dadurch vereinfacht werden kann, daß man die Ergebnisse von Berechnungen, die er durchführt, fest einprogrammiert.

Man könnte also den Algorithmus zur Berechnung der römischen Zahlen sogar ganz enorm vereinfachen, indem man alle römischen Zahlen von 1 bis 1000 als Konstanten einfügt. Für jedes weitere Tausend einer Zahl müsste dann nur noch jeweils der Buchstabe M vor die Ausgabe geschrieben werden. Zur einmaligen Erstellung (Programmierung) des Algorithmus würde dann natürlich das Ermitteln der römischen Zahlen 1 bis 1000 gehören.

Doch genau dies soll der Programmierer ja nicht tun. Rechnen soll der Komputer und nicht der Programmierer. Der Komputer soll dem Menschen Rechenaufwand abnehmen, nicht umgekehrt! Was der Rechenknecht nicht tun kann, ist das Denken. Der Mensch muß also mit Hilfe seines Verstands überlegen, welche Rechenschritte im einzelnen zu tun sind, er sollte die auf Grundlage dieser Überlegungen zu tätigende Ausführung (Berechnung) dann aber dem Komputer überlassen. Insofern ist der obige Algorithmus mit der minimal erforderlichen Vorgabe von Konstanten, nämlich den römischen Ziffern und ihren Wertigkeiten, genau richtig.

 



Weiterführende Links:

[Foto:daniel-rehbein-intergeo.jpg]


[Abrufstatistik]  Nach oben  Homepage  Impressum & Copyright