Jump to content

roman numerals to decimal conversion question


jodunno
 Share

Recommended Posts

Hello fellow coders,

I am having trouble trying to convert roman numerals to decimals (yes i know some people like to call numbers Arabic or more precisely Western Arabic.) Anyway, i cannot figure out how to allow the subtractive numbers up to and including five. I have tried several methods. Anyone have suggestions for how to deal with this problem?

to be clear: MDVC for 1595 should be acceptable but MDVIIC should be invalid. I know that all converters online do not allow subtractive numbers but i want to allow them. I think that scholars have used numbers up to five in subtraction because i have not seen greater numbers being used.

I have created code for converting decimals to roman numerals but i am struggling with the reverse method. I think that this is a php and logic question, not necessarily a let me see code question. For example, i was thinking that if the first number and last number are larger than all numbers between then invalid but this logic excludes MDVC. Should i scan a string for all possible invalid numbers? how does a calculus person handle this situation? there must be a math solution. I suck at math, please offer advice.

I hope that this makes sense. i am tired now and sick of working on this problem. I need to clear my mind.

Thank you and Best wishes.

Link to comment
Share on other sites

Hello gw1500se, 

The code on the linked site is for converting decimals to roman numerals. I all ready created code for that. I used a different method for this conversion. I mapped all of the roman numbers by places, then split the number by places and display the corresponding numeral in its correct decimal place. Ones, Tens, Hundreds or Thousands.

Anyway, now i want to convert roman numbers into decimal numbers but still allow subtracive roman numbers up to and including five.

Thus, MDVC for 1595 (M=1000, D=500, 100-5 - 95 or VC) instead of the normal Roman number for 1595 (MDXCV). I want to allow both forms in the conversion but disallow subtractive numbers greater than 5 (so MDVIIC is invalid for 1593).

I can't figure out how to accomplish this task. I can only figure out how to disallow all subtractive numbers.

Best wishes and Thank you for reading and trying to offer help. :-)

Link to comment
Share on other sites

Hello gw1500se,

Thank you for taking time to assist me. I appreciate it very much.

I saw those posts at stackoverflow when i searched google for examples. I tried a few and the results are the same as my attempts at filtering subtractive numbers: not working. For example, MDVIIC should be invalid but it is an invalid method of specifying 1593 but ait is handled in code as 1605. My code does the same thing.

Is it stupid of me to think that a valid number array versus invalid number array is the best route here? i was wondering if i need to tell my code was is valid versus invalid and go from there.  Perhaps there is no easy way to do this.

Best wishes.

Link to comment
Share on other sites

In general, if you have problems with code not doing what you want, posting said code is probably a good first step. There are multiple ways of enforcing the digit rules so knowing what your current code is will help identify what approach will work best.

Link to comment
Share on other sites

Hi Requinix,

i'm trying to avoid trouble and not ask for code help. I am sorry that my mind is unable to grasp this right now. I apologize for having to ask for help but i am so mentally tired. I can't see the answer right now. I'm doing something wrong.

Before i post my code (i have to fetch it from my other pc, i'll be right back), i want to tell everyone that i have it allowing MDVC and rendering MDVIC or MDVIIC etc as invalid. but now my childish rules are blocking MDIC and MDIIC and MDIIIC. I am missing something simple here and i just can't grasp it. I'm getting very frustrated.

Thank you all for trying to assist me despite my poor programming skills.

I shall return with my latest filtering attempt.

Link to comment
Share on other sites

okay, so here is my last attempt. @Barand my current code allows MCM, filters MDVIIIC but allows MDVC. Now the problem is that MDIC fails. I'm missing something and stupidly so. Maybe other eyes and minds can help me nail this?

<?php
//limit 3999 or MMMCMXCIX
$RomanDecimal = array('I' => 1, 'V' => 5, 'X' => 10, 'L' => 50, 'C' => 100, 'D' => 500, 'M' => 1000);
$total = $add = $subtract = 0;
//if L C D or M follow any V followed by one or more ones, then invalid
//invalid before L C D or M: VI, VII, VIII, IX

$RomanNumeral = 'MCM';
$Subtractive = array('IX', 'VIII', 'VII', 'VI');
$FollowedBy = array('L', 'C', 'D', 'M');

foreach ($Subtractive as $n) {
  foreach ($FollowedBy as $m) {
    $pos = strpos($RomanNumeral, $n . $m);
    if ($pos !== false) { $invalid = 1; break(2); }
  }
}
if (!empty($invalid)) { echo 'invalid number'; exit; }
echo $RomanNumeral . '<br>';
$RomanNumeral = str_split($RomanNumeral); $PlaceCount = count($RomanNumeral);
$count = 1; $total = 0;
foreach ($RomanNumeral as $i) {
  if (!empty($skip)) { $skip = 0; $count++; continue; }
  if ($count < $PlaceCount) {
    if ($RomanDecimal[$i] < $RomanDecimal[$RomanNumeral[$count]]) {
      if (in_array($RomanDecimal[$RomanNumeral[$count]], $FollowedBy)) { $subtract += $RomanDecimal[$i]; $count++; continue; }
      if ($RomanDecimal[$i] == $RomanDecimal[$RomanNumeral[$count]]) { $subtract += $RomanDecimal[$i]; $subtract += $RomanDecimal[$RomanNumeral[$count]]; $count++; $skip = 1; continue; }
      $subtract += $RomanDecimal[$i];
    } else {
      $add += $RomanDecimal[$i]; 
    }
  } else { $add += $RomanDecimal[$i]; }
  $count++;
}
  $total = $add - $subtract;
  echo $total;

?>

 

Link to comment
Share on other sites

Hi gw1500se,

Thank you for taking time to dig up this code, it is very helpful code. I have been so busy battling this concept and i forgot about validation. Excellent contribution! Much appreciated.

I am done battling for the day. I have an early appointment in the morning. I had an operation on my arm and i have a check up. Part of this code problem is my arm. I cannot use my mouse very much and typing creates pain. I am right handed and the surgery was on my right arm. Anyway, I go to bed soon (time difference here in Deutschland.) Although i still keep American time on my system.

Best wishes and Good night everyone. I'll try again tomorrow.

Link to comment
Share on other sites

Just for fun ...

echo ad_decimo('XIIX');        // 18         (used by Roman 18th legion)
echo ad_decimo('MCMIL');       // 1949
echo ad_decimo('MCMXLIX');     // 1949

function ad_decimo($str)
{
    $M = 1000;
    $D = 500;
    $C = 100;
    $L = 50;
    $X = 10;
    $V = 5;
    $I = 1;
         
    foreach (str_split(strtoupper($str)) as $r) {
        if (!isset($$r)) return 0;
        $ra[] = [ 'r' => $r, 'v' => $$r ];
    }
    $d = 0;
    foreach ($ra as $p => $a) {
        $d += $a['v'] * subtracto($ra, $p, $a['v']);
    }
    return $d;
}

function subtracto($rarr, $pos, $val)
{
    // is there a higher value letter following this one?    
    for($i=$pos+1, $k=count($rarr); $i<$k; $i++) {
        if ($rarr[$i]['v'] > $val) return -1;
    }
    return 1;
}

 

  • Like 1
Link to comment
Share on other sites

Hi Barand,

I hope that you are doing well. I haven't been here alot lately due to an operaion. I didn't forget about you.

I allready know that you are a top notch programmer and the code that you have posted illustrates this statement.

However, and trying to not seem ungrateful, there is still a problem which exists even in your functions. Let me explain, today i tried to work on this problem and i got a working solution (which is horrible compared to your code). Then i accidentally copied and pasted IC between XX, thus attempting to convert XICX. Again, it was completely by accident. My - thought to be working code - actually converted this number, which should be invalid. Your program also converts this number.

I am about to give up on this idea. I can't seem to define what is valid versus invalid. I'm certain that Donald Knuth could conjure a solution but i am unable to do so with my poor math skills. And really, my logic skills are not too impressive either since i can't see a way to define even an invalid number.

I thank you for your code contribution. I will either give up on this and accept the fact that invalid numbers will still be processed or i will work on this slowly as a side project.

Best wishes.

Link to comment
Share on other sites

I can see that smaller numbers before and after larger numbers with subtraction could be invalid but i have no idea how to code that concept with all of the other concepts smashed into the equation.

By the way, here is the code that i worked on this morning. LOL it is so kindergarten like compared to your code. As you can see, i just battle with code that i know to accomplish what i'm trying to do. I'm not at your level for sure. LOL

<?php
//limit 3999 or MMMCMXCIX
$RomanNumeralInput = strtoupper('XICX'); //XICX
$RomanNumeral = strtoupper($RomanNumeralInput);
$Ones = array('I' => 1, 'II' => 2, 'III' => 3, 'IV' => 4, 'V' => 5, 'VI' => 6, 'VII' => 7, 'VIII' => 8, 'IX' => 9);
$Multiples = array('X' => 10, 'XL' => 40, 'L' => 50, 'XC' => 90, 'C' => 100, 'CD' => 400, 'D' => 500, 'CM' => 900, 'M' => 1000);
$MultiplesR = array('IX', 'XL', 'XC', 'CD', 'CM');
$Filter = array_merge($Ones, $Multiples);
$invalid = $total = $add = $subtract = 0;

foreach ($MultiplesR as $m) {
    $pos = strpos($RomanNumeralInput, $m);
    if ($pos !== false) { $RomanNumeral = str_replace($m, '', $RomanNumeral);
    $add += $Filter[$m]; }
}
$validSubtraction = array('IV', 'III', 'II', 'I', 'V');
$FollowedBy = array('X', 'L', 'C', 'D', 'M');
foreach ($validSubtraction as $m) {
  foreach ($FollowedBy as $n) {
    $pos = strpos($RomanNumeral, $m . $n);
    if ($pos !== false) { $RomanNumeral = str_replace($m, '', $RomanNumeral);
      $subtract += $Ones[$m]; break(2); }
  }
}
$invalidSubtraction = array('I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX');
foreach ($invalidSubtraction as $m) {
  foreach ($FollowedBy as $n) {
    $pos = strpos($RomanNumeral, $m . $n);
    if ($pos !== false) { $invalid = 1; break(2); }
  }
}
$RomanNumeral = str_split($RomanNumeral);
$PlaceCount = count($RomanNumeral);
$count = 1;
foreach ($RomanNumeral as $i) {
  $add += $Filter[$i];
}
if (!$invalid) {
  $total = $add - $subtract;
  echo $RomanNumeralInput;
  echo '<br>';
  echo $total;
} else {
  echo 'invalid number';
}
?>

 

Link to comment
Share on other sites

I'm going to call it a day now. I am tired and my arm is hurting. I want to relax a bit before bed.

I hope that you, Barand, and everyone else has a splendid day (or evening or night).

Best wishes.

Link to comment
Share on other sites

Good morning, I have been thinking endlessly about this problem. I started to realize that one roadblock to understanding a roman numeral is a lack of isolation. I have no idea if a subtractive number exists or not. Thus, i was thinking that if we can separate paired numbers (VII or IX) versus singular numbers (X, M, C etc) then we will have a way to know what we are dealing with. Yet, i don't think that we need to isolate additive pairs (VI, VII etc.) because they will correctly be handled via addition from isolation. Thus, i have tried a quick script for subtractive isolation and used my previous code to detect a non standard subtraction. I need an opinion about this method. Do you think that it is a good idea? could this help us solve the problem?

here is my new code which isolates numbers excluding non standard subtraction. Tell me what you think, please.

<?php
//limit 3999 or MMMCMXCIX
$RomanNumeralInput = strtoupper('MMMCMXCIX'); //XICX ixcx
$RomanDecimal = array('I' => 1, 'IV' => 4, 'V' => 5, 'IX' => 9, 'X' => 10, 'XL' => 40, 'L' => 50, 'XC' => 90, 'C' => 100, 'CD' => 400, 'D' => 500, 'CM' => 900, 'M' => 1000);
$invalid = 0;

$Subtractive = array('IX', 'VIII', 'VII', 'VI');
$FollowedBy = array('X', 'L', 'C', 'D', 'M');
foreach ($Subtractive as $n) {
  foreach ($FollowedBy as $m) {
    $pos = strpos($RomanNumeralInput, $n . $m);
    if ($pos !== false) { $invalid = 1; break(2); }
  }
}
if ($invalid) { echo $RomanNumeralInput . ' is a nonstandard Roman Numeral'; exit; }

$RomanNumeral = str_split($RomanNumeralInput);
$Isolation = array();
$PlaceCount = count($RomanNumeral);
$count = 1;
$skip = 0;
foreach ($RomanNumeral as $i) {
  if ($skip) { $skip = 0; $count++; continue; }
  if ($count < $PlaceCount) {
    if ($RomanDecimal[$i] < $RomanDecimal[$RomanNumeral[$count]]) {
      array_push($Isolation, $i . $RomanNumeral[$count]);
      $count++; $skip = 1; continue;
    } array_push($Isolation, $i); $count++;
  } else { array_push($Isolation, $i); }
}
print_r($Isolation);

?>

so now that numbers are isolated, we could have a better shot at analyzing the number for non standard input. right?

Best wishes,

John

Link to comment
Share on other sites

Your subtractive array is far from exhaustive. You have IX and VI so why not XI and IV. And II as in XIIX?

16 hours ago, Barand said:

Looks like solution is to look for substrings of lower value number prior to an even larger one


    IX     C   X
   |___| 
     |
    -9    100  10 

 

This recursive solution implements the above approach giving

	XIIX                   18
	IXCX                  101
	XICX                   99
	MCMIL                1949
	MCMXLIX              1949
	MCMXXXXVIIII         1949
	MDCCCCXXXXVIIII      1949

Code

<?php
// Test values
$nums = ['XIIX', 'IXCX', 'XICX', 'MCMIL', 'MCMXLIX', 'MCMXXXXVIIII', 'MDCCCCXXXXVIIII' ];
echo '<pre><br>';
foreach ($nums as $rn) {
    printf( "\t%-20s %4d<br>", $rn, ad_decimo($rn) );
}
echo '</pre>';


function ad_decimo($str)
{
    $M = 1000;
    $D = 500;
    $C = 100;
    $L = 50;
    $X = 10;
    $V = 5;
    $I = 1;
         
    foreach (str_split(strtoupper($str)) as $r) {
        if (!isset($$r)) return 0;
        $ra[] = [ 'r' => $r, 'v' => $$r, 's' => 1 ];
    }
    $d = 0;
    //
    //  check each value and flag if it is subtractive
    //  then process contiguous subtractive values as a sub-numeral (recursive)
    //
    foreach ($ra as $p => $a) {
        $ra[$p]['s'] = subtracto($ra, $p, $a['v']);
    }
    $substr = '';
    foreach ($ra as $p => $a) {
        if ($a['s'] == 1) {
            $d -= ad_decimo($substr);    // recursive call to evaluate the substring of consecutive subtractive letters
            $substr = '';
            $d += $a['v'];
        }
        else {
            $substr .= $a['r'];
        }
    }
    $d -= ad_decimo($substr);            // process remaining group
    
    return $d;
}

function subtracto($rarr, $pos, $val)
{
    // a value is subtractive if followed by a higher value
    for($i=$pos+1, $k=count($rarr); $i<$k; $i++) {
        if ($rarr[$i]['v'] > $val) return -1;
    }
    return 1;
}

?>

 

Link to comment
Share on other sites

Hi Barand,

my new code post was the first attempt at isolation. The script is unfinished in the post. However, i just added the missing factors. My latest script appears to work as expected but i haven't tested every number combo.

I got started on this because i actually saw an old book with MDVC and i didn't know how to calculate that. I was introduced to subtractive roman numerals at that point. I couldn't even remeber what D was (now i know it is 500). I don't use Latin numbers. Anyway, my first thought was to use an online converter for the MDVC but they all rejected this number as invalid. I yelled at the screen "BUT I SEE IT IN AN OLD BOOK'. LOL. I decided that i want to add a roman numeral converter to my site that allows subtractive Roman numerals. However, i have not seen subtractive numbers greater than 5 and really it doesn't make sense to write VIIX instead of III. I can understand MDVC but not MDIXC because it would be easier to write MDCI. I wanted to control the subtraction to five.

Anyway, here is my latest code which also checks for my so-called valid subtraction and adds it to the final array. It seems to be working...

<?php
//limit 3999 or MMMCMXCIX
$RomanNumeralInput = strtoupper('xiix'); //XICX ixcx MDIIC
$RomanDecimal = array('I' => 1, 'IV' => 4, 'V' => 5, 'IX' => 9, 'X' => 10, 'XL' => 40, 'L' => 50, 'XC' => 90, 'C' => 100, 'CD' => 400, 'D' => 500, 'CM' => 900, 'M' => 1000);
$invalid = $subtraction = $subtractionCount = $subtractionIsolated = $append = 0;
echo $RomanNumeralInput . '<br>';

$Subtractive = array('IX', 'VIII', 'VII', 'VI');
$FollowedBy = array('X', 'L', 'C', 'D', 'M');
foreach ($Subtractive as $n) {
  foreach ($FollowedBy as $m) {
    $pos = strpos($RomanNumeralInput, $n . $m);
    if ($pos !== false) { $invalid = 1; break(2); }
  }
}
if ($invalid) { echo ' is a nonstandard Roman Numeral'; exit; }

$validSubtraction = array('IV', 'III', 'II', 'I', 'V');
foreach ($validSubtraction as $m) {
  foreach ($FollowedBy as $n) {
    $pos = stripos($RomanNumeralInput, $m . $n);
    if ($pos !== false && $m . $n !== 'IX') { $subtraction = 1; $subtractionCount = strlen($m . $n); $subtractionIsolated = $m . $n;
    $RomanNumeralInput = str_replace($m.$n, '', $RomanNumeralInput);
    break(2); }
  }
}
$Isolation = array();
if (!empty($RomanNumeralInput)) {
  $RomanNumeral = str_split($RomanNumeralInput);
  $PlaceCount = count($RomanNumeral); if ($pos >= $PlaceCount) { $append = 1; unset($pos); }
  $count = 1; $stringcount = 0;
  $skip = 0;
    foreach ($RomanNumeral as $i) {
      if (isset($pos) && $stringcount === $pos) { array_push($Isolation, $subtractionIsolated); unset($pos); }
      if ($skip) { $skip = 0; $count++; $stringcount++; continue; }
      if ($count < $PlaceCount) {
        if ($RomanDecimal[$i] < $RomanDecimal[$RomanNumeral[$count]]) {
          array_push($Isolation, $i . $RomanNumeral[$count]);
          $count++; $stringcount++; $skip = 1; continue;
        } array_push($Isolation, $i); $count++;
      } else { array_push($Isolation, $i); }
      $stringcount++;
    }
  if ($append) { array_push($Isolation, $subtractionIsolated); }
} else { array_push($Isolation, $subtractionIsolated); }
echo '<br>'; print_r($Isolation);
?>

the XICX is still irritating me. Which is why i keep trying to exclude it. It really is a strange non standard roman numeral. It annoys me like a mosquito. even ixcx but my latest script denies ixcx.

Link to comment
Share on other sites

well your code is very nice and seriously professional. The only negative aspect to your code is the calculation of non stadard numerals but noone should really care. However, it is your code not mine. I can't legally use someone elses code in a commercial application. Remember that my website is commercial. I'm not a thief, Sir. :-) I respect you and your intellectual property.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.