3

I'm trying to write a function in Octave to convert roman numerals into decimal numbers.

What I have so far has two major problems:

1) It will only work for roman numerals up to 6 letters in length

2) The answer is only correct if each letter is smaller than the previous. ie it will not compute (IV) correctly as 4, but incorrectly as 6.

I need to somehow fix both of these problems. I have a strong suspicion that my approach to the problem is wrong and that there is an altogether more efficient way of doing this.

Anyway, if the problem interests you and/or you know of a good way to do this, any help is much appreciated.

function roman2num(s)

  decimal = [1000, 500, 100, 50, 10, 5, 1];
  roman = ["M", "D", "C", "L", "X", "V", "I"];

  num = 0;

  for i = 1:7
    if strcmp(s(1), roman(i))
      num += decimal(i);
      break 
    end
  end

      if length(s) >= 2
      for i = 1:7
        if strcmp(s(2), roman(i))
          num += decimal(i);
          break 
        end
      end
   end

    if length(s) >= 3
      for i = 1:7
        if strcmp(s(3), roman(i))
          num += decimal(i);
          break 
        end
      end
   end

    if length(s) >= 4
      for i = 1:7
        if strcmp(s(4), roman(i))
          num += decimal(i);
          break 
        end
      end
   end

    if length(s) >= 5
      for i = 1:7
        if strcmp(s(5), roman(i))
          num += decimal(i);
          break 
        end
      end
   end

    if length(s) >= 6
      for i = 1:7
        if strcmp(s(6), roman(i))
          num += decimal(i);
          break 
        end
      end
   end

  num

end

1 Answers1

5

As far as I can tell the only rule you need to worry about is determining if the letter constitutes an "add" or "subtract" operation. For some letter, we only subtract it if the first non-equal letter to the right represents a greater value.

For example in 'IIV' The first non-equal letter to the right of both I's is V so we subtract 2 and add 5 for the V since it has no letters to the right.

Implementing this rule in MATLAB is fairly straightforward.

function num = roman2num(s)
    decimal = [1000, 500, 100, 50, 10, 5, 1];
    roman = ['M', 'D', 'C', 'L', 'X', 'V', 'I'];
    tokens = arrayfun(@(x) decimal(find(roman==x,1)), char(s));
    num = tokens(end);
    for idx = 1:numel(tokens)-1
        val = tokens(idx);
        ridx = find(tokens(idx+1:end) ~= val, 1);
        if ~isempty(ridx) && tokens(ridx + idx) > val
            num = num - val;
        else
            num = num + val;
        end
    end

I tested using all the numerals between 1 and 3333.

jodag
  • 19,885
  • 5
  • 47
  • 66
  • Your solution looks pretty neat. Thanks for taking the time. I think I understand the program up until line 8 and 9 where you create and use the variable ridx. I guess I just don't understand the find function well enough. Is there any further explanation you could give me? – rustybandit May 26 '18 at 04:35
  • @rustybandit Sure thing. The first argument `tokens(idx+1:end) ~= val` is a boolean array containing `1` wherever `tokens(idx+1:end)` is *not* equal to `val` and `0` elsewhere. `find` returns the index of the first non-zero entry of this array. Since I truncated the tokens array before passing it into `find` I need to add `idx` to `ridx` to get the index relative to the beginning of tokens. This results in `ridx+idx` being the index of the first entry to the right of `idx` which is not equal to `tokens(idx)`. Or if no such entry exists then `ridx` will be empty. – jodag May 26 '18 at 04:49