Romeinse cijfers
Gepost door Jan Willem van der Veer op 02-11-2010 00:23.
Ondanks dat er al twee scripts in de library staan die iets met romeinse cijfers te doen hebbe, hier nog één. Ik denk dat deze wat toevoegd aan de SL om de volgende redenen:
1. Het is een two-way-script. Dus zowel van Romeinse cijfers naar decimale cijfers als van decimale naar Romeinse cijfers. Met checks voor de validiteit van romeinse cijfers.
2. De code is efficiënter.
3. Het bereik van deze scripts is hoger (en wel tot de officiële 3.888.888 die je maximaal met romeinse cijfers kunt halen).
4. Het is OOP (ligt er een beetje aan wat je daaronder verstaat).
Feedback is welkom. Suggesties ter verdere verbetering ook. Negatief commentaar mag je uitdrukken in een cijfer.
Ter vergelijking een verwijzing naar de andere scripts met vergelijkbare functionaliteit op deze site:
- http://www.pfz.nl/scripts/90-romeinse-cijfers-omzetten/
- http://www.pfz.nl/scripts/42-romeinse-cijfers/
Bestanden van dit script
romanNumerals.class.php
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | <?php
error_reporting(E_ALL);
class RomanNumerals {
/* Officially the numbers > 1000 should be overlined.
* In unicode there are several codepoints for this purpose.
* But for compatibility with ISO-8859 I used lowercase characters (more often used for this trick).
* See for example: http://jeankorte.ca/jk-roman-numeral-converter.html
*/
static private $asRomanTransTable = Array(
1e6 => 'm',
9e5 => 'cm',
5e5 => 'd',
4e5 => 'cd',
1e5 => 'c',
9e4 => 'xc',
5e4 => 'l',
4e4 => 'xl',
1e4 => 'x',
9e3 => 'ix',
5e3 => 'v',
4e3 => 'iv',
1e3 => 'M',
900 => 'CM',
500 => 'D',
400 => 'CD',
100 => 'C',
90 => 'XC',
50 => 'L',
40 => 'XL',
10 => 'X',
9 => 'IX',
5 => 'V',
4 => 'IV',
1 => 'I'
);
/**
* From decimal to Roman system.
*
* @param integer $iDecimal
* @return string The given decimal in Roman numeral system.
*/
public static function toRoman($iDecimal) {
# There is no Roman numeral representation below 1, and 3,999,999 is the longest number represented by Roman numerals
$decimalValue = (int) $iDecimal;
if (($iDecimal < 1) || ($iDecimal <= 0) || ($iDecimal > 3999999)) return NULL;
$sRoman = '';
foreach(self::$asRomanTransTable as $iNum => $sSymbol){
if ($iDecimal < 1) break;
while ($iDecimal >= $iNum){
$iDecimal -= $iNum;
$sRoman .= $sSymbol;
}
}
return $sRoman;
}
/**
* From roman to decimal numeral system
*
* @param string $sRoman
* @return integer 0 on failure.
*/
public static function toDecimal($sRoman){
if(!is_string($sRoman)) return 0;
$iStrLen = strlen($sRoman);
$iDoubleSymbol = $iDec = $iPos = 0;
foreach(self::$asRomanTransTable as $iNum => $sSymbol){
$iLen = strlen($sSymbol);
$iCount = 0;
if($iDoubleSymbol){
--$iDoubleSymbol;
continue;
}
# Mind the fact that 1000 in the Roman numeral system may be represented by M or i.
while((($sChunk = substr($sRoman, $iPos, $iLen)) == $sSymbol) || (($iNum < 1e4) && ($sChunk == strtr($sSymbol, 'iM', 'Mi')))){
if($iLen == 2) $iDoubleSymbol = 3 - 2*($iNum % 3);
$iDec += $iNum;
$iPos += $iLen;
# All symbols that represent 1eX may appear at maximum three times. All other symbols may only represent one time in a roman number.
if(fmod(log10($iNum), 1) || (++$iCount == 3)) break;
}
if($iPos == $iStrLen) break;
}
# If there are symbols left, then the number was mallformed (following default rules (M = 1000 and i = 1000)).
return (($iPos == $iStrLen)? $iDec : 0);
}
}
define('NUMBS','3809818
1351971
2897170
1789491
3794541
1923748
2989434
985486
2656459
769272');
/**
* Result should be:
Mind the fact that i = M!
mmmdcccMxDCCCXVIII
mccclMCMLXXI
mmdcccxcvMMCLXX
mdcclxxxMxCDXCI
mmmdccxcMvDXLI
mcmxxMMMDCCXLVIII
mmcmlxxxMxCDXXXIV
cmlxxxvCDLXXXVI
mmdclvMCDLIX
dcclxMxCCLXXII
equals (script output, following wikipedian rules):
mmmdcccixDCCCXVIII
mccclMCMLXXI
mmdcccxcvMMCLXX
mdcclxxxixCDXCI
mmmdccxcivDXLI
mcmxxMMMDCCXLVIII
mmcmlxxxixCDXXXIV
cmlxxxvCDLXXXVI
mmdclvMCDLIX
dcclxixCCLXXII
even equals:
mmmdcccixDCCCXVIII
mcccliCiLXXI
mmdcccxcviiCLXX
mdcclxxxixCDXCI
mmmdccxcivDXLI
mcmxxiiiDCCXLVIII
mmcmlxxxixCDXXXIV
cmlxxxvCDLXXXVI
mmdclviCDLIX
dcclxixCCLXXII
*/
foreach (explode("\n", NUMBS) as $numb) {
echo RomanNumerals::toRoman($numb)."\n";
}
echo "\n";
define('ROMAN', 'mmmdcccixDCCCXVIII
mccclMCMLXXI
mmdcccxcvMMCLXX
mdcclxxxixCDXCI
mmmdccxcivDXLI
mcmxxMMMDCCXLVIII
mmcmlxxxixCDXXXIV
cmlxxxvCDLXXXVI
mmdclvMCDLIX
dcclxixCCLXXII');
/**
* Result should be:
3809818
1351971
2897170
1789491
3794541
1923748
2989434
985486
2656459
769272
*
*/
foreach (explode("\n", ROMAN) as $numb) {
echo RomanNumerals::toDecimal(trim($numb))."\n";
}
echo "\n";
define('ROMAN1', 'mmmdcccMxDCCCXVIII
mccclMCMLXXI
mmdcccxcvMMCLXX
mdcclxxxMxCDXCI
mmmdccxcMvDXLI
mcmxxMMMDCCXLVIII
mmcmlxxxMxCDXXXIV
cmlxxxvCDLXXXVI
mmdclvMCDLIX
dcclxMxCCLXXII');
/**
* Result should be the same as previous example...
*
*/
foreach (explode("\n", ROMAN1) as $numb) {
echo RomanNumerals::toDecimal(trim($numb))."\n";
}
/**
* The biggest and highest Roman numbers allowed.
* Result:
3888888
3999999
*
*/
echo RomanNumerals::toDecimal('mmmdccclxxxvMMMDCCCLXXXVIII')."\n";
echo RomanNumerals::toDecimal('mmmcmxcMxCMXCIX')."\n";
/**
* Some invalid roman numbers...
*
* Should all return zero or NULL.
*
*/
echo RomanNumerals::toDecimal('mmmmdccclxxxvMMMDCCCLXXXVIII')."\n"; # Too many m's.
echo RomanNumerals::toDecimal('mmmcdclxxxvMMMDCCCLXXXVIII')."\n"; # cdc forbidden... cdc should be only d.
echo RomanNumerals::toDecimal('mmmdccclxxxvMMMDXCLVIII')."\n"; # XCL = 140, should be coded as CXL
echo RomanNumerals::toDecimal('mmmdccclxxxvMMMDCLLVIII')."\n"; # LL = 100, should be coded as C.
echo RomanNumerals::toRoman(4e6);
?> |


