Util Roman numerals functions

Discussion in 'Resources' started by PhantomUnicorns, Aug 19, 2016.

Thread Status:
Not open for further replies.
  1. Offline

    PhantomUnicorns

    These two functions are the most compact way I have ever found or created for Roman numerals to decimal and decimal to Roman numerals. One is not my creation and I can't find the original person who made it. The second one is based off of that and is just as compact.
    Code:
        public static int unnumeral(String number) {
            if (number.startsWith("M")) return 1000 + unnumeral(number.replaceFirst("M", ""));
            if (number.startsWith("CM")) return 900 + unnumeral(number.replaceFirst("CM", ""));
            if (number.startsWith("D")) return 500 + unnumeral(number.replaceFirst("D", ""));
            if (number.startsWith("CD")) return 400 + unnumeral(number.replaceFirst("CD", ""));
            if (number.startsWith("C")) return 100 + unnumeral(number.replaceFirst("C", ""));
            if (number.startsWith("XC")) return 90 + unnumeral(number.replaceFirst("XC", ""));
            if (number.startsWith("L")) return 50 + unnumeral(number.replaceFirst("L", ""));
            if (number.startsWith("XL")) return 40 + unnumeral(number.replaceFirst("XL", ""));
            if (number.startsWith("X")) return 10 + unnumeral(number.replaceFirst("X", ""));
            if (number.startsWith("IX")) return 9 + unnumeral(number.replaceFirst("IX", ""));
            if (number.startsWith("V")) return 5 + unnumeral(number.replaceFirst("V", ""));
            if (number.startsWith("IV")) return 4 + unnumeral(number.replaceFirst("IV", ""));
            if (number.startsWith("I")) return 1 + unnumeral(number.replaceFirst("I", ""));
            return 0;
        }
    
        public static String numeral(int number) {
            if (number<=0) return "";
            if (number-1000>=0) return "M" + numeral(number-1000);
            if (number-900>=0) return "CM" + numeral(number-900);
            if (number-500>=0) return "D" + numeral(number-500);
            if (number-400>=0) return "CD" + numeral(number-400);
            if (number-100>=0) return "C" + numeral(number-100);
            if (number-90>=0) return "XC" + numeral(number-90);
            if (number-50>=0) return "L" + numeral(number-50);
            if (number-40>=0) return "XL" + numeral(number-40);
            if (number-10>=0) return "X" + numeral(number-10);
            if (number-9>=0) return "IX" + numeral(number-9);
            if (number-5>=0) return "V" + numeral(number-5);
            if (number-4>=0) return "IV" + numeral(number-4);
            if (number-1>=0) return "I" + numeral(number-1);
            return null;
        }
        // Or
    
        public String numural(int number) {
            String[] symbols = new String[] { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
            int[] numbers = new int[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
            for (int i = 0; i < numbers.length; i++) {
                if (number >= numbers[i]) {
                    return symbols[i] + numural(number - numbers[i]);
                }
            }
            return "";
        }
     
        public int unnumural(String number) {
            String[] symbols = new String[] { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
            int[] numbers = new int[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
            for (int i = 0; i < symbols.length; i++) {
                if (number.startsWith(symbols[i])) {
                    return numbers[i] + unnumural(number.replaceFirst(symbols[i], ""));
                }
            }
            return 0;
        }
    
    Really you could/should initialize numbers and symbols outside the method (private, static, and final)
     
    Last edited: Jun 28, 2017
  2. Offline

    MarinD99

    Wish I had this while writing my Latin exam last year.
     
  3. Offline

    PhantomUnicorns

    I actually have it by memory because of how much code I've done to reduce it XD. Sadly there is no character that java will allow me to put in for 5000 10000 and so on.
     
  4. Offline

    ChipDev

    Cool! Are there any more efficient ways to do this? Just because the code hits me with my OCD >_>
    (As in maybe 1-2 if statements, some Array's, etc)
    P.S You have been really active on this account, 14 posts, 4 likes since 3-4 days? Either you have used Bukkit / spigot for a long time, have an alt, or just are really good xD
    EDIT: Can we have a little explanation on this? It seems that both of the methods rely on each other. Can we have an output example?
     
  5. Offline

    I Al Istannen

    @ChipDev
    They don't rely on each other, but use recursion.

    I find the recursive example he used quite clever and (easy) to read and understand, what exactly do you have troubles with?

    You can certainly convert this to an imperative example (certain means to 100%, you can do that in every case), but it won't look as neat and requires more space.

    I did a similar thing some time ago (quite a bit, don't jugde the code xD)
    It is imperative and has no reflection. Now, what looks nicer and easier to understand? (Though this could be cleaned up by a lot probably)
    Code:java
    1.  
    2. /**
    3.   * @param number The number to convert
    4.   * @return The Converted String
    5.   */
    6. public static String convertToRoman(int number) {
    7. StringBuilder builder = new StringBuilder();
    8.  
    9. if(number < 3999) {
    10.  
    11. while(number >= 1) {
    12. int firstNumber = number / Integer.valueOf("1" + getPaddedString("0", ("" + number).length() - 1));
    13. if(firstNumber == 4) {
    14. builder.append(getCharForDecimalPlace(("" + number).length(), false) + "" + getCharForDecimalPlace(("" + number).length(), true));
    15.  
    16. number -= Integer.valueOf("" + firstNumber + getPaddedString("0", ("" + number).length() - 1));
    17. }
    18. else if(firstNumber == 9) {
    19. builder.append(getCharForDecimalPlace(("" + number).length(), false) + "" + getCharForDecimalPlace(("" + number).length() + 1, false));
    20.  
    21. number -= Integer.valueOf("" + firstNumber + getPaddedString("0", ("" + number).length() - 1));
    22. }
    23. else {
    24. if(firstNumber >= 5) {
    25. builder.append(getCharForDecimalPlace(("" + number).length(), true));
    26. }
    27. else {
    28. builder.append(getCharForDecimalPlace(("" + number).length(), false));
    29. }
    30.  
    31. number -= getAsNormalFigure(builder.charAt(builder.length() - 1));
    32. }
    33.  
    34. }
    35. }
    36. else {
    37. return "" + number;
    38. }
    39.  
    40. return builder.toString();
    41. }
    42.  
    43. private static char getCharForDecimalPlace(int place, boolean biggest) {
    44. switch(place) {
    45. case 1: {
    46. return biggest ? 'V' : 'I';
    47. }
    48. case 2: {
    49. return biggest ? 'L' : 'X';
    50. }
    51. case 3: {
    52. return biggest ? 'D' : 'C';
    53. }
    54. default: {
    55. return 'M';
    56. }
    57. }
    58. }
    59.  
    60. private static String getPaddedString(String base, int amount) {
    61. StringBuilder builder = new StringBuilder();
    62.  
    63. for(int i = 0; i < amount; i++) {
    64. builder.append(base);
    65. }
    66.  
    67. return builder.toString();
    68. }
    69.  
    70. /**
    71.   * @param roman The Roman String to convert
    72.   * @return The number it represents
    73.   */
    74. public static int convertToNormal(String roman) {
    75. int sum = 0;
    76.  
    77. int previous = 0;
    78.  
    79. for (int i = 0; i < roman.toCharArray().length; i++) {
    80. int current = getAsNormalFigure(roman.charAt(i));
    81.  
    82. // just add it, it is smaller or the same as the one before. Examples:
    83. // LX X is smaller, just add it.
    84. // XX X is equal, just add it
    85. if(current <= previous) {
    86. sum += current;
    87. }
    88. else if(current > previous) {
    89. sum -= previous;
    90. sum += current - previous;
    91. }
    92.  
    93. previous = current;
    94. }
    95.  
    96. return sum;
    97. }
    98.  
    99. private static int getAsNormalFigure(char c) {
    100. switch (Character.toUpperCase(c)) {
    101. case 'I': {
    102. return 1;
    103. }
    104. case 'V': {
    105. return 5;
    106. }
    107. case 'X': {
    108. return 10;
    109. }
    110. case 'L': {
    111. return 50;
    112. }
    113. case 'C': {
    114. return 100;
    115. }
    116. case 'D': {
    117. return 500;
    118. }
    119. case 'M': {
    120. return 1000;
    121. }
    122. }
    123. return 0;
    124. }
    125.  
    126. /**
    127.   * @param s The String to check
    128.   * @return True if it is a roman number. Case Sensitive
    129.   */
    130. public static boolean isRomanNumber(String s) {
    131. for (char c : s.trim().toCharArray()) {
    132. if(getAsNormalFigure(c) == 0) {
    133. return false;
    134. }
    135. }
    136.  
    137. return convertToNormal(s) == -1 ? false : true;
    138. }
    139.  
    140.  
     
  6. Offline

    ChipDev

    I actually think, well.. I haven't ever used Roman Numerals so I am kind of stuck at that part. Most likely not a code issue!
     
  7. Offline

    I Al Istannen

    @ChipDev
    I totally understand that :p
    Does this help? It is quited boilded down. You only need the first few points (1-3). Was a random google search though :p
     
  8. Offline

    PhantomUnicorns

    My methods don't require on each other, instead it calls it self instead of using a while block and a initialized variable. This is THE shortest way to do roman numerals that I have ever seen or thought of, the only way to beat it would be near impossible and most likely a complete fluke. I have it so it does the highest value first and it calls it self again (after minusing the value to the given int) and checks for the second highest third and so fourth. Because roman numerals has a couple of special rules, this covers both of them (which is kind of a fluke actually). Hope that helps :3
     
  9. Offline

    InstanceofDeath

    Wow! Thats so genius to work with a recursive method, that class itself .. I am rlly proud of you. Thought the only use
    for recursive methods are fractions ... ;)
     
  10. Offline

    I Al Istannen

    @InstanceofDeath
    Oh, there are SO many uses for recursive programming. In general, everything you can do with "normal" (iterative it is called) programming can be solved recursive. Recursive is often less efficient than iterative though.

    What is really great about it is it's simplicity. In this case, you can directly relate to what is happening and why it is. An iterative solution would probably be more complex.

    It is also useful for tree structures or alike, as you can simply delegate calls to the children.
    Or take a "toString()" function. It calls the toString() for all necessary fields. You can't really do that iterative in the same simplicity.

    A quite heavy downside is that recursive programming on a more advanced scale gets quite hard to think of and understand, as you must keep track of the relationships and levels you are currently in. Imagine a recusive function with multiple parameters and a few hundred levels. It can be quite hard sometimes to get that right.

    And you must specify an "anchor", at which the recrusion stops. It is quite easy to miss, and in this case it will cause an infinite loop which will be ungracefully ended by a StackOverFlowException when it gets too deep.


    Do you know the fibonacci sequence? There is a really simple solution to get the nth fibonacci number. Only downside is that quite small numbers will already crash the program, due to the many, many, many levels of recursion, most unneeded.
    You can see a simple example of the recursive fibonacci here, and here why it is "bad".

    Pick the best tool for the task, and I think @PhantomUnicorns definitely made a good choice there :)
    I will totally steal this if I ever need it :p
     
    InstanceofDeath and AlvinB like this.
  11. Offline

    PhantomUnicorns

    @l_Al_Istannen finally someone who uses StringBuilder XD
     
Thread Status:
Not open for further replies.

Share This Page