Функция Octave/MATLAB для преобразования римской цифры в десятичное число

Я пытаюсь написать функцию в Octave для преобразования римских цифр в десятичные числа.

То, что у меня есть до сих пор, имеет две основные проблемы:

1) Он будет работать только для римских цифр длиной до 6 букв.

2) Ответ правильный только в том случае, если каждая буква меньше предыдущей. т.е. он не будет правильно вычислять (IV) как 4, а неправильно как 6.

Мне нужно как-то исправить обе эти проблемы. У меня есть сильное подозрение, что мой подход к проблеме неверен и что есть более эффективный способ сделать это.

В любом случае, если проблема вас интересует и/или вы знаете хороший способ сделать это, мы будем очень признательны за любую помощь.

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

person rustybandit    schedule 26.05.2018    source источник


Ответы (1)


Насколько я могу судить, единственное правило, о котором вам нужно беспокоиться, это определение того, является ли буква операцией «сложения» или «вычитания». Для некоторой буквы мы вычитаем ее только в том случае, если первая неравная буква справа представляет большее значение.

Например, в 'IIV' первой неравной буквой справа от I является V, поэтому мы вычитаем 2 и добавляем 5 для V, так как справа от нее нет букв.

Реализовать это правило в MATLAB довольно просто.

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

Я тестировал, используя все цифры от 1 до 3333.

person jodag    schedule 26.05.2018
comment
Ваше решение выглядит довольно аккуратно. Спасибо, что нашли время. Я думаю, что понимаю программу вплоть до строк 8 и 9, где вы создаете и используете переменную ridx. Наверное, я просто недостаточно хорошо понимаю функцию поиска. Есть ли какие-либо дополнительные объяснения, которые вы могли бы мне дать? - person rustybandit; 26.05.2018
comment
@rustybandit Конечно. Первый аргумент tokens(idx+1:end) ~= val — это логический массив, содержащий 1 везде, где tokens(idx+1:end) не равно val и 0 где-либо еще. find возвращает индекс первой ненулевой записи этого массива. Поскольку я усек массив токенов перед передачей его в find, мне нужно добавить idx к ridx, чтобы получить индекс относительно начала токенов. Это приводит к тому, что ridx+idx является индексом первой записи справа от idx, которая не равна tokens(idx). Или, если такой записи не существует, то ridx будет пустым. - person jodag; 26.05.2018