web-dev-qa-db-fra.com

Comment convertir un chiffre romain en entier en PHP?

En utilisant PHP, j'aimerais convertir une chaîne contenant un nombre romain en une représentation entière. J'ai besoin de cela parce que je dois faire des calculs sur eux.

Wikipedia sur les chiffres romains

Il suffirait de ne reconnaître que les caractères numériques de base, tels que:

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

Cela signifie que le nombre le plus élevé possible est 3999 (MMMCMXCIX). J'utiliserai N pour représenter zéro, à part cela, seuls les entiers positifs sont pris en charge.

Je ne peux pas utiliser la bibliothèque PEAR pour les nombres romains.

J'ai trouvé cette excellente question sur SO sur la manière de vérifier si la chaîne contient un chiffre romain valide:

Comment faites-vous correspondre uniquement les chiffres romains valides avec une expression régulière?

Quel serait le meilleur moyen de coder cela?

25
kapa

Que dis-tu de ça:

$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;

qui devrait générer 3999 pour le $roman fourni. Cela semble fonctionner pour mes tests limités:

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

Vous voudrez peut-être aussi faire quelques validations :-)

35
andyb

Je ne suis pas sûr si vous avez ZF ou non, mais au cas où vous (ou l'un de vous qui lisez ceci) faites ici mon extrait:

$number = new Zend_Measure_Number('MCMLXXV', Zend_Measure_Number::ROMAN);
$number->convertTo (Zend_Measure_Number::DECIMAL);
echo $number->getValue();
9
akond

C’est celui que j’ai proposé, j’ai également ajouté le contrôle de validité.

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;
    }
}
9
kapa

Idée rapide - parcourez le numéro romain de droite à gauche, si la valeur de $current (plus à gauche) est inférieure à $previous, puis soustrayez-la du résultat, si elle est plus grande, puis ajoutez-la.

$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";

Vous devez également ajouter une validation du nombre romain, bien que vous ayez déjà indiqué comment vous y prendre.

4
binaryLV

Copyrights concerne ce blog (d'ailleurs!) 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);


?>
3
publikz.com

Je viens de tomber sur cette beauté et je dois la publier partout:

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;
}
1
Daniel

Je suis en retard à la fête, mais voici le mien. Suppose que Numerals est valide dans la chaîne, mais ne recherche pas un nombre romain valide, peu importe ce que c'est ... il ne semble pas y avoir de consensus. Cette fonction fonctionnera pour les nombres romains commeVC(95), ouMIM(1999), ouMMMMMM(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;
}
1
akTed
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;

}
0
Dariusz Majchrzak

essayez cette fonction simple

function arabicToRomanNumber($arabicNumber='') {

            $arabicNumValue = intval($arabicNumber);
            $convertedNumber = '';
            $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);
            foreach ($romans as $roman => $romanValue) 
            {
                  $matchingNumber = intval($arabicNumValue / $romanValue); // Determine the number of matches
                  $convertedNumber .= str_repeat($roman, $matchingNumber); // Store that many characters
                  $arabicNumValue = $arabicNumValue % $romanValue; // Substract that from the number
            }

            return $convertedNumber;

      }

et enfin juste passer les chiffres arabes comme

echo arabicToRomanNumber('25'); // le résultat sera XXV

echo arabicToRomanNumber('250'); // le résultat sera CCL

0
Manojkiran.A

Je viens d'écrire cela en environ 10 minutes, ce n'est pas parfait, mais semble fonctionner pour les quelques cas de test que je lui ai donnés. Je n'applique pas quelles valeurs peuvent être soustraites de ce qui est, il s'agit simplement d'une boucle de base qui compare la valeur de lettre actuelle à la suivante dans la séquence (si elle existe) et ajoute ensuite la valeur ou ajoute le montant soustrait. au total:

$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;
0
Ashley Sheridan

Définissez votre propre schéma! (optionnel)

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;
}

Inspiré par la réponse de andyb

0
Shad
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;
}
0
scheruku