Arabskie cyfry na Rzymskie, i odwrotnie...

michaell

<font color="blue">Arabskie cyfry na Rzymskie, i odwrotnie...</span>

Witam. W Internecie można znaleźć wiele sposobów na konwersje systemu arabskiego na rzymski lub rzymskiego na arabski, aczkolwiek mój sposób wydaje mi się prosty, w dodatku funkcje mogą się nawzajem kontrolować przed błędami w zapisie.

Jeśli kogoś ten artykuł interesuje to przejdę do sedna sprawy:

#<font color="red">Założenie i ogólny zarys systemu:</span>
System liczb rzymskich z reguły wydaję się dość skomplikowany jeśli chodzi o przedstawienie go w formie algorytmu. Jednak, jeśliby się przyjrzeć dokładniej można stwierdzić, że jedyną rzeczą, która się zmienia wraz ze wzrostem wartości liczby to są tylko znaki. układ zawsze pozostaje taki sam, Np.:
Liczba 1 = I Liczba 100 = C
Liczba 2 = II Liczba 200 = CC
Liczba 3 = III Liczba 300 = CCC

<font color="red">Wyjątkiem są tysiące, gdzie każdy kolejny jest dodawany jako kolejna literka M, Np.:
5000 = MMMMM
</span>

Tak wiec system składania tych liczb jest bardzo podobny. Można łatwo znaleźć zależności pomiędzy wszystkimi literami:
<font color="green">I - 1
V - 5
X - 10
L - 50
C - 100
D - 500
M - 1000</span>

Jeśli by przyjąć, że skład danej liczby dzieli się na jeden, pięć i dziesięć można zapisać ogólny wzór gdzie w odpowiednie miejsca wstawi się odpowiednie litery biorąc pod uwagę podział jednostkowy w cyfrze arabskiej. Całość wygląda mniej więcej tak:

Jedności:
Litery: I, V, X
jeden = I, pięć = V, dziesięć = X.

Dziesiątki:
Litery: X, L, C
jeden = X, pięć = L, dziesięć = C.

Setki:
Litery: C, D, M
jeden = C, pięć = D, dziesięć = M.

Przykłady:
4 (4, 40, 400):
Wzór:
|jeden||pięć|
4 = IV
40 = XL
400 = CD

7 (7, 70, 700):
    <b>Wzór:</b>
        <i><b>|pięć||jeden||jeden|</b></i>
    7 = VII
    70 = LXX
    700 = DCC

Napiszemy teraz odpowiednią funkcję zajmującą się wyżej podaną zależnością:

Funkcja będzie zwracała wartość string, gdyż zwracać nam będzie pojedynczą liczbę w formacie rzymskim. Nazwiemy ją Wstaw.
Jako argumenty funkcji musimy podać zmienne:
num: integer - przechowywać będzie liczbę do konwersji
one, five, ten: string - zmienne działające na zasadzie wzoru podanego wyżej.

function Wstaw(num : integer; one, five, ten : string) : string;

Potrzebne nam też będą zmienne wewnętrzne funkcji:

var
  i: integer;
  T: string;

Zaczynamy. Najpierw musimy sprawdzać po kolei warunki jakie spełnia zmienna num. Jeśli Np.: jest mniejsza od 4 (czyli przyjmuje wartość 1, 2 lub 3 - wartości 0 nie może mieć ale o tym w dalszej części artykułu) to musimy wstawić tyle znaków zapisanych w zmiennej one.
W przypadku jeśli zmienna num przyjmuje wartości z przedziału 6 do 8 włącznie to należy postąpić podobnie, tylko najpierw wstawić znak zmiennej five, a następnie odpowiednią ilość znaków zmiennej one.
Całość funkcji ogólnie wydaje mi się zrozumiała dlatego nie będę jej konkretnie i szczegółowo rozpisywał. Na końcu oczywiście funkcja zwraca łańcuch znaków odpowiadający podanej liczbie.

function Wstaw(num : integer; one, five, ten : string) : string;

var
  i: integer;
  T: string;
begin
T:='';

if num < 4 then for i:=1 to num do T:=T+one;
if num = 4 then T:=one + five;
if num = 5 then T := five;
if (num > 5) and (num < 9) then
begin
  T:=five;
  for i:=1 to num - 5 do  T:=T+one;
end;
if num = 9 then T:=one+ten;

Result := T;
end;

W przypadku zamiany liczb rzymskich na arabskie, wystarczy po kolei dodawać odpowiednie wartości, pamiętając, że jeżeli przed znakiem większym stoi znak mniejszy to odejmujemy większy od mniejszego, a różnice dodajemy do całości.

 2.   <font color=red><b>Zamiana arabskich na rzymskie:</b></span>

Zajmijmy się teraz właściwą funkcją zamieniającą liczby rzymskie na arabskie.
Funkcja ta posiadać będzie tylko jeden parametr, mianowicie zmienną typy integer przechowującą liczbę do zamiany. Sama funkcja będzie zwracać string, w którym zapisana będzie ów liczba w postaci rzymskich znaków.

function NaRzymska(Number: integer): string;

Potrzebne nam będą mimo to dwie zmienne wewnętrzne w funkcji. Jedna typu integer, jako pomocnicza, a druga typu string jako tymczasowa do przechowywania liczby rzymskiej.

var
  i: integer;
  Rzymska: string;

Jak już wcześniej powiedziałem należy "wyciągnąć" z liczby arabskiej tysiące. setki, dziesiątki i jedności. Zaczniemy od tysięcy, a skończymy na jednościach. Najprostszym sposobem jest podzielenie liczby przez daną wartość, Np.: dzieląc liczbę przez tysiąc otrzymamy tysiące, gdyż kompilator automatycznie zaokrągli nam wynik w dół (pamiętać musimy w tym momencie, że działamy na zmiennych typu integer dlatego, nie mogą nam wyjść wyniki zmienno przecinkowe.

Tworzymy warunek:

if (number div 1000) > 0 then
    begin

Jeśli liczba tysięcy jest większa od zera (dlatego właśnie liczba podawana w poprzedniej funkcji nie może przyjąć zera).

for i := 1 to number div 1000 do
        Rzymska := Rzymska +'M';

Wstawiamy do zmiennej tymczasowej tyle literek "M" ile jest tysięcy. Jak już wspominałem, tysiące są wyjątkami w zapisie rzymskim.

Na końcu musimy pozbyć się tysięcy. Najprostszym sposobem jest użycie operatora mod, który zostawia tylko resztę z dzielenia.

number := number mod 1000;
    end;

W przypadku setek, podobnie jak i przy tysiącach musimy sprawdzić ile ich jest. Dzielimy liczbę przez 100.

if (number div 100) > 0 then
    begin

W tym momencie wywołujemy funkcję Wstaw, która automatycznie zwraca nam zamienioną liczbę w postaci rzymskiej cyfry.

Rzymska := Rzymska + Wstaw(number div 100,'C','D','M');

Na końcu pozbywamy się setek.

number := number mod 100;
    end;

W przypadku dziesiątek, postępujemy praktycznie identycznie jak w przypadku tysięcy, z tą różnicą, że dzielimy przez 10 i w funkcji wstaw podajemy odpowiednio literki: X, L, C.

Ostatnią rzeczą, która nam została to jedności. Nic prostszego, sprawdzamy czy liczba jest większa od 0, jeśli tak to wstawiamy cyfrę rzymską poprzez funkcję Wstaw z literkami: I, V, X.

if number > 0 then
    Rzymska := Rzymska + wstaw(number,'I','V','X');

Na końcu zwracamy do funkcji gotową liczbę rzymską

Result := Rzymska;
end;

I to by było wszystko jeśli chodzi o konwersje liczb arabskich na rzymskie.

 3.   <font color=red><b>Zamiana rzymskich na arabskie:</b></span>

Wspominałem wcześniej o co mniej więcej chodzi w tym procesie. Teraz mam zamiar rozwinąć bardziej szczegółowo ten temat.
Napiszmy więc funkcję:

function NaArabskie(rzym : string) : integer;

Zwraca ona wartość typu integer, czyli jak można się łatwo domyślić liczbę arabską. Posiada też jeden parametr typu string, czyli też nie trzeba się wysilać, jest to cyfra rzymska.
Musimy niestety podać kilka zmiennych wewnętrznych.
Oprócz zmiennych pomocniczych potrzebne nam będą:
add: integer czyli zmienna przechowująca liczbę która ma być dodana,
min: integer czyli zmienna przechowująca liczbę, która ma być odjęta od większej.
pom: boolean jeśli bezie trzeba odejmować, musimy pominąć następny znak, ta zmienna na przyjdzie z pomocą.

var
  i, add, min, sum: integer;
  pom: boolean;

Najpierw musimy wyzerować zmienną pom, ustawiamy ją na false.
Następnie tworzymy pętle for, której liczbą końcową będzie długość parametru rzym.

for i:= 1 to Length(rzym) do
  begin

Zaraz na początku pętli musimy wyzerować zmienne add i min.

Jeśli wcześniej nie odejmowaliśmy znaków, czyli jeśli zmienna pom = false:

if pom = false then
      begin

to do zmiennej add przypisujemy odpowiednią wartość w zależności od danego znaku w cyfrze rzymskiej, np.: M - add = 1000, V - add = 5.

case Rzym[i] of
            'M': add := 1000;
            'D': add := 500;
            'C': add := 100;
            'L': add := 50;
            'X': add := 10;
            'V': add := 5;
            'I': add := 1;
        end;

Jeszcze jednym ważnym elementem zanim przystąpimy do dodawania jest sprawdzenie, czy następny znak w łańcuchu nie jest większy do obecnego. Do zmiennej min przypisujemy wartość następnego znaku:

case Rzym[i + 1] of
            'M': min := 1000;
            'D': min := 500;
            'C': min := 100;
            'L': min := 50;
            'X': min := 10;
            'V': min := 5;
            'I': min := 1;
        end;

Sprawdzamy teraz czy następny znak jest większy, jeżeli tak, odejmuje od niego wartość obecnego znaku, a następnie różnice dodajemy do sumy. Ustawiamy też zmienną pom na true, musimy pominąć następny znak w dodawaniu.

if add < min then begin add := min - add; pom := true; end;

Dodajemy wartości do sumy.

sum := sum + add;

Ot cała filozofia, funkcje są gotowe do użycia.

 4.   <font color=red><b>Wykorzystanie funkcji:</b></span>

Wspomniałem na początku artykułu, że funkcje mogą sprawdzać poprawność formy zapisu. Jeśli ktoś faktycznie zna się na rzeczy, zauważy, że nie napisałem nic co by mogło pełnić tę rolę. Otóż ma racje ale można to w bardzo prosty sposób napisać. Jeśli chodzi o sprawdzanie cyfr arabskich to wydaje mi się, że nie ma potrzeby żebym coś tu tłumaczył, praktycznie wystarczy wstawić gdzieś w kodzie funkcje sprawdzającą czy wartość jest liczbą i po krzyku. Jeżeli chodzi o liczby rzymskie można w prosty sposób sprawdzić czy format jest poprawny:
Wystarczy jak po wprowadzeniu liczby rzymskiej obliczmy jej wynik, następnie wynik zamienimy spowrotem na cyfrę rzymską. Jeśli cyfra rzymska wprowadzona zgadza się z cyfrą rzymską obliczoną przez komputer, znaczy, że format jest poprawny. Poniższą procedurę można też łatwo przerobić na automatyczne poprawianie wpisanego tekstu.

var
rzymska: string;
arabska: int;
begin
arabska := NaArabska(rzymska);
if NaRzymska(arabska) = rzymska then
  writeln('poprawna') else
  writeln('bledna');

end;

Ogólnie wydaje mi się, że funkcje są bardzo elastyczne, można łatwo dopisać do nich wyjątki, jak np.: sposób zapisu po rosyjsku (4 = IIII). Można również rozbudować program o zamienianie większych cyfr niż 9999. To pozostawiam wam. Jeśli chodzi o informacje dotyczące samych cyfr rzymskich to polecam encyklopedie "Wiem".

Dziękuje.

Oto przykładowy program:


program Arabskie;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  Arabska : integer;

function Wstaw(num : integer; one, five, ten : string) : string;
var
  i: integer;
  T: string;
begin
T:='';

if num < 4 then for i:=1 to num do T:=T+one;
if num = 4 then T:=one + five;
if num = 5 then T := five;
if (num > 5) and (num < 9) then
begin
  T:=five;
  for i:=1 to num - 5 do  T:=T+one;
end;
if num = 9 then T:=one+ten;

Result := T;
end;

function NaRzymska(Number: integer): string;
var
  i: integer;
  Rzymska: string;
begin
if (number div 1000) > 0 then
    begin
      for i := 1 to number div 1000 do
        Rzymska := Rzymska +'M';
      number := number mod 1000;
    end;

  if (number div 100) > 0 then
    begin
      Rzymska := Rzymska + Wstaw(number div 100,'C','D','M');
      number := number mod 100;
    end;

  if (number div 10) > 0 then
    begin
      Rzymska := Rzymska + Wstaw(number div 10,'X','L','C');
      number := number mod 10;
    end;

  if number > 0 then
    Rzymska := Rzymska + wstaw(number,'I','V','X');
Result := Rzymska;
end;

function NaArabskie(rzym : string) : integer;
var
  i, add, min, sum: integer;
  pom: boolean;
begin
sum := 0;
pom := false;

for i:= 1 to Length(rzym) do
  begin
    add := 0;
    min := 0;

    if pom = false then
      begin
        case Rzym[i] of
            'M': add := 1000;
            'D': add := 500;
            'C': add := 100;
            'L': add := 50;
            'X': add := 10;
            'V': add := 5;
            'I': add := 1;
        end;

        case Rzym[i + 1] of
            'M': min := 1000;
            'D': min := 500;
            'C': min := 100;
            'L': min := 50;
            'X': min := 10;
            'V': min := 5;
            'I': min := 1;
        end;

        if add < min then begin add := min - add; pom := true; end;
        sum := sum + add;
      end
    else
      pom := false;
end;

result := sum;
end;

begin
    write('Podaj rok: ');
    readln(Arabska);

if (Arabska > 0) and (Arabska < 10000) then
begin
      writeln(NaRzymska(Arabska));
      writeln(inttostr(NaArabskie(NaRzymska(Arabska))));
end else
  writeln('Bledna data');
readln;

end.

0 komentarzy