29

使用 PHP,我想将包含罗马数字的字符串转换为其整数表示形式。我需要这个,因为我需要对它们进行计算。

关于罗马数字的维基百科

只识别基本的罗马数字字符就足够了,例如:

$roman_values=array(
    'I' => 1,
    'V' => 5,
    'X' => 10,
    'L' => 50,
    'C' => 100,
    'D' => 500,
    'M' => 1000,
);

这意味着可能的最高数字是 3999 (MMMCMXCIX)。N除了只支持正整数之外,我将使用它来表示零。

我不能将 PEAR 库用于罗马数字。

我发现这个关于如何测试字符串是否包含有效罗马数字的好问题:

如何仅将有效的罗马数字与正则表达式匹配?

对此进行编码的最佳方法是什么?

4

13 回答 13

40

这个怎么样:

$romans = array(
    'M' => 1000,
    'CM' => 900,
    'D' => 500,
    'CD' => 400,
    'C' => 100,
    'XC' => 90,
    'L' => 50,
    'XL' => 40,
    'X' => 10,
    'IX' => 9,
    'V' => 5,
    'IV' => 4,
    'I' => 1,
);

$roman = 'MMMCMXCIX';
$result = 0;

foreach ($romans as $key => $value) {
    while (strpos($roman, $key) === 0) {
        $result += $value;
        $roman = substr($roman, strlen($key));
    }
}
echo $result;

它应该为提供的输出 3999 $roman。它似乎适用于我有限的测试:

MCMXC = 1990
MM = 2000
MMXI = 2011
MCMLXXV = 1975

您可能还想先进行一些验证:-)

于 2011-06-07T13:45:04.123 回答
10

我不确定你是否有 ZF,但如果你(或任何正在阅读本文的人)在这里做我的片段:

$number = new Zend_Measure_Number('MCMLXXV', Zend_Measure_Number::ROMAN);
$number->convertTo (Zend_Measure_Number::DECIMAL);
echo $number->getValue();
于 2011-06-07T13:34:24.787 回答
10

这是我想出的,我也添加了有效性检查。

class RomanNumber {
    //array of roman values
    public static $roman_values=array(
        'I' => 1, 'V' => 5, 
        'X' => 10, 'L' => 50,
        'C' => 100, 'D' => 500,
        'M' => 1000,
    );
    //values that should evaluate as 0
    public static $roman_zero=array('N', 'nulla');
    //Regex - checking for valid Roman numerals
    public static $roman_regex='/^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/';

    //Roman numeral validation function - is the string a valid Roman Number?
    static function IsRomanNumber($roman) {
         return preg_match(self::$roman_regex, $roman) > 0;
    }

    //Conversion: Roman Numeral to Integer
    static function Roman2Int ($roman) {
        //checking for zero values
        if (in_array($roman, self::$roman_zero)) {
            return 0;
        }
        //validating string
        if (!self::IsRomanNumber($roman)) {
            return false;
        }

        $values=self::$roman_values;
        $result = 0;
        //iterating through characters LTR
        for ($i = 0, $length = strlen($roman); $i < $length; $i++) {
            //getting value of current char
            $value = $values[$roman[$i]];
            //getting value of next char - null if there is no next char
            $nextvalue = !isset($roman[$i + 1]) ? null : $values[$roman[$i + 1]];
            //adding/subtracting value from result based on $nextvalue
            $result += (!is_null($nextvalue) && $nextvalue > $value) ? -$value : $value;
        }
        return $result;
    }
}
于 2011-06-09T10:01:05.027 回答
4

快速的想法 - 从右到左遍历罗马数字,如果$current(更左边)的值小于$previous,则从结果中减去它,如果更大,则添加它。

$romanValues=array(
    'I' => 1,
    'V' => 5,
    'X' => 10,
    'L' => 50,
    'C' => 100,
    'D' => 500,
    'M' => 1000,
);
$roman = 'MMMCMXCIX';

// RTL
$arabic = 0;
$prev = null;
for ( $n = strlen($roman) - 1; $n >= 0; --$n ) {
    $curr = $roman[$n];
    if ( is_null($prev) ) {
        $arabic += $romanValues[$roman[$n]];
    } else {
        $arabic += $romanValues[$prev] > $romanValues[$curr] ? -$romanValues[$curr] : +$romanValues[$curr];
    }
    $prev = $curr;
}
echo $arabic, "\n";

// LTR
$arabic = 0;
$romanLength = strlen($roman);
for ( $n = 0; $n < $romanLength; ++$n ) {
    if ( $n === $romanLength - 1 ) {
        $arabic += $romanValues[$roman[$n]];
    } else {
        $arabic += $romanValues[$roman[$n]] < $romanValues[$roman[$n+1]] ? -$romanValues[$roman[$n]] : +$romanValues[$roman[$n]];
    }
}
echo $arabic, "\n";

还应该添加一些罗马数字的验证,尽管你说你已经找到了如何做到这一点。

于 2011-06-07T13:22:35.873 回答
3

版权属于此博客(顺便说一句!) http://scriptsense.blogspot.com/2010/03/php-function-number-to-roman-and-roman.html

<?php

function roman2number($roman){
    $conv = array(
        array("letter" => 'I', "number" => 1),
        array("letter" => 'V', "number" => 5),
        array("letter" => 'X', "number" => 10),
        array("letter" => 'L', "number" => 50),
        array("letter" => 'C', "number" => 100),
        array("letter" => 'D', "number" => 500),
        array("letter" => 'M', "number" => 1000),
        array("letter" => 0, "number" => 0)
    );
    $arabic = 0;
    $state = 0;
    $sidx = 0;
    $len = strlen($roman);

    while ($len >= 0) {
        $i = 0;
        $sidx = $len;

        while ($conv[$i]['number'] > 0) {
            if (strtoupper(@$roman[$sidx]) == $conv[$i]['letter']) {
                if ($state > $conv[$i]['number']) {
                    $arabic -= $conv[$i]['number'];
                } else {
                    $arabic += $conv[$i]['number'];
                    $state = $conv[$i]['number'];
                }
            }
            $i++;
        }

        $len--;
    }

    return($arabic);
}


function number2roman($num,$isUpper=true) {
    $n = intval($num);
    $res = '';

    /*** roman_numerals array ***/
    $roman_numerals = array(
        'M' => 1000,
        'CM' => 900,
        'D' => 500,
        'CD' => 400,
        'C' => 100,
        'XC' => 90,
        'L' => 50,
        'XL' => 40,
        'X' => 10,
        'IX' => 9,
        'V' => 5,
        'IV' => 4,
        'I' => 1
    );

    foreach ($roman_numerals as $roman => $number)
    {
        /*** divide to get matches ***/
        $matches = intval($n / $number);

        /*** assign the roman char * $matches ***/
        $res .= str_repeat($roman, $matches);

        /*** substract from the number ***/
        $n = $n % $number;
    }

    /*** return the res ***/
    if($isUpper) return $res;
    else return strtolower($res);
}

/* TEST */
echo $s=number2roman(1965,true);
echo "\n and bacK:\n";
echo roman2number($s);


?>
于 2011-06-07T13:10:56.407 回答
1

刚刚偶然发现了这个美女,不得不把它全部贴出来:

function roman($N)
{
    $c = 'IVXLCDM';
    for ($a = 5, $b = $s = ''; $N; $b++, $a ^= 7)
    {
        for (
            $o = $N % $a, $N = $N / $a ^ 0;

            $o--;

            $s = $c[$o > 2 ? $b + $N - ($N &= -2) + $o = 1 : $b] . $s
        );
    }
    return $s;
}
于 2013-02-19T10:46:32.367 回答
1

我参加聚会迟到了,但这是我的。假定字符串中的数字有效,但不测试有效的罗马数字,不管是什么......似乎没有达成共识。此函数适用于VC (95)、MIM (1999) 或MMMMMM (6000) 等罗马数字。

function roman2dec( $roman ) {
    $numbers = array(
        'I' => 1,
        'V' => 5,
        'X' => 10,
        'L' => 50,
        'C' => 100,
        'D' => 500,
        'M' => 1000,
    );

    $roman = strtoupper( $roman );
    $length = strlen( $roman );
    $counter = 0;
    $dec = 0;
    while ( $counter < $length ) {
        if ( ( $counter + 1 < $length ) && ( $numbers[$roman[$counter]] < $numbers[$roman[$counter + 1]] ) ) {
            $dec += $numbers[$roman[$counter + 1]] - $numbers[$roman[$counter]];
            $counter += 2;
        } else {
            $dec += $numbers[$roman[$counter]];
            $counter++;
        }
    }
    return $dec;
}
于 2013-02-03T07:35:08.717 回答
0

我只是在大约 10 分钟内写了这个,它并不完美,但似乎适用于我给它的少数测试用例。我没有强制执行允许从什么中减去哪些值,这只是一个基本循环,它将当前字母值与序列中的下一个字母值(如果存在)进行比较,然后添加该值或添加减去的数量总计:

$roman = strtolower($_GET['roman']);

$values = array(
'i' => 1,
'v' => 5,
'x' => 10,
'l' => 50,
'c' => 100,
'd' => 500,
'm' => 1000,
);
$total = 0;
for($i=0; $i<strlen($roman); $i++)
{
    $v = $values[substr($roman, $i, 1)];
    $v2 = ($i < strlen($roman))?$values[substr($roman, $i+1, 1)]:0;

    if($v2 && $v < $v2)
    {
        $total += ($v2 - $v);
        $i++;
    }
    else
        $total += $v;

}

echo $total;
于 2012-04-02T16:13:11.853 回答
0

定义您自己的架构!(可选的)

function rom2arab($rom,$letters=array()){
    if(empty($letters)){
        $letters=array('M'=>1000,
                       'D'=>500,
                       'C'=>100,
                       'L'=>50,
                       'X'=>10,
                       'V'=>5,
                       'I'=>1);
    }else{
        arsort($letters);
    }
    $arab=0;
    foreach($letters as $L=>$V){
        while(strpos($rom,$L)!==false){
            $l=$rom[0];
            $rom=substr($rom,1);
            $m=$l==$L?1:-1;
            $arab += $letters[$l]*$m;
        }
    }
    return $arab;
}

灵感来自andyb的回答

于 2011-06-07T23:33:25.347 回答
0
function Romannumeraltonumber($input_roman){
  $di=array('I'=>1,
            'V'=>5,
            'X'=>10,
            'L'=>50,
            'C'=>100,
            'D'=>500,
            'M'=>1000);
  $result=0;
  if($input_roman=='') return $result;
  //LTR
  for($i=0;$i<strlen($input_roman);$i++){ 
    $result=(($i+1)<strlen($input_roman) and 
          $di[$input_roman[$i]]<$di[$input_roman[$i+1]])?($result-$di[$input_roman[$i]]) 
                                                        :($result+$di[$input_roman[$i]]);
   }
 return $result;
}
于 2015-05-06T23:10:45.970 回答
0
function parseRomanNumerals($input)
{
$roman_val = '';
$roman_length = strlen($input);
$result_roman = 0;
for ($x = 0; $x <= $roman_length; $x++) {
$roman_val_prev = $roman_val;
$roman_numeral = substr($input, $roman_length-$x,1);

switch ($roman_numeral) {
case "M":
$roman_val = 1000;
break;
case "D":
$roman_val = 500;
break;
case "C":
$roman_val = 100;
break;
case "L":
$roman_val = 50;
break;
case "X":
$roman_val = 10;
break;
case "V":
$roman_val = 5;
break;
case "I":
$roman_val = 1;
break;
default:
$roman_val = 0;
}
if ($roman_val_prev<$roman_val) {
$result_roman = $result_roman - $roman_val;
}
else {
$result_roman = $result_roman + $roman_val;
}
}
return abs($result_roman);
}
于 2019-05-03T10:51:24.377 回答
0
function rom_to_arabic($number) {

$symbols = array( 
    'M'  => 1000,  
    'D'  => 500, 
    'C'  => 100, 
    'L'  => 50, 
    'X'  => 10, 
    'V'  => 5, 
    'I'  => 1);

$a = str_split($number);

$i = 0;
$temp = 0;
$value = 0;
$q = count($a);
while($i < $q) {

    $thys = $symbols[$a[$i]];
    if(isset($a[$i +1])) {
        $next = $symbols[$a[$i +1]];
    } else {
        $next = 0;
    }

    if($thys < $next) {
        $value -= $thys;
    } else {
        $value += $thys;
    }

    $temp = $thys;
    $i++;
}

return $value;

}
于 2015-08-23T20:02:35.550 回答
0

哇!这些是相当多的答案,并且由它们组成的代码繁重!在我给出答案之前,我们先为此定义一个算法怎么样?

基础知识

  • 'CM' => 900不要在数组中存储多位罗马数字,如,或类似的东西。 如果您知道M - C(1000 - 100) 等于 900,那么最终,您应该只存储 和 的1000100CMI你不会有像for多位罗马数字901,对吗?任何这样做的答案对于理解罗马语法的人来说都是低效的。

算法

示例:LIX( 59)

  • 对数字执行 for 循环,从罗马数字字符串的末尾开始。 在我们的示例中:我们从“X”开始。
  • Greater-Than-Equal-To Case — 如果我们正在查看的值等于或大于最后一个值,只需将其添加到累积结果中即可。在我们的示例中:$result += $numeral_values["X"].
  • 小于案例——如果我们减去的值小于前一个数字,我们从累积结果中减去它。在我们的例子IX中,I1X10,所以,因为 1 小于 10,我们减去它:给我们 9。

演示

完整的在线工作演示

编码

function RomanNumeralValues() {
    return [
        'I'=>1,
        'V'=>5,
        'X'=>10,
        'L'=>50,
        'C'=>100,
        'D'=>500,
        'M'=>1000,
    ];
}

function ConvertRomanNumeralToArabic($input_roman){
    $input_length = strlen($input_roman);
    if($input_length === 0) {
        return $result;
    }
    
    $roman_numerals = RomanNumeralValues();
    
    $current_pointer = 1;
    $result = 0;
    
    for($i = $input_length - 1; $i > -1; $i--){ 
        $letter = $input_roman[$i];
        $letter_value = $roman_numerals[$letter];
        
        if($letter_value === $current_pointer) {
            $result += $letter_value;
        } elseif ($letter_value < $current_pointer) {
            $result -= $letter_value;
        } else {
            $result += $letter_value;
            $current_pointer = $letter_value;
        }
    }
    
    return $result;
}

print ConvertRomanNumeralToArabic("LIX");
于 2022-01-16T21:54:15.353 回答