问题
This is a homework assignment I am having trouble with.
I need to make an integer to Roman Numeral converter using a method. Later, I must then use the program to write out 1 to 3999 in Roman numerals, so hardcoding is out. My code below is very bare-bones; it is a basic I/O loop with a way to exit while using a package for getIntegerFromUser
we made in class.
Is there a way to assign values to Strings and then add them together when I call the method?
Update: I got some pseudo code from my professor to help me, and while I understand what he is trying to say, I am having some trouble with the if
s. Will I be needing many, many if
statements so that my converter will correctly handle the Roman numeral formatting or is there a manner in which I can do this with more efficiency? I've updated my code to reflect my placeholder method.
Update (Oct 28 2012): I got it working. Here's what I ended up using:
public static String IntegerToRomanNumeral(int input) {
if (input < 1 || input > 3999)
return "Invalid Roman Number Value";
String s = "";
while (input >= 1000) {
s += "M";
input -= 1000; }
while (input >= 900) {
s += "CM";
input -= 900;
}
while (input >= 500) {
s += "D";
input -= 500;
}
while (input >= 400) {
s += "CD";
input -= 400;
}
while (input >= 100) {
s += "C";
input -= 100;
}
while (input >= 90) {
s += "XC";
input -= 90;
}
while (input >= 50) {
s += "L";
input -= 50;
}
while (input >= 40) {
s += "XL";
input -= 40;
}
while (input >= 10) {
s += "X";
input -= 10;
}
while (input >= 9) {
s += "IX";
input -= 9;
}
while (input >= 5) {
s += "V";
input -= 5;
}
while (input >= 4) {
s += "IV";
input -= 4;
}
while (input >= 1) {
s += "I";
input -= 1;
}
return s;
}
回答1:
A compact implementation using Java TreeMap and recursion:
import java.util.TreeMap;
public class RomanNumber {
private final static TreeMap<Integer, String> map = new TreeMap<Integer, String>();
static {
map.put(1000, "M");
map.put(900, "CM");
map.put(500, "D");
map.put(400, "CD");
map.put(100, "C");
map.put(90, "XC");
map.put(50, "L");
map.put(40, "XL");
map.put(10, "X");
map.put(9, "IX");
map.put(5, "V");
map.put(4, "IV");
map.put(1, "I");
}
public final static String toRoman(int number) {
int l = map.floorKey(number);
if ( number == l ) {
return map.get(number);
}
return map.get(l) + toRoman(number-l);
}
}
Testing:
public void testRomanConversion() {
for (int i = 1; i<= 100; i++) {
System.out.println(i+"\t =\t "+RomanNumber.toRoman(i));
}
}
回答2:
Use these libraries:
import java.util.LinkedHashMap;
import java.util.Map;
The code:
public static String RomanNumerals(int Int) {
LinkedHashMap<String, Integer> roman_numerals = new LinkedHashMap<String, Integer>();
roman_numerals.put("M", 1000);
roman_numerals.put("CM", 900);
roman_numerals.put("D", 500);
roman_numerals.put("CD", 400);
roman_numerals.put("C", 100);
roman_numerals.put("XC", 90);
roman_numerals.put("L", 50);
roman_numerals.put("XL", 40);
roman_numerals.put("X", 10);
roman_numerals.put("IX", 9);
roman_numerals.put("V", 5);
roman_numerals.put("IV", 4);
roman_numerals.put("I", 1);
String res = "";
for(Map.Entry<String, Integer> entry : roman_numerals.entrySet()){
int matches = Int/entry.getValue();
res += repeat(entry.getKey(), matches);
Int = Int % entry.getValue();
}
return res;
}
public static String repeat(String s, int n) {
if(s == null) {
return null;
}
final StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; i++) {
sb.append(s);
}
return sb.toString();
}
Testing the code:
for (int i = 1;i<256;i++) {
System.out.println("i="+i+" -> "+RomanNumerals(i));
}
The output:
i=1 -> I
i=2 -> II
i=3 -> III
i=4 -> IV
i=5 -> V
i=6 -> VI
i=7 -> VII
i=8 -> VIII
i=9 -> IX
i=10 -> X
i=11 -> XI
i=12 -> XII
i=13 -> XIII
i=14 -> XIV
i=15 -> XV
i=16 -> XVI
i=17 -> XVII
i=18 -> XVIII
i=19 -> XIX
i=20 -> XX
i=21 -> XXI
i=22 -> XXII
i=23 -> XXIII
i=24 -> XXIV
i=25 -> XXV
i=26 -> XXVI
i=27 -> XXVII
i=28 -> XXVIII
i=29 -> XXIX
i=30 -> XXX
i=31 -> XXXI
i=32 -> XXXII
i=33 -> XXXIII
i=34 -> XXXIV
i=35 -> XXXV
i=36 -> XXXVI
i=37 -> XXXVII
i=38 -> XXXVIII
i=39 -> XXXIX
i=40 -> XL
i=41 -> XLI
i=42 -> XLII
i=43 -> XLIII
i=44 -> XLIV
i=45 -> XLV
i=46 -> XLVI
i=47 -> XLVII
i=48 -> XLVIII
i=49 -> XLIX
i=50 -> L
i=51 -> LI
i=52 -> LII
i=53 -> LIII
i=54 -> LIV
i=55 -> LV
i=56 -> LVI
i=57 -> LVII
i=58 -> LVIII
i=59 -> LIX
i=60 -> LX
i=61 -> LXI
i=62 -> LXII
i=63 -> LXIII
i=64 -> LXIV
i=65 -> LXV
i=66 -> LXVI
i=67 -> LXVII
i=68 -> LXVIII
i=69 -> LXIX
i=70 -> LXX
i=71 -> LXXI
i=72 -> LXXII
i=73 -> LXXIII
i=74 -> LXXIV
i=75 -> LXXV
i=76 -> LXXVI
i=77 -> LXXVII
i=78 -> LXXVIII
i=79 -> LXXIX
i=80 -> LXXX
i=81 -> LXXXI
i=82 -> LXXXII
i=83 -> LXXXIII
i=84 -> LXXXIV
i=85 -> LXXXV
i=86 -> LXXXVI
i=87 -> LXXXVII
i=88 -> LXXXVIII
i=89 -> LXXXIX
i=90 -> XC
i=91 -> XCI
i=92 -> XCII
i=93 -> XCIII
i=94 -> XCIV
i=95 -> XCV
i=96 -> XCVI
i=97 -> XCVII
i=98 -> XCVIII
i=99 -> XCIX
i=100 -> C
i=101 -> CI
i=102 -> CII
i=103 -> CIII
i=104 -> CIV
i=105 -> CV
i=106 -> CVI
i=107 -> CVII
i=108 -> CVIII
i=109 -> CIX
i=110 -> CX
i=111 -> CXI
i=112 -> CXII
i=113 -> CXIII
i=114 -> CXIV
i=115 -> CXV
i=116 -> CXVI
i=117 -> CXVII
i=118 -> CXVIII
i=119 -> CXIX
i=120 -> CXX
i=121 -> CXXI
i=122 -> CXXII
i=123 -> CXXIII
i=124 -> CXXIV
i=125 -> CXXV
i=126 -> CXXVI
i=127 -> CXXVII
i=128 -> CXXVIII
i=129 -> CXXIX
i=130 -> CXXX
i=131 -> CXXXI
i=132 -> CXXXII
i=133 -> CXXXIII
i=134 -> CXXXIV
i=135 -> CXXXV
i=136 -> CXXXVI
i=137 -> CXXXVII
i=138 -> CXXXVIII
i=139 -> CXXXIX
i=140 -> CXL
i=141 -> CXLI
i=142 -> CXLII
i=143 -> CXLIII
i=144 -> CXLIV
i=145 -> CXLV
i=146 -> CXLVI
i=147 -> CXLVII
i=148 -> CXLVIII
i=149 -> CXLIX
i=150 -> CL
i=151 -> CLI
i=152 -> CLII
i=153 -> CLIII
i=154 -> CLIV
i=155 -> CLV
i=156 -> CLVI
i=157 -> CLVII
i=158 -> CLVIII
i=159 -> CLIX
i=160 -> CLX
i=161 -> CLXI
i=162 -> CLXII
i=163 -> CLXIII
i=164 -> CLXIV
i=165 -> CLXV
i=166 -> CLXVI
i=167 -> CLXVII
i=168 -> CLXVIII
i=169 -> CLXIX
i=170 -> CLXX
i=171 -> CLXXI
i=172 -> CLXXII
i=173 -> CLXXIII
i=174 -> CLXXIV
i=175 -> CLXXV
i=176 -> CLXXVI
i=177 -> CLXXVII
i=178 -> CLXXVIII
i=179 -> CLXXIX
i=180 -> CLXXX
i=181 -> CLXXXI
i=182 -> CLXXXII
i=183 -> CLXXXIII
i=184 -> CLXXXIV
i=185 -> CLXXXV
i=186 -> CLXXXVI
i=187 -> CLXXXVII
i=188 -> CLXXXVIII
i=189 -> CLXXXIX
i=190 -> CXC
i=191 -> CXCI
i=192 -> CXCII
i=193 -> CXCIII
i=194 -> CXCIV
i=195 -> CXCV
i=196 -> CXCVI
i=197 -> CXCVII
i=198 -> CXCVIII
i=199 -> CXCIX
i=200 -> CC
i=201 -> CCI
i=202 -> CCII
i=203 -> CCIII
i=204 -> CCIV
i=205 -> CCV
i=206 -> CCVI
i=207 -> CCVII
i=208 -> CCVIII
i=209 -> CCIX
i=210 -> CCX
i=211 -> CCXI
i=212 -> CCXII
i=213 -> CCXIII
i=214 -> CCXIV
i=215 -> CCXV
i=216 -> CCXVI
i=217 -> CCXVII
i=218 -> CCXVIII
i=219 -> CCXIX
i=220 -> CCXX
i=221 -> CCXXI
i=222 -> CCXXII
i=223 -> CCXXIII
i=224 -> CCXXIV
i=225 -> CCXXV
i=226 -> CCXXVI
i=227 -> CCXXVII
i=228 -> CCXXVIII
i=229 -> CCXXIX
i=230 -> CCXXX
i=231 -> CCXXXI
i=232 -> CCXXXII
i=233 -> CCXXXIII
i=234 -> CCXXXIV
i=235 -> CCXXXV
i=236 -> CCXXXVI
i=237 -> CCXXXVII
i=238 -> CCXXXVIII
i=239 -> CCXXXIX
i=240 -> CCXL
i=241 -> CCXLI
i=242 -> CCXLII
i=243 -> CCXLIII
i=244 -> CCXLIV
i=245 -> CCXLV
i=246 -> CCXLVI
i=247 -> CCXLVII
i=248 -> CCXLVIII
i=249 -> CCXLIX
i=250 -> CCL
i=251 -> CCLI
i=252 -> CCLII
i=253 -> CCLIII
i=254 -> CCLIV
i=255 -> CCLV
回答3:
From Java Notes 6.0 website:
/**
* An object of type RomanNumeral is an integer between 1 and 3999. It can
* be constructed either from an integer or from a string that represents
* a Roman numeral in this range. The function toString() will return a
* standardized Roman numeral representation of the number. The function
* toInt() will return the number as a value of type int.
*/
public class RomanNumeral {
private final int num; // The number represented by this Roman numeral.
/* The following arrays are used by the toString() function to construct
the standard Roman numeral representation of the number. For each i,
the number numbers[i] is represented by the corresponding string, letters[i].
*/
private static int[] numbers = { 1000, 900, 500, 400, 100, 90,
50, 40, 10, 9, 5, 4, 1 };
private static String[] letters = { "M", "CM", "D", "CD", "C", "XC",
"L", "XL", "X", "IX", "V", "IV", "I" };
/**
* Constructor. Creates the Roman number with the int value specified
* by the parameter. Throws a NumberFormatException if arabic is
* not in the range 1 to 3999 inclusive.
*/
public RomanNumeral(int arabic) {
if (arabic < 1)
throw new NumberFormatException("Value of RomanNumeral must be positive.");
if (arabic > 3999)
throw new NumberFormatException("Value of RomanNumeral must be 3999 or less.");
num = arabic;
}
/*
* Constructor. Creates the Roman number with the given representation.
* For example, RomanNumeral("xvii") is 17. If the parameter is not a
* legal Roman numeral, a NumberFormatException is thrown. Both upper and
* lower case letters are allowed.
*/
public RomanNumeral(String roman) {
if (roman.length() == 0)
throw new NumberFormatException("An empty string does not define a Roman numeral.");
roman = roman.toUpperCase(); // Convert to upper case letters.
int i = 0; // A position in the string, roman;
int arabic = 0; // Arabic numeral equivalent of the part of the string that has
// been converted so far.
while (i < roman.length()) {
char letter = roman.charAt(i); // Letter at current position in string.
int number = letterToNumber(letter); // Numerical equivalent of letter.
i++; // Move on to next position in the string
if (i == roman.length()) {
// There is no letter in the string following the one we have just processed.
// So just add the number corresponding to the single letter to arabic.
arabic += number;
}
else {
// Look at the next letter in the string. If it has a larger Roman numeral
// equivalent than number, then the two letters are counted together as
// a Roman numeral with value (nextNumber - number).
int nextNumber = letterToNumber(roman.charAt(i));
if (nextNumber > number) {
// Combine the two letters to get one value, and move on to next position in string.
arabic += (nextNumber - number);
i++;
}
else {
// Don't combine the letters. Just add the value of the one letter onto the number.
arabic += number;
}
}
} // end while
if (arabic > 3999)
throw new NumberFormatException("Roman numeral must have value 3999 or less.");
num = arabic;
} // end constructor
/**
* Find the integer value of letter considered as a Roman numeral. Throws
* NumberFormatException if letter is not a legal Roman numeral. The letter
* must be upper case.
*/
private int letterToNumber(char letter) {
switch (letter) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: throw new NumberFormatException(
"Illegal character \"" + letter + "\" in Roman numeral");
}
}
/**
* Return the standard representation of this Roman numeral.
*/
public String toString() {
String roman = ""; // The roman numeral.
int N = num; // N represents the part of num that still has
// to be converted to Roman numeral representation.
for (int i = 0; i < numbers.length; i++) {
while (N >= numbers[i]) {
roman += letters[i];
N -= numbers[i];
}
}
return roman;
}
/**
* Return the value of this Roman numeral as an int.
*/
public int toInt() {
return num;
}
}
回答4:
There is actually another way of looking at this problem, not as a number problem, but a Unary problem, starting with the base character of Roman numbers, "I". So we represent the number with just I, and then we replace the characters in ascending value of the roman characters.
public String getRomanNumber(int number) {
return join("", nCopies(number, "I"))
.replace("IIIII", "V")
.replace("IIII", "IV")
.replace("VV", "X")
.replace("VIV", "IX")
.replace("XXXXX", "L")
.replace("XXXX", "XL")
.replace("LL", "C")
.replace("LXL", "XC")
.replace("CCCCC", "D")
.replace("CCCC", "CD")
.replace("DD", "M")
.replace("DCD", "CM");
}
I especially like this method of solving this problem rather than using a lot of ifs and while loops, or table lookups. It is also actually a quit intuitive solution when you thinking of the problem not as a number problem.
回答5:
I have written a very simple solution. All we have to do is divide and find how many times a particular letter(or letter combination occurs) and append that to the StringBuilder object sb
. We also should keep track of the remaining number (num
).
public static String intToRoman(int num) {
StringBuilder sb = new StringBuilder();
int times = 0;
String[] romans = new String[] { "I", "IV", "V", "IX", "X", "XL", "L",
"XC", "C", "CD", "D", "CM", "M" };
int[] ints = new int[] { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500,
900, 1000 };
for (int i = ints.length - 1; i >= 0; i--) {
times = num / ints[i];
num %= ints[i];
while (times > 0) {
sb.append(romans[i]);
times--;
}
}
return sb.toString();
}
回答6:
I like André Kramer Orten's answer, very elegant, I particularly like how it avoids loops, I thought of another way to handle it also avoiding loops.
It uses integer division and modulo on the input to select the correct index from a hard-coded set of string arrays for each unit type.
The nice thing here is you can specify exact conversions depending on if you want the additive or subtractive numeral form i.e. IIII vs IV. Here I use the the "subtractive form" for all numbers in the form 5x-1 (4,9,14,19,40,90,etc)
It would also be trivial to extend this to allow for larger numbers by simply extending the thousands array with further additive or subtractive forms, i.e. "IV", "V" or "MMMM", "MMMMM"
For bonus points I actually make sure the number parameter is within the given range for the problem.
public class RomanNumeralGenerator {
static final int MIN_VALUE = 1;
static final int MAX_VALUE = 3999;
static final String[] RN_M = {"", "M", "MM", "MMM"};
static final String[] RN_C = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
static final String[] RN_X = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
static final String[] RN_I = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
public String generate(int number) {
if (number < MIN_VALUE || number > MAX_VALUE) {
throw new IllegalArgumentException(
String.format(
"The number must be in the range [%d, %d]",
MIN_VALUE,
MAX_VALUE
)
);
}
return new StringBuilder()
.append(RN_M[number / 1000])
.append(RN_C[number % 1000 / 100])
.append(RN_X[number % 100 / 10])
.append(RN_I[number % 10])
.toString();
}
}
回答7:
I think my solution is one of the more concise ones:
private static String convertToRoman(int mInt) {
String[] rnChars = { "M", "CM", "D", "C", "XC", "L", "X", "IX", "V", "I" };
int[] rnVals = { 1000, 900, 500, 100, 90, 50, 10, 9, 5, 1 };
String retVal = "";
for (int i = 0; i < rnVals.length; i++) {
int numberInPlace = mInt / rnVals[i];
if (numberInPlace == 0) continue;
retVal += numberInPlace == 4 && i > 0? rnChars[i] + rnChars[i - 1]:
new String(new char[numberInPlace]).replace("\0",rnChars[i]);
mInt = mInt % rnVals[i];
}
return retVal;
}
回答8:
String convert(int i){
String ones = "";
String tens = "";
String hundreds = "";
String thousands = "";
String result ;
boolean error = false;
Vector v = new Vector();
//assign passed integer to temporary value temp
int temp=i;
//flags an error if number is greater than 3999
if (temp >=4000) {
error = true;
}
/*loops while temp can no more be divided by 10.
Lets say i = 3254, then temp is also 3254 at line 14.
3254
3254/10 = 25 / \ 3254%10 = 4
/ \
now temp = 25 325 4 - here 4 is added to the vector v's 0th index.
/ \
now temp = 32 32 5 - here 5 is added to the vector v's 1st index.
/ \
now temp = 3 3 2 - here 2 is added to the vector v's 2nd index, and loop exits
/ \ since temp/10 = 0
0 3 - here 3 is not added to the vector v's 3rd index as loop exits when
temp/10 = 0.
*/
while (temp/10 != 0) {
if (temp / 10 != 0 && temp <4000) {
v.add(temp%10);
temp = temp / 10;
}else {
break;
}
}
//therefore you have to add temp one last time to the vector
v.add(temp);
//as in the example now you have 4,5,2,3 respectively in v's 0,1,2,3 indices.
for (int j = 0; j < v.size(); j++) {
//you see that v's 0th index has number of ones. So make them roman ones here.
if (j==0) {
switch (v.get(0).toString()){
case "0" : ones = ""; break;
case "1" : ones = "I"; break;
case "2" : ones = "II"; break;
case "3" : ones = "III"; break;
case "4" : ones = "IV"; break;
case "5" : ones = "V"; break;
case "6" : ones = "VI"; break;
case "7" : ones = "VII"; break;
case "8" : ones = "VIII"; break;
case "9" : ones = "IX"; break;
}
//in the second iteration of the loop (when j==1)
//index 1 of v is checked. Now you understand that v's 1st index
//has the tens
} else if (j == 1) {
switch (v.get(1).toString()){
case "0" : tens = ""; break;
case "1" : tens = "X"; break;
case "2" : tens = "XX"; break;
case "3" : tens = "XXX"; break;
case "4" : tens = "XL"; break;
case "5" : tens = "L"; break;
case "6" : tens = "LX"; break;
case "7" : tens = "LXX"; break;
case "8" : tens = "LXXX"; break;
case "9" : tens = "XC"; break;
}
} else if(j == 2){ //and hundreds
switch (v.get(2).toString()){
case "0" : hundreds = ""; break;
case "1" : hundreds = "C"; break;
case "2" : hundreds = "CC"; break;
case "3" : hundreds = "CCC"; break;
case "4" : hundreds = "CD"; break;
case "5" : hundreds = "D"; break;
case "6" : hundreds = "DC"; break;
case "7" : hundreds = "DCC"; break;
case "8" : hundreds = "DCCC"; break;
case "9" : hundreds = "CM"; break;
}
} else if(j == 3){ //and finally thousands.
switch (v.get(3).toString()){
case "0" : thousands = ""; break;
case "1" : thousands = "M"; break;
case "2" : thousands = "MM"; break;
case "3" : thousands = "MMM"; break;
}
}
}
if (error) {
result = "Error!";
}else{
result = thousands + hundreds + tens + ones;
}
return result;
}
回答9:
private static String toRoman(int n) {
String[] romanNumerals = { "M", "CM", "D", "CD", "C", "XC", "L", "X", "IX", "V", "I" };
int[] romanNumeralNums = { 1000, 900, 500, 400 , 100, 90, 50, 10, 9, 5, 1 };
String finalRomanNum = "";
for (int i = 0; i < romanNumeralNums.length; i ++) {
int currentNum = n /romanNumeralNums[i];
if (currentNum==0) {
continue;
}
for (int j = 0; j < currentNum; j++) {
finalRomanNum +=romanNumerals[i];
}
n = n%romanNumeralNums[i];
}
return finalRomanNum;
}
回答10:
I am curious how this is going to end up. I'd start looking into the mapping 1,2,3,5,6,7,8,9,10 to I,II,III,IV,V,VI,VII,VII,IX,X ... then you might look into the rule for roman numbers: I,II,III are created by concatentation V, X, L, C, D and M are symbols for 5, 10, 50, 100, 500 and 1000 The romans thought that they could save space in writing numbers by instead of writing e.g. IIII for 4 use IV (meaning: 5 minus 1 ...) You might want to look into those rules e.g. at http://en.wikipedia.org/wiki/Roman_numerals and capture them in code e.g. in a class "RomanNumbers" If you would like to cheat you might want to follow the link http://www.moxlotus.alternatifs.eu/programmation-converter.html
回答11:
My solution is in function getRoman:
public String getRoman(int number) {
String riman[] = {"M","XM","CM","D","XD","CD","C","XC","L","XL","X","IX","V","IV","I"};
int arab[] = {1000, 990, 900, 500, 490, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
StringBuilder result = new StringBuilder();
int i = 0;
while (number > 0 || arab.length == (i - 1)) {
while ((number - arab[i]) >= 0) {
number -= arab[i];
result.append(riman[i]);
}
i++;
}
return result.toString();
}
回答12:
I think if you study the theory of roman numerals carefully you don't require mappings for numbers 4,9,40 etc because the theory tells us if the roman numeral is IV = 5-1 = 4, hence when the prefix is smaller than the succeeding number in that case you have to subtract the former number from the succeeding number to get the actual value and this is what I have incorporated into my code for the problem, take a look and point out any mistakes if necessary, I followed this table to devise my logic - http://literacy.kent.edu/Minigrants/Cinci/romanchart.htm
import java.util.Set;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.io.FileReader;
import java.io.IOException;
import java.io.BufferedReader;
public class RomanStringToIntegerConversion {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)));
String[] romanString = br.readLine().split("");
HashMap<String, Integer> romanToIntegerMap = new HashMap<String, Integer>();
romanToIntegerMap.put("I", 1);
romanToIntegerMap.put("V", 5);
romanToIntegerMap.put("X", 10);
romanToIntegerMap.put("L", 50);
romanToIntegerMap.put("C", 100);
romanToIntegerMap.put("D", 500);
romanToIntegerMap.put("M", 1000);
int numLength = romanString.length;
Set<Integer> lessIndices = new HashSet<Integer>();
for(int i = 0; i < numLength; ++i){
if(i+1 < numLength){
if(romanToIntegerMap.get(romanString[i]) < romanToIntegerMap.get(romanString[i+1]))
lessIndices.add(i);
}
}
int num = 0;
for(int i = 0; i < numLength;){
if(!lessIndices.contains(i)){
num = num + romanToIntegerMap.get(romanString[i]);
++i;
}
else{
num = num + romanToIntegerMap.get(romanString[i+1]) - romanToIntegerMap.get(romanString[i]);
i+=2;
}
}
System.out.println("The integer representation of the roman numeral is : " + num);
}
}
回答13:
I noticed that it's quite easy to translate from integer to Roman Numeral, because there's always sort of 1, 5 and 10 for every digit (i.e. I, V and X for 1-10, X, L and C for 10-100 etc.) That's why I made an array of Roman Numerals to get the right letter from.
In my example, I go through the whole number one digit per time, using modulo operator to get the last digit each time. Then I form the Roman Numeral from current digit inside a switch-statement, adding it in the beginning of asRomanNumerals String. After the digit has been translated, it gets removed from the number and index used to look for letter in array gets increased with two (IVX -> XLC).
public static void main(String[] args) {
// number is the one to be translated into Roman Numerals
int number = 2345;
number = Math.min(3999, Math.max(1, number)); // wraps number between 1-3999
String asRomanNumerals = "";
// Array including numerals in ascending order
String[] RN = {"I", "V", "X", "L", "C", "D", "M" };
int i = 0; // Index used to keep track which digit we are translating
while (number > 0) {
switch(number % 10) {
case 1: asRomanNumerals = RN[i] + asRomanNumerals; break;
case 2: asRomanNumerals = RN[i] + RN[i] + asRomanNumerals; break;
case 3: asRomanNumerals = RN[i] + RN[i] + RN[i] + asRomanNumerals; break;
case 4: asRomanNumerals = RN[i] + RN[i + 1] + asRomanNumerals; break;
case 5: asRomanNumerals = RN[i + 1] + asRomanNumerals; break;
case 6: asRomanNumerals = RN[i + 1] + RN[i] + asRomanNumerals; break;
case 7: asRomanNumerals = RN[i + 1] + RN[i] + RN[i] + asRomanNumerals; break;
case 8: asRomanNumerals = RN[i + 1] + RN[i] + RN[i] + RN[i] +asRomanNumerals; break;
case 9: asRomanNumerals = RN[i] + RN[i + 2] + asRomanNumerals; break;
}
number = (int) number / 10;
i += 2;
}
System.out.println(asRomanNumerals);
}
回答14:
Here's the results of MY homework. It doesn't ensure the input is in the right range and I probably should be using StringBuilder
(time I looked it up!) and isn't one single method. But if anyone is reading this far I'd appreciate both positive and negative feedback on it!
import java.util.Scanner;
/**
*Main() allows user input and tests 1-3999
*toRoman() breaks the number down into digits and passes them to romanLogic()
*romanLogic() converts each digit into a the numerals that represent it.
*/
public class RomanNumerals
{
public static void main(String args[]){
Scanner in = new Scanner(System.in);
System.out.print("give us an integer < 4000: ");
System.out.println("the roman numeral version is: " + toRoman(in.nextInt()));
for (int i = 1; i<=3999; i++){
System.out.println(i +" === "+ toRoman(i));
}
}
public static String toRoman(int i){
String output = "";
int digits = i%10;
int tens = (i%100)/10;
int hundreds = (i%1000)/100;
int thousands = (i%10000)/1000;
return (romanLogic(thousands, "M","","")+
romanLogic(hundreds,"C","D","M")+
romanLogic(tens,"X","L","C")+
romanLogic(digits,"I","V","X"));
}
public static String romanLogic(int i, String ones, String fives, String tens){
String result = "";
if (i == 0){
return result;
} else {
if ((i>=4)&&(i<=8)){
result += fives;
}
if (i==9){
result += tens;
}
if(i%5 < 4){
while(i%5 > 0){
result += ones;
i--;
}
}
if(i%5 == 4){
result = ones + result;
}
}
return result;
}
}
回答15:
After doing some research and analysing answers above, I ended up with this:
package roman;
public class RomanNumbers {
public static final int[] decimal = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000};
public static final String[] letters = {"I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M"};
public static String stringToRoman(int num) {
String roman = "";
if (num < 1 || num > 3999) {
System.out.println("Invalid roman number value!");
}
while (num > 0) {
int maxFound = 0;
for (int i=0; i < decimal.length; i++) {
if (num >= decimal[i]) {
maxFound = i;
}
}
roman += letters[maxFound];
num -= decimal[maxFound];
}
return roman;
}
}
Unit tests also passed:
package roman;
import static org.junit.Assert.*;
import org.junit.Test;
public class RomanNumbersTest {
@Test
public void testReturn1() {
String actual = RomanNumbers.stringToRoman(1);
String expected = "I";
assertEquals(expected, actual);
}
@Test
public void testReturn5() {
String actual = RomanNumbers.stringToRoman(5);
String expected = "V";
assertEquals(expected, actual);
}
@Test
public void testReturn2() {
String actual = RomanNumbers.stringToRoman(2);
String expected = "II";
assertEquals(expected, actual);
}
@Test
public void testReturn4() {
String actual = RomanNumbers.stringToRoman(4);
String expected = "IV";
assertEquals(expected, actual);
}
@Test
public void testReturn399() {
String actual = RomanNumbers.stringToRoman(399);
String expected = "CCCXCIX";
assertEquals(expected, actual);
}
@Test
public void testReturn3992() {
String actual = RomanNumbers.stringToRoman(3992);
String expected = "MMMCMXCII";
assertEquals(expected, actual);
}
}
回答16:
enum Numeral {
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);
int weight;
Numeral(int weight) {
this.weight = weight;
}
};
public static String roman(long n) {
if( n <= 0) {
throw new IllegalArgumentException();
}
StringBuilder buf = new StringBuilder();
final Numeral[] values = Numeral.values();
for (int i = values.length - 1; i >= 0; i--) {
while (n >= values[i].weight) {
buf.append(values[i]);
n -= values[i].weight;
}
}
return buf.toString();
}
public static void test(long n) {
System.out.println(n + " = " + roman(n));
}
public static void main(String[] args) {
test(1999);
test(25);
test(944);
test(0);
}
回答17:
I like using a Chain of Responsiblity pattern myself. I think it makes a lot of sense for this scenario.
public abstract class NumberChainOfResponsibility {
protected NumberChainOfResponsibility next;
protected int decimalValue;
protected String romanNumeralValue;
public NumberChainOfResponsibility() {
}
public String convert(int decimal) {
int remainder = decimal;
StringBuilder numerals = new StringBuilder();
while (remainder != 0) {
if (remainder >= this.decimalValue) {
numerals.append(this.romanNumeralValue);
remainder -= this.decimalValue;
} else {
numerals.append(next.convert(remainder));
remainder = 0;
}
}
return numerals.toString();
}
}
Then I create a class extending this one for every roman numeral (1/5/10/50/100/500/1000 as well as 4/9/40/90/400/900).
1000
public class Cor1000 extends NumberChainOfResponsibility {
public Cor1000() {
super();
this.decimalValue = 1000;
this.romanNumeralValue = "M";
this.next = new Cor900();
}
}
1
public class Cor1 extends NumberChainOfResponsibility {
public Cor1() {
super();
this.decimalValue = 1;
this.romanNumeralValue = "I";
this.next = null;
}
}
A class serving as an "interface" to the converter, exposing a method to convert a specific number.
public class Converter {
private static int MAX_VALUE = 5000;
private static int MIN_VALUE = 0;
private static String ERROR_TOO_BIG = "Value is too big!";
private static String ERROR_TOO_SMALL = "Value is too small!";
public String convertThisIntToRomanNumerals(int decimal) {
Cor1000 startingCor = new Cor1000();
if (decimal >= MAX_VALUE)
return ERROR_TOO_BIG;
if (decimal <= MIN_VALUE)
return ERROR_TOO_SMALL;
String numeralsWithoutConversion = startingCor.convert(decimal);
return numeralsWithoutConversion;
}
}
And the client code (in my case a JUnit test).
@Test
public void assertConversionWorks() {
Assert.assertEquals("MMMMCMXCIX", converter.convertThisIntToRomanNumerals(4999));
Assert.assertEquals("CMXCIX", converter.convertThisIntToRomanNumerals(999));
Assert.assertEquals("CMLXXXIX", converter.convertThisIntToRomanNumerals(989));
Assert.assertEquals("DCXXVI", converter.convertThisIntToRomanNumerals(626));
Assert.assertEquals("DCXXIV", converter.convertThisIntToRomanNumerals(624));
Assert.assertEquals("CDXCVIII", converter.convertThisIntToRomanNumerals(498));
Assert.assertEquals("CXXIII", converter.convertThisIntToRomanNumerals(123));
Assert.assertEquals("XCIX", converter.convertThisIntToRomanNumerals(99));
Assert.assertEquals("LI", converter.convertThisIntToRomanNumerals(51));
Assert.assertEquals("XLIX", converter.convertThisIntToRomanNumerals(49));
}
See the whole example on my Github account.
回答18:
After seeing some of the answers here I had to post this. I think that my algorithm is far the most easiest to understand and the bit of performance lost is not important even on a relatively large scale. I'm also obeying the standardized coding conventions as opposed to some of the users here.
Average conversion time: 0.05ms (based on converting all numbers 1-3999 and dividing by 3999)
public static String getRomanNumeral(int arabicNumber) {
if (arabicNumber > 0 && arabicNumber < 4000) {
final LinkedHashMap<Integer, String> numberLimits =
new LinkedHashMap<>();
numberLimits.put(1, "I");
numberLimits.put(4, "IV");
numberLimits.put(5, "V");
numberLimits.put(9, "IX");
numberLimits.put(10, "X");
numberLimits.put(40, "XL");
numberLimits.put(50, "L");
numberLimits.put(90, "XC");
numberLimits.put(100, "C");
numberLimits.put(400, "CD");
numberLimits.put(500, "D");
numberLimits.put(900, "CM");
numberLimits.put(1000, "M");
String romanNumeral = "";
while (arabicNumber > 0) {
int highestFound = 0;
for (Map.Entry<Integer, String> current : numberLimits.entrySet()){
if (current.getKey() <= arabicNumber) {
highestFound = current.getKey();
}
}
romanNumeral += numberLimits.get(highestFound);
arabicNumber -= highestFound;
}
return romanNumeral;
} else {
throw new UnsupportedOperationException(arabicNumber
+ " is not a valid Roman numeral.");
}
}
First you have to take into account that Roman numerals are only in the interval of <1-4000), but that can be solved by a simple if and a thrown exception. Then you can try to find the largest set roman numeral in a given integer and if found subtract it from the original number and add it to the result. Repeat with the newly acquired number until you hit zero.
回答19:
Alternative solution based on the OP's own solution by utilizing an enum
. Additionally, a parser and round-trip tests are included.
public class RomanNumber {
public enum Digit {
M(1000, 3),
CM(900, 1),
D(500, 1),
CD(400, 1),
C(100, 3),
XC(90, 1),
L(50, 1),
XL(40, 1),
X(10, 3),
IX(9, 1),
V(5, 1),
IV(4, 1),
I(1, 3);
public final int value;
public final String symbol = name();
public final int maxArity;
private Digit(int value, int maxArity) {
this.value = value;
this.maxArity = maxArity;
}
}
private static final Digit[] DIGITS = Digit.values();
public static String of(int number) {
if (number < 1 || 3999 < number) {
throw new IllegalArgumentException(String.format(
"Roman numbers are only defined for numbers between 1 and 3999 (%d was given)",
number
));
}
StringBuilder sb = new StringBuilder();
for (Digit digit : DIGITS) {
int value = digit.value;
String symbol = digit.symbol;
while (number >= value) {
sb.append(symbol);
number -= value;
}
}
return sb.toString();
}
public static int parse(String roman) {
if (roman.isEmpty()) {
throw new NumberFormatException("The empty string does not comprise a valid Roman number");
}
int number = 0;
int offset = 0;
for (Digit digit : DIGITS) {
int value = digit.value;
int maxArity = digit.maxArity;
String symbol = digit.symbol;
for (int i = 0; i < maxArity && roman.startsWith(symbol, offset); i++) {
number += value;
offset += symbol.length();
}
}
if (offset != roman.length()) {
throw new NumberFormatException(String.format(
"The string '%s' does not comprise a valid Roman number",
roman
));
}
return number;
}
/** TESTS */
public static void main(String[] args) {
/* Demonstrating round-trip for all possible inputs. */
for (int number = 1; number <= 3999; number++) {
String roman = of(number);
int parsed = parse(roman);
if (parsed != number) {
System.err.format(
"ERROR: number: %d, roman: %s, parsed: %d\n",
number,
roman,
parsed
);
}
}
/* Some illegal inputs. */
int[] illegalNumbers = { -1, 0, 4000, 4001 };
for (int illegalNumber : illegalNumbers) {
try {
of(illegalNumber);
System.err.format(
"ERROR: Expected failure on number %d\n",
illegalNumber
);
} catch (IllegalArgumentException e) {
// Failed as expected.
}
}
String[] illegalRomans = { "MMMM", "CDCD", "IM", "T", "", "VV", "DM" };
for (String illegalRoman : illegalRomans) {
try {
parse(illegalRoman);
System.err.format(
"ERROR: Expected failure on roman %s\n",
illegalRoman
);
} catch (NumberFormatException e) {
// Failed as expected.
}
}
}
}
回答20:
This may help:
using System;
using System.Text;
public class Test
{
public static string ToRoman(int number)
{
StringBuilder br=new StringBuilder("");
while(number!=0)
{
if(number>=1000)
{
br.Append("M");
number-=1000;
}
if(number>=900)
{
br.Append("CM");
number-=900;
}
if(number>=500)
{
br.Append("D");
number-=500;
}
if(number>=400)
{
br.Append("CD");
number-=400;
}
if(number>=100)
{
br.Append("C");
number-=100;
}
if(number>=90)
{
br.Append("XC");
number-=90;
}
if(number>=50)
{
br.Append("L");
number-=50;
}
if(number>=40)
{
br.Append("XL");
number-=40;
}
if(number>=10)
{
br.Append("X");
number-=10;
}
if(number>=9)
{
br.Append("IX");
number-=9;
}
if(number>=5)
{
br.Append("V");
number-=5;
}
if(number>=4)
{
br.Append("IV");
number-=4;
}
if(number>=1)
{
br.Append("I");
number-=1;
}
}
return br.ToString();
}
public static void Main()
{
Console.WriteLine(ToRoman(int.Parse(Console.ReadLine())));
}
}
回答21:
Simplest solution:
public class RomanNumerals {
private static int [] arabic = {50, 40, 10, 9, 5, 4, 1};
private static String [] roman = {"L", "XL", "X", "IX", "V", "IV", "I"};
public static String convert(int arabicNumber) {
StringBuilder romanNumerals = new StringBuilder();
int remainder = arabicNumber;
for (int i=0;i<arabic.length;i++) {
while (remainder >= arabic[i]) {
romanNumerals.append(roman[i]);
remainder -= arabic[i];
}
}
return romanNumerals.toString();
}
}
回答22:
private String convertToRoman(int num) {
String result = "";
while(num > 0){
if(num >= 1000){
result += "M";
num -= 1000;
}else if(num >= 900){
result += "CM";
num -= 900;
}
else if(num >= 500){
result += "D";
num -= 500;
}else if(num >= 400){
result += "CD";
num -= 400;
}else if(num >= 100){
result += "C";
num -= 100;
}else if(num >= 90){
result += "XC";
num -= 90;
}else if(num >= 50){
result += "L";
num -= 50;
}else if(num >= 40){
result += "XL";
num -= 40;
}
else if(num >= 10){
result += "X";
num -= 10;
}else if(num >= 9){
result += "IX";
num -= 9;
}
else if(num >= 5){
result += "V";
num -= 5;
}else if(num >= 4){
result += "IV";
num -= 4;
}else if(num >= 1){
result += "I";
num -= 1;
}
else{
break;
}
}
return result;
}
回答23:
I did this before three years, may be it helps you:
public class ToRoman
{
public static String toRoman(int number)
{
StringBuilder br = new StringBuilder("");
while(number!=0)
{
while(number>=1000)
{
br.append("M");
number-=1000;
}
while(number>=900)
{
br.append("CM");
number-=900;
}
while(number>=500)
{
br.append("D");
number-=500;
}
while(number>=400)
{
br.append("CD");
number-=400;
}
while(number>=100)
{
br.append("C");
number-=100;
}
while(number>=90)
{
br.append("XC");
number-=90;
}
while(number>=50)
{
br.append("L");
number-=50;
}
while(number>=40)
{
br.append("XL");
number-=40;
}
while(number>=10)
{
br.append("X");
number-=10;
}
while(number>=9)
{
br.append("IX");
number-=9;
}
while(number>=5)
{
br.append("V");
number-=5;
}
while(number>=4)
{
br.append("IV");
number-=4;
}
while(number>=1)
{
br.append("I");
number-=1;
}
}
return br.toString();
}
public static void main(String [] args)
{
System.out.println(toRoman(2000));
}
}
回答24:
Just to keep up with the technology, here's a Java 8 version using streams and a custom Collector, eliminating the need for loops or if statements:
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.IntStream;
public class RomanNumeral {
public static void main(String arg[]) {
IntStream.range(1, 4000).forEach(value -> System.out.println( Arrays.stream(Mark.values()).collect(new MarkCollector<Mark>(value)).toString()));
}
enum Mark {
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);
private final int value;
private Mark(int value) { this.value = value; }
public int value() { return value; }
}
static class MarkCollector<T extends Mark> implements Collector<T, StringBuilder, StringBuilder> {
private final int[] valueholder = new int[1];
MarkCollector(int value) { valueholder[0] = value; }
@Override
public Supplier<StringBuilder> supplier() { return () -> StringBuilder::new; }
@Override
public BiConsumer<StringBuilder, T> accumulator() {
return (builder, mark) -> {
builder.append(String.join("", Collections.nCopies(valueholder[0] / mark.value(), mark.name())));
valueholder[0] = valueholder[0] % mark.value();
};
}
@Override
public BinaryOperator<StringBuilder> combiner() { return null; }
@Override
public Function<StringBuilder, StringBuilder> finisher() { return Function.identity(); }
@Override
public Set<Characteristics> characteristics() { return Collections.singleton(Characteristics.IDENTITY_FINISH); }
}
}
回答25:
First break the number into its decimal factors like 995 = 900 + 90 + 5 then convert each factor recursively
public class IntegerToRoman {
private Map<Integer, String> romanChars = new HashMap<>();
public IntegerToRoman() {
romanChars.put(1, "I");
romanChars.put(5, "V");
romanChars.put(10, "X");
romanChars.put(50, "L");
romanChars.put(100, "C");
romanChars.put(500, "D");
romanChars.put(1000, "M");
romanChars.put(5000, "V|");
}
public String intToRoman(int num) {
if (num == 0) {
return "";
}
int decimalFact = 0;
StringBuilder result = new StringBuilder();
for (int i = (int)Math.log10(num); i >= 0; i--) {
int divisor = (int) Math.pow(10, i);
decimalFact = num - num % divisor;
result.append(convertDecimalFact(decimalFact));
num = num % divisor;
}
return result.toString();
}
private String convertDecimalFact(int decimalFact){
if(decimalFact == 0){return "";}
int[] keyArray = romanChars.keySet().stream().mapToInt(key -> key)
.sorted().toArray();
for(int i =0 ; i+1<keyArray.length ; i++){
if( keyArray[i] <= decimalFact && decimalFact<= keyArray[i+1] ){
int bigger1stDgt = getLeftMostNum(keyArray[i+1]);
int decimalFact1stDgt = getLeftMostNum(decimalFact);
return decimalFact1stDgt >= bigger1stDgt-1 ?
intToRoman(keyArray[i+1]-decimalFact)+romanChars.get(keyArray[i+1]):
romanChars.get(keyArray[i])+intToRoman(decimalFact - keyArray[i]);
}
}
return "";
}
private int getLeftMostNum(int number) {
int oneDgt = Integer.valueOf(Integer.valueOf(number).toString()
.substring(0, 0 +1));
if(number<10){
return oneDgt;
}
int twoDgts = Integer.valueOf(Integer.valueOf(number).toString()
.substring(0, 0 +2));
return twoDgts==10 ? twoDgts : oneDgt;
}
public static void main(String[] args) {
IntegerToRoman solution = new IntegerToRoman();
System.out.format(" Decimal 3 -> Roman %s \n ", solution.intToRoman(3));
System.out.format("Decimal 4 -> Roman %s \n ", solution.intToRoman(4));
System.out.format("Decimal 8 -> Roman %s \n ", solution.intToRoman(8));
System.out.format("Decimal 58 -> Roman %s \n ", solution.intToRoman(58));
System.out.format("Decimal 344 -> Roman %s \n ", solution.intToRoman(344));
System.out.format("Decimal 995 -> Roman %s \n ", solution.intToRoman(995));
System.out.format("Decimal 1994 -> Roman %s \n ", solution.intToRoman(1994));
}
}
Output is like:
Decimal 3 -> Roman III
Decimal 4 -> Roman IV
Decimal 8 -> Roman VIII
Decimal 58 -> Roman LVIII
Decimal 344 -> Roman CCCXLIV
Decimal 995 -> Roman CMXCV
Decimal 1994 -> Roman MCMXCIV
回答26:
import java.util.Scanner;
import java.io.*;
public class LetteredNumerationSystem {
public static void main(String args[]) {
File file = new File("lettered.in");
try {
Scanner input = new Scanner(file);
int dataCollect = input.nextInt();
int sum = 0;
String lineInput = "";
for (int i = 0; i <= dataCollect; i++) {
while (input.hasNext()) {
lineInput = input.next();
char lineArray[] = lineInput.toCharArray();
for (int j = 0; j < lineArray.length; j++) {
if (lineArray[j] == 'A') {
sum += 1;
} else if (lineArray[j] == 'B') {
sum += 10;
} else if (lineArray[j] == 'C') {
sum += 100;
} else if (lineArray[j] == 'D') {
sum += 1000;
} else if (lineArray[j] == 'E') {
sum += 10000;
} else if (lineArray[j] == 'F') {
sum += 100000;
} else if (lineArray[j] == 'G') {
sum += 1000000;
} else if (lineArray[j] == 'X') {
System.out.println(sum);
sum = 0;
}
}
}
}
} catch (FileNotFoundException e) {
System.out.println("ERROR");
}
}
}
来源:https://stackoverflow.com/questions/12967896/converting-integers-to-roman-numerals-java