web-dev-qa-db-ja.com

整数をローマ数字に変換する-Java

これは私が問題を抱えている宿題です。

メソッドを使用してローマ数字から整数への整数を作成する必要があります。後で、プログラムを使用してローマ数字で1〜3999を書き込む必要があるため、ハードコーディングが行われます。以下の私のコードは非常に基本的なものです。これは、クラスで作成したgetIntegerFromUserのパッケージを使用中に終了する方法を備えた基本的なI/Oループです。

文字列に値を割り当て、メソッドを呼び出すときにそれらを一緒に追加する方法はありますか?

更新:私は教授から疑似コードを入手して助けてくれましたが、彼が言おうとしていることは理解していますが、ifsに問題があります。コンバーターがローマ数字のフォーマットを正しく処理するために、多くのifステートメントが必要になりますか、それともより効率的にこれを行うことができますか?プレースホルダーメソッドを反映するようにコードを更新しました。

更新(2012年10月28日):動作しました。私が最終的に使用したものは次のとおりです。

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

Java TreeMap および再帰を使用したコンパクトな実装:

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

}

テスト:

public void testRomanConversion() {

    for (int i = 1; i<= 100; i++) {
        System.out.println(i+"\t =\t "+RomanNumber.toRoman(i));
    }

}
112

これらのライブラリを使用します。

import Java.util.LinkedHashMap;
import Java.util.Map;

コード:

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

コードのテスト:

  for (int i = 1;i<256;i++) {
    System.out.println("i="+i+" -> "+RomanNumerals(i));
  }

出力:

  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
28
chepe lucho

Java Notes 6. Webサイトから:

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


      }
25
Mordechai

実際には、この問題を数字の問題としてではなく、ローマ数字の基本文字「I」から始まる単項問題として見る別の方法があります。したがって、数字をIだけで表し、ローマ字の昇順で文字を置き換えます。

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

私は、多くのifやwhileループ、またはテーブル検索を使用するよりも、この問題を解決するこの方法が特に好きです。また、問題を数の問題としてではなく考える場合、実際には直観的な解決策です。

21

私は非常に簡単なソリューションを書きました。分割し、特定の文字(または文字の組み合わせ)が何回出現するかを見つけて、StringBuilderオブジェクトsbに追加するだけです。また、残りの番号(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();
} 
11
Adilli Adil

AndréKramer Ortenの答えはとてもエレガントです。特にループを回避する方法が好きです。ループを回避する別の方法を考えました。

入力で整数除算とモジュロを使用して、ユニットタイプごとに文字列配列のハードコードされたセットから正しいインデックスを選択します。

ここでの良い点は、加算または減算の数値形式、つまりIIII対IVのどちらが必要かによって、正確な変換を指定できることです。ここでは、5x-1(4,9,14,19,40,90など)の形式のすべての数値に「減算形式」を使用します

また、これを拡張して、数千の配列を単純に追加または減算の形式、つまり「IV」、「V」または「MMMM」、「MMMMM」で拡張することで、より大きな数を可能にすることも簡単です

ボーナスポイントについては、実際に数値パラメーターが問題に対して指定された範囲内にあることを確認します。

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();
    }
}
9
Fraser

私の解決策はより簡潔なものの1つだと思います:

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

}
3
Lahiru Kavinda
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;
}
3
Mark F

これがどうなってしまうのか興味があります。 I、II、III、IV、V、VI、VII、VII、IX、Xへのマッピング1,2,3,5,6,7,8,9,10を調べ始めます... I、II、IIIは連結によって作成されます。V、X、L、C、D、およびMは5、10、50、100、500、および1000のシンボルです。例えば数字を書く代わりに、数字を書くスペース4使用IIII IV(意味:5マイナス1 ...) http://en.wikipedia.org/wiki/Roman_numerals でコードでキャプチャします。クラス "RomanNumbers"で、チートを行いたい場合は、リンクをたどることができます http://www.moxlotus.alternatifs.eu/programmation-converter.html

2
Wolfgang Fahl

私の解決策は関数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();
}
2
gizmo16

整数からローマ数字への翻訳は非常に簡単であることに気付きました。なぜなら、すべての数字に常に1、5、および10の種類があるからです(つまりIVおよびX 1-10、XLおよびC for 10 -100など)ですから、正しい文字を取得するためにローマ数字の配列を作成しました。

この例では、毎回1桁の整数を調べ、モジュロ演算子を使用して毎回最後の桁を取得します。次に、スイッチステートメント内の現在の数字からローマ数字を作成し、asRomanNumerals文字列の先頭に追加します。数字が変換された後、数字から削除され、配列内の文字を検索するために使用されるインデックスが2つ増えます(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);
}
2
Aleksi Sjöberg

これが私の宿題の結果です。入力が正しい範囲にあることを保証するものではなく、おそらくStringBuilder(調べたとき!)を使用する必要があり、1つの方法ではありません。しかし、ここまで読んでいる人がいれば、それについてのポジティブなフィードバックとネガティブなフィードバックの両方に感謝します!

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;
        }    
    }
2
Luke Matthews

ローマ数字の理論を注意深く研究すれば、数字が4,9,40などのマッピングを必要としないと思います。なぜなら、理論はローマ数字がIV = 5-1 = 4であるかどうかを教えてくれるからです。その場合の後続の番号は、実際の値を取得するために後続の番号から前の番号を引く必要があり、これが問題のコードに組み込まれたものであり、必要に応じて間違いを指摘し、私はこれに従いました私の論理を考案するための表- 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);
    }
}
2
AnkitSablok

上記のいくつかの調査を行い、回答を分析した結果、次のようになりました。

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

ユニットテストも合格しました。

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);
}
}
1
Igorssh
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);
    }

テクノロジーに遅れずについていくために、ストリームとカスタムコレクターを使用するJava 8バージョンがあり、ループやifステートメントの必要性がなくなります。

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); }
    }
}
0
Matthew Flynn

最も簡単なソリューション:

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();
    }
}
0
wild_nothing

最初に、数値を995 = 900 + 90 + 5などの小数の要因に分割し、次に各要因を再帰的に変換します

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

}

出力は次のようになります。

10進数3->ローマIII

10進数4->ローマIV

10進数8->ローマ8世

10進数58->ローマンLVIII

10進数344->ローマCCCXLIV

10進数995->ローマンCMXCV

1994年10進数->ローマンMCMXCIV

0
ShayneR

私は3年前にこれをしました、あなたに役立つかもしれません:

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));
    }
}
0
besartm

私はChain of Responsiblityパターンを自分で使うのが好きです。このシナリオでは非常に理にかなっていると思います。

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

次に、すべてのローマ数字(1/5/10/50/100/500/1000および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;
    }
}

特定の数値を変換するメソッドを公開する、コンバーターへの「インターフェイス」として機能するクラス。

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

クライアントコード(私の場合はJUnitテスト)。

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

Githubアカウント の例全体を参照してください。

0
Chris Neve

ここでいくつかの答えを見た後、私はこれを投稿しなければなりませんでした。私のアルゴリズムは最も理解しやすいものであり、パフォーマンスの低下は比較的大規模であっても重要ではないと思います。また、ここでの一部のユーザーとは対照的に、標準化されたコーディング規則に従います。

平均変換時間:0.05ms(すべての数値1-3999の変換と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.");
    }
}

最初に、ローマ数字は<1-4000の間隔にあることを考慮に入れる必要がありますが、単純なifおよびスローされた例外によって解決できます。次に、指定された整数で最大のローマ数字セットを見つけ、見つかった場合は元の数字から減算して結果に追加することができます。ゼロになるまで、新しく取得した番号で繰り返します。

0
Dropout

これは役立つかもしれません:

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())));
}
}
0
Roshan Halwai
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;
}
0
azibit

enumを利用したOP独自のソリューションに基づく代替ソリューション。さらに、パーサーとラウンドトリップテストが含まれています。

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.
            }
        }
    }
}
0
Halle Knast