Gyakorlati alapok II.
Váltogassunk tovább: római-arab szám váltó I.
Először nézzük meg a kötelező elméleti vonatkozásokat!
A római számjelölés úgynevezett additív (hozzáadásos) számábrázolási módszer, amely során bizonyos számértékek önálló jelölést kaptak (római számok esetében ezek a latin ABC bizonyos nagybetűi) és a számösszeg ezen jelölések egymás mellé tevéses, illetve ezek összeadásán alapuló kombinációiból épül fel. Az alapkészlet:
-
I = 1
-
V = 5
-
X = 10
-
L = 50
-
C = 100
-
D = 500
-
M = 1000
Számok ábrázolásakor a sorrend balról jobbra (→) haladva a következő (ettől azonban logikátlanul vannak eltérések):
-
ha van - ezresek
-
ha van - százasok
-
ha van - tízesek
-
egyesek
Például:
-
6 = VI
-
54 = LIV
-
138 = CXXXVIII
-
781 = DCCLXXXI
-
1977 = MCMLXXVII
-
2000 = MM
-
3999 = MMMCMXCIX
Tulajdonságai:
-
matematikailag nézve fejletlen és rendkívül nehézkes számábrázolási mód, ezért az arab számábrázolás könnyen szorította ki a középkorban. (Bár egyik előnyeként említhető, hogy a korabeli viszonyok között nehéz volt hamisítani, valószínűleg ezért nem pusztult ki hamarabb.) Manapság már csak fejezet-, törvénycikk-, és uralkodói dinasztiák számozására használatos (például II. bajor Lajos).
-
A 7 betűből álló alap betűkészletből kirakható számmaximum a 3999 (MMMCMXCIX). Ennél magasabb számábrázoláshoz további, speciális karaktereket bevonása szükséges (fordított C), amelytől a jelen fejezetben eltekintünk.
-
IV (4) helyett gyakran használnak IIII kombinációt, a jelen fejezetben ettől eltekintünk, illetve a későbbiekben hibának minősítjük.
-
I csak V illetve X előtt állhat, bár egyes vélekedések megengedik az IC (99) használatát. A későbbiekben hibának minősítjük.
Projektügyileg nézve tehát maximálisan 3999 db római számot kell átkonvertálnunk arab számra. Valójában ez adatbázis szempontjából nem olyan sok adat, ezért nagy a kísértés, hogy mind a 3999 db római számot beletegyük 1 db adatbázisba és onnan hajtsuk végre a lekérdezést (íme a lista). Ezt a Számváltás adatbázisból című fejezetben fogjuk megtenni. Ezen megoldás roppant nagy előnye, hogy nem kell vesződnünk validátor (helyesség ellenőrző) függvény írásával. A problémát azonban mégsem kerüljük meg, ezért ki kell egészítenünk a konvertálást logikai algoritmusos megoldással is, illetve bónuszként a rendkívül bonyolult validátor függvény megalkotásával (Utóirat: a korrekt római szám validátor című fejezet).
Az alábbi logikai algoritmus magja a www.stackoverflow.com című oldalról származik, amelyet én saját kódokkal egészítettem ki.
Ezen ne lepődjünk meg: nem számítva a kötelező szerzői jogi megjegyzést (a mostani esetben ez a forrásmegjelölés, azaz www.stackoverflow.com), a problémák felmerülésekor a gyakorló programozók első dolga az Interneten való rákeresés szokott lenni.
A feladat funkcióit természetesen külön metódusokban kell megvalósítanunk, ezek a következők:
-
adatBekeres() - bekéri a római számot String formájában, meghívja az ellenőrző metódust és amíg rossz a bemeneti adat, hibaüzenetet küld,
-
ellenorzesString() - (korlátozottan) ellenőrzi a bemeneti karakterfüzért, a rendszerbe más karakter nem kerülhet mint I, V, X, L, C, D ,M.
-
konverter() - a szamitas() metódus segítségével átkonvertálja a római számot arab számmá.
import java.util.Scanner;
public class Main {
static String adatBekeres(){
Scanner in = new Scanner (System.in);
String romaiString = new String();
boolean rosszAdat = false;
System.out.println ("Kérem, hogy adja meg a római számot!");
do{
romaiString = in.nextLine();
rosszAdat = ellenorzesString(romaiString);
}while (rosszAdat == true);
rosszAdat = false;
return romaiString;
}
static boolean ellenorzesString(String romaiString){
boolean rosszAdat = false;
char karakter = romaiString.charAt(0);
if(karakter == '0'){
rosszAdat = true;
}
for(int i = 0; i < romaiString.length(); i++){
karakter = romaiString.charAt(i);
if(karakter != 'I'
&& karakter != 'V'
&& karakter != 'X'
&& karakter != 'L'
&& karakter != 'C'
&& karakter != 'D'
&& karakter != 'M'){
rosszAdat = true;
break;
}
}
if(rosszAdat == true){
System.out.println("Kérem, hogy megfelelő karaktereket adjon meg (I,V,X,L,C,D,M)");
}
return rosszAdat;
}
public static void konverter(String romaiString) {
int decimalisSzam = 0;
int utolsoSzam = 0;
String romaiSzam = romaiString.toUpperCase();
for (int i = romaiSzam.length() - 1; i >= 0 ; i--) {
char romaiSzamKarakter = romaiSzam.charAt(i);
switch (romaiSzamKarakter) {
case 'M':
decimalisSzam = szamitas(1000, utolsoSzam, decimalisSzam);
utolsoSzam = 1000;
break;
case 'D':
decimalisSzam = szamitas(500, utolsoSzam, decimalisSzam);
utolsoSzam = 500;
break;
case 'C':
decimalisSzam = szamitas(100, utolsoSzam, decimalisSzam);
utolsoSzam = 100;
break;
case 'L':
decimalisSzam = szamitas(50, utolsoSzam, decimalisSzam);
utolsoSzam = 50;
break;
case 'X':
decimalisSzam = szamitas(10, utolsoSzam, decimalisSzam);
utolsoSzam = 10;
break;
case 'V':
decimalisSzam = szamitas(5, utolsoSzam, decimalisSzam);
utolsoSzam = 5;
break;
case 'I':
decimalisSzam = szamitas(1, utolsoSzam, decimalisSzam);
utolsoSzam = 1;
break;
}
}
System.out.println(decimalisSzam);
}
public static int szamitas(int decimalisSzam, int utolsoSzam, int
utolsoDecimalisSzam) {
if (utolsoSzam > decimalisSzam) {
return utolsoDecimalisSzam - decimalisSzam;
} else {
return utolsoDecimalisSzam + decimalisSzam;
}
}
public static void main(java.lang.String args[]) {
String romaiString = adatBekeres();
konverter(romaiString);
}
}
Végeredmény:
Kérem, hogy adja meg a római számot!
MMMCMXCIX
3999
A kód működőképes, de valójában az ellenőrzési rész (ellenorzesString()) pontatlan, mert a római számok jelölése további belső szabályokat is magában hordoz. Így lehetséges, hogy például a római V (5) helyett IIIII karakterkombinációt is begépelhetünk, ami nem megengedett. Az érvényességi vizsgálatokat az Utóirat: a korrekt római szám validátor című fejezetben tanulmányozhatjuk. Az alábbi, futtatható Java-kód már az ott publikált kódot tartalmazza. Az alábbi Java-kódban az ellenorzesString() nevű metódust romaiSzamValidator() névre neveztük át:
import java.util.Scanner;
public class Main {
static char karakter;
static boolean rosszAdat = false;
static int tombIndexOfKarakter[] = new int[7];
static String [] nemMegengedettString = new String[58];
static {
nemMegengedettString[0] = "IIII";
nemMegengedettString[1] = "IIIII";
nemMegengedettString[2] = "IIIIII";
nemMegengedettString[3] = "IIIIIII";
nemMegengedettString[4] = "IIIIIIII";
nemMegengedettString[5] = "IIIIIIIII";
nemMegengedettString[6] = "IIV";
nemMegengedettString[7] = "IIIV";
nemMegengedettString[8] = "VV";
nemMegengedettString[9] = "VVV";
nemMegengedettString[10] = "VVVV";
nemMegengedettString[11] = "VVVVV";
nemMegengedettString[12] = "VVVVVV";
nemMegengedettString[13] = "VVVVVVV";
nemMegengedettString[14] = "VVVVVVVV";
nemMegengedettString[15] = "VVVVVVVVV";
nemMegengedettString[16] = "IIX";
nemMegengedettString[17] = "IIIX";
nemMegengedettString[18] = "XXXX";
nemMegengedettString[19] = "XXXXX";
nemMegengedettString[20] = "XXXXXX";
nemMegengedettString[21] = "XXXXXXX";
nemMegengedettString[22] = "XXXXXXXX";
nemMegengedettString[23] = "XXXXXXXXX";
nemMegengedettString[24] = "XXL";
nemMegengedettString[25] = "LL";
nemMegengedettString[26] = "LLL";
nemMegengedettString[27] = "LLLL";
nemMegengedettString[28] = "LLLLL";
nemMegengedettString[29] = "LLLLLL";
nemMegengedettString[30] = "LLLLLLL";
nemMegengedettString[31] = "LLLLLLLL";
nemMegengedettString[32] = "LLLLLLLLL";
nemMegengedettString[33] = "XXC";
nemMegengedettString[34] = "CCCC";
nemMegengedettString[35] = "CCCCC";
nemMegengedettString[36] = "CCCCCC";
nemMegengedettString[37] = "CCCCCCC";
nemMegengedettString[38] = "CCCCCCCC";
nemMegengedettString[39] = "CCCCCCCCC";
nemMegengedettString[40] = "CCD";
nemMegengedettString[41] = "CCCD";
nemMegengedettString[42] = "DD";
nemMegengedettString[43] = "DDD";
nemMegengedettString[44] = "DDDD";
nemMegengedettString[45] = "DDDDD";
nemMegengedettString[46] = "DDDDDD";
nemMegengedettString[47] = "DDDDDDD";
nemMegengedettString[48] = "DDDDDDDD";
nemMegengedettString[49] = "DDDDDDDDD";
nemMegengedettString[50] = "CCM";
nemMegengedettString[51] = "CCCM";
nemMegengedettString[52] = "MMMM";
nemMegengedettString[53] = "MMMMM";
nemMegengedettString[54] = "MMMMMM";
nemMegengedettString[55] = "MMMMMMM";
nemMegengedettString[56] = "MMMMMMMM";
nemMegengedettString[57] = "MMMMMMMMM";
}
static String adatBekeres(){
Scanner in = new Scanner (System.in);
String romaiString = "";
boolean rosszAdat = false;
System.out.println ("Kérem, hogy adja meg a római számot!");
do{
romaiString = in.nextLine();
rosszAdat =
romaiSzamValidator(romaiString);
if(rosszAdat == true){
System.out.println("Kérem, hogy csak megfelelő karaktereket
és kombinációkat adjon meg (I,V,X,L,C,D,M)\nÚjra!");
}
}while (rosszAdat == true);
return romaiString;
}
static boolean romaiSzamValidator(String romaiString){
rosszAdat = false;
rosszAdat = hossz(romaiString);
rosszAdat = karakter(romaiString);
rosszAdat = nemMegengedettString(romaiString);
rosszAdat = nemMegengedettString2(romaiString);
rosszAdat = karakterPozicio(romaiString);
rosszAdat = karakterPozicio2(romaiString);
return rosszAdat;
}
//Hiba: például nincs bemeneti String
vagy VVVVVVVVVV (10 karakter)
static boolean hossz(String romaiString){
boolean rosszAdat = false;
if((romaiString.length() < 1 || romaiString.length() > 9)){
rosszAdat = true;
}
return rosszAdat;
}
//Hiba: például a
static boolean karakter(String
romaiString){
for(int i = 0; i < romaiString.length(); i++){
karakter = romaiString.charAt(i);
if(karakter != 'I'
&& karakter != 'V'
&& karakter != 'X'
&& karakter != 'L'
&& karakter != 'C'
&& karakter != 'D'
&& karakter != 'M'){
rosszAdat = true;
}
}
return rosszAdat;
}
//Hiba: például IIIIIIIII
static boolean
nemMegengedettString(String romaiString){
for(int i = 0; i < nemMegengedettString.length; i++){
if(nemMegengedettString[i].equals(romaiString)){
rosszAdat = true;
}
else
continue;
}
return rosszAdat;
}
//Hiba: például IIIVVIII
static boolean
nemMegengedettString2(String romaiString){
for(int i = 0; i < romaiString.length(); i++){
for(int j = 0; j <
nemMegengedettString.length; j++){
if(romaiString.length() >=
nemMegengedettString[j].length()){
if(romaiString.contains(nemMegengedettString[j])){
rosszAdat = true;
}
}
else
continue;
}
}
return rosszAdat;
}
//Hiba:
//V nem lehet X előtt
//I, V nem lehet L előtt
//I, V, L nem lehet C előtt
//I, V, X, L nem lehet D előtt
//I, V, X, L, D nem lehet M előtt
static boolean karakterPozicio(String
romaiString){
//A tombIndexOfKarakter tömb
létrehozása, inicializálása
//M=0 D=1 C=2 L=3 X=4 V=5 I=6
for(int i = 0; i <
tombIndexOfKarakter.length; i++){
tombIndexOfKarakter[i] = 0;
}
for(int i = 0; i < romaiString.length(); i++){
karakter = romaiString.charAt(i);
if(karakter == 'M'){
tombIndexOfKarakter[0] = (romaiString.indexOf('M'))
+ 1;
}
else if(karakter == 'D'){
tombIndexOfKarakter[1] = (romaiString.indexOf('D'))
+ 1;
}
else if(karakter == 'C'){
tombIndexOfKarakter[2] = (romaiString.indexOf('C'))
+ 1;
}
else if(karakter == 'L'){
tombIndexOfKarakter[3] = (romaiString.indexOf('L'))
+ 1;
}
else if(karakter == 'X'){
tombIndexOfKarakter[4] = (romaiString.indexOf('X'))
+ 1;
}
else if(karakter == 'V'){
tombIndexOfKarakter[5] = (romaiString.indexOf('V'))
+ 1;
}
else if(karakter == 'I'){
tombIndexOfKarakter[6] = (romaiString.indexOf('I'))
+ 1;
}
}
if(((tombIndexOfKarakter[6] < tombIndexOfKarakter[0]) &&
((tombIndexOfKarakter[6] != 0) && (tombIndexOfKarakter[0] != 0))
|| (tombIndexOfKarakter[5] < tombIndexOfKarakter[0]) &&
((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[0] != 0))
|| (tombIndexOfKarakter[4] < tombIndexOfKarakter[0]) &&
((tombIndexOfKarakter[4] != 0) && (tombIndexOfKarakter[0] != 0))
|| (tombIndexOfKarakter[3] < tombIndexOfKarakter[0]) &&
((tombIndexOfKarakter[3] != 0) && (tombIndexOfKarakter[0] != 0))
|| (tombIndexOfKarakter[2] < tombIndexOfKarakter[0]) &&
((tombIndexOfKarakter[2] != 0) && (tombIndexOfKarakter[0] != 0))
)){
rosszAdat = true;
}
if(((tombIndexOfKarakter[6] < tombIndexOfKarakter[1]) &&
((tombIndexOfKarakter[6] != 0) && (tombIndexOfKarakter[1] != 0))
|| (tombIndexOfKarakter[5] < tombIndexOfKarakter[1]) &&
((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[1] != 0))
|| (tombIndexOfKarakter[4] < tombIndexOfKarakter[1]) &&
((tombIndexOfKarakter[4] != 0) && (tombIndexOfKarakter[1] != 0))
|| (tombIndexOfKarakter[3] < tombIndexOfKarakter[1]) &&
((tombIndexOfKarakter[3] != 0) && (tombIndexOfKarakter[1] != 0))
)){
rosszAdat = true;
}
if(((tombIndexOfKarakter[6] < tombIndexOfKarakter[2]) &&
((tombIndexOfKarakter[6] != 0) && (tombIndexOfKarakter[2] != 0))
|| (tombIndexOfKarakter[5] < tombIndexOfKarakter[2]) &&
((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[2] != 0))
|| (tombIndexOfKarakter[3] < tombIndexOfKarakter[2]) &&
((tombIndexOfKarakter[3] != 0) && (tombIndexOfKarakter[2] != 0))
)){
rosszAdat = true;
}
if(((tombIndexOfKarakter[6] < tombIndexOfKarakter[3]) &&
((tombIndexOfKarakter[6] != 0) && (tombIndexOfKarakter[3] != 0))
|| (tombIndexOfKarakter[5] < tombIndexOfKarakter[3]) &&
((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[3] != 0))
)){
rosszAdat = true;
}
if(((tombIndexOfKarakter[5] < tombIndexOfKarakter[4]) &&
((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[4] != 0))
)){
rosszAdat = true;
}
return rosszAdat;
}
static boolean karakterPozicio2(String romaiString){
//A tombIndexOfKarakter tömb
újrafelhasználása
//M=0 D=1 C=2 L=3 X=4 V=5 I=6
for(int i = 0; i <
tombIndexOfKarakter.length; i++){
tombIndexOfKarakter[i] = 0;
}
for(int i = 0; i < romaiString.length(); i++){
karakter = romaiString.charAt(i);
if(karakter == 'M'){
tombIndexOfKarakter[0] += 1;
}
else if(karakter == 'D'){
tombIndexOfKarakter[1] += 1;
}
else if(karakter == 'C'){
tombIndexOfKarakter[2] += 1;
}
else if(karakter == 'L'){
tombIndexOfKarakter[3] += 1;
}
else if(karakter == 'X'){
tombIndexOfKarakter[4] += 1;
}
else if(karakter == 'V'){
tombIndexOfKarakter[5] += 1;
}
else if(karakter == 'I'){
tombIndexOfKarakter[6] += 1;
}
}
//M-hiba: például MIMI, MIIM, egyetlen
kivétel MMCM
if(tombIndexOfKarakter[0] == 2 && !romaiString.contains("MM")){
rosszAdat = true;
}
else if(romaiString.equals("MMCM") ){
rosszAdat = false;
return rosszAdat;
}
else if(tombIndexOfKarakter[0] == 3 && !romaiString.contains("MMM") ){
rosszAdat = true;
}
//D-hiba: például DMD
else if(tombIndexOfKarakter[1] > 1){
rosszAdat = true;
}
//C-hiba: például CXC, CCIC, egyetlen
kivétel CCXC
else if(tombIndexOfKarakter[2] == 2 && !romaiString.contains("CC")){
rosszAdat = true;
}
else if(romaiString.equals("CCXC") ){
rosszAdat = false;
return rosszAdat;
}
else if(tombIndexOfKarakter[2] == 3 && !romaiString.contains("CCC")){
rosszAdat = true;
}
//L-hiba: például LIL
else if(tombIndexOfKarakter[3] > 1){
rosszAdat = true;
}
//X-hiba: XIXI, egyetlen kivétel XIX
else if(romaiString.equals("XIX") ){
rosszAdat = false;
return rosszAdat;
}
else if(tombIndexOfKarakter[4] == 2 && !romaiString.contains("XX")){
rosszAdat = true;
}
//XXVX, egyetlen kivétel XXIX
else if(romaiString.equals("XXIX") ){
rosszAdat = false;
return rosszAdat;
}
else if(tombIndexOfKarakter[4] == 3 && !romaiString.contains("XXX")){
rosszAdat = true;
}
//V-hiba: például VIV
else if(tombIndexOfKarakter[5] > 1){
rosszAdat = true;
}
//I-hiba: például IXI, IVII
if(tombIndexOfKarakter[6] == 2 && !romaiString.contains("II")){
rosszAdat = true;
}
else if(tombIndexOfKarakter[6] == 3 && !romaiString.contains("III")){
rosszAdat = true;
}
return rosszAdat;
}
public static void konverter(String romaiString) {
int decimalisSzam = 0;
int utolsoSzam = 0;
String romaiSzam = romaiString.toUpperCase();
for (int i = romaiSzam.length() - 1; i >= 0 ; i--) {
char romaiSzamKarakter = romaiSzam.charAt(i);
switch (romaiSzamKarakter) {
case 'M':
decimalisSzam
= szamitas(1000, utolsoSzam, decimalisSzam);
utolsoSzam =
1000;
break;
case 'D':
decimalisSzam
= szamitas(500, utolsoSzam, decimalisSzam);
utolsoSzam =
500;
break;
case 'C':
decimalisSzam
= szamitas(100, utolsoSzam, decimalisSzam);
utolsoSzam =
100;
break;
case 'L':
decimalisSzam
= szamitas(50, utolsoSzam, decimalisSzam);
utolsoSzam =
50;
break;
case 'X':
decimalisSzam
= szamitas(10, utolsoSzam, decimalisSzam);
utolsoSzam =
10;
break;
case 'V':
decimalisSzam
= szamitas(5, utolsoSzam, decimalisSzam);
utolsoSzam =
5;
break;
case 'I':
decimalisSzam
= szamitas(1, utolsoSzam, decimalisSzam);
utolsoSzam =
1;
break;
}
}
System.out.println(decimalisSzam);
}
public static int szamitas(int decimalisSzam, int utolsoSzam, int
utolsoDecimalisSzam) {
if (utolsoSzam > decimalisSzam) {
return utolsoDecimalisSzam - decimalisSzam;
} else {
return utolsoDecimalisSzam + decimalisSzam;
}
}
public static void main(java.lang.String args[]) {
String romaiString = adatBekeres();
konverter(romaiString);
}
}
Végeredmény:
Kérem, hogy adja meg a római számot!
IIIIIIIIII
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
IIIIVII
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
A
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
VX
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
MIMI
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
DMD
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
CCIC
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
LIL
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
XIXI
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
XXVX
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
VIV
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
IVII
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
IIII
Kérem, hogy csak megfelelő karaktereket és kombinációkat adjon meg (I,V,X,L,C,D,M)
Újra!
III
3