Оптимальний генератор римських цифр з короткою рукою


21

Мета:
Напишіть функцію, яка приймає число як вхідне, і повертає коротке римське число для цього числа як вихід.

Римські числові символи:

Symbol  Value
I       1
V       5
X       10
L       50
C       100
D       500
M       1,000

На прикладі того, що я маю на увазі, коли я кажу «короткі римські цифри», розглянемо, як знайти римську цифру для позначення 1983 року, адже це рік, коли я народився. Один із варіантів - це зробити звичайним способом (10 букв):

1983 = MCMLXXXIII = (1000 - 100 + 1000 + 50 + 30 + 3)

Інший варіант - зробити це короткочасним способом (6 символів):

1983 = MXVIIM = (1000 - (10 + 10) + 1000 + 3)

Ви знаєте, що це означає?!? !! ?? Якби я був роман, я міг би врятувати 4 персонажі кожного разу, коли я писав свою дату народження! Woot Woot !!

Однак перед тим, як я випереджаю себе в хвилюванні, у мене є питання, щоб написати, тому я, мабуть, повинен визначити короткі римські правила цифр, щоб ми всі були на одній сторінці:

Короткі римські правила числення:

  1. Завжди розглядайте символи зліва направо, поки немає більше символів, які слід розглядати.
  2. Якщо праворуч від поточного символу немає символів більш високого значення:
    • Додайте значення поточного символу до загальної кількості цієї римської цифри.
  3. Якщо праворуч від символу, який ви розглядаєте, є символи більш високого значення:
    • Знайдіть крайній правий символ, що найвищий, праворуч від поточного символу
    • Розгляньте всі символи до цього символу як одну римську цифру
    • Обчисліть значення цієї римської цифри за допомогою цих кроків
    • Відніміть значення цієї римської цифри від загальної кількості цієї римської цифри.
    • Перейдіть до наступного символу після групи, яку ви тільки що розглянули
  4. Кожна римська цифра повинна містити принаймні 1 символ.
  5. Це воно! Все, що дотримується цих правил, буде прийнято!

Приклади:

IIIIV = (-(1+1+1+1)+5) = 1  //Don't ask me why you'd want to do this!  

VVX = (-(5+5) + 10) = 0  //Who said you couldn't represent 0 with roman numerals?!!?

VVXM = (-(-(5+5) + 10) + 1000) = 1000  //Again...don't ask me why you'd want to do this!

MXIIXMI = (1000-(10-(1+1)+10)+1000+1) = 1983  //Ahhh...such a great year :)

Правила запитання:

  1. Створіть функцію, яка приймає одне число як вхідне і повертає римську цифру для цього числа як вихідну, використовуючи наведені вище правила. Обчисліть кодGolfScore цієї функції.

    example input: 2011
    example possible output: MMXI
    another possible output: MMVVIVV     //(2000 + 10 - 4 + 5) 
    
  2. Використовуючи свою функцію з правила 1, згенеруйте римські цифри від -1000 (це правильно, НЕГАТИВНА тисяча) та 3000. Потім підсумуйте довжину символів цих римських цифр, щоб отримати загальнийCharacterCount . Ось псевдокод для уточнення:

    totalCharacterCount = 0;
    for(currentNumber = -1000; currentNumber <= 3000; currentNumber++){
        totalCharacterCount += getRomanNumeral(currentNumber).length;
    }
    return totalCharacterCount;
    
  3. finalScore = codeGolfScore + totalCharacterCount

  4. Найнижчі виграші у FinalScore !

Примітка. Оскільки загальний підрахунок символів буде складати десять тисяч +, алгоритм довжини символів повинен бути основним пріоритетом. Оцінки гольф-коду - це лише перемикач, якщо кілька користувачів знаходять оптимальний алгоритм або алгоритми, близькі один до одного.

Удачі та весело провести час на своїх святкуваннях MMXII завтра ввечері !!!


1
Чудове завдання! Однак ви могли б навести приклад того, як виглядає негативна римська стенограма? Чи DDDDMстоїть -1000?
pimvdb

@pimvdb Ви отримали це!
Briguy37

Питання, що стосується спеціального випадку нуля: чи ""дозволено нуль чи ми повинні використовувати VVXчи щось еквівалентне?
Говард

@Howard: Чудове запитання, я про це не думав! Я додав правило римської цифри 4, яке пояснює цей випадок.
Briguy37

1
"Знайдіть крайній правий символ найвищого значення праворуч від поточного символу" - який виграє, найправіший чи найзначніший? тобто, це IXV = -(-1 + 10) + 5 = -4(кращий виграш), або IXV = -1 + 10 + 5 = 14(виграш з найвищими цінами)?
Кіт Рендалл

Відповіді:


5

Haskell, 25637 (= 268 + 25369) 26045 (= 222 + 25823)

r 0="VVX"
r n=s(zip[1000,500,100,50,10,5]"MDCLXV")n ξ
ξ='ξ'
s[]q f
 |q<0=s[](5-q)f++"V"
 |q<1=""
 |r<-q-1='I':s[]r f
s ω@((v,a):l)q f
 |q>=v,f/=a=a:s ω(q-v)ξ
 |f==a,γ<-'I':a:s l(q-v+1)ξ,η γ<η(s l q ξ)=γ
 |f==ξ,γ<-s ω(v-q)a++[a],η γ<η(s l q ξ)=γ
 |True=s l q ξ
η=length

використовуватися як напр

GHCi> r 7
"VII"
GHCi> r 39
"XIL"
GHCi> r (-39)
"ICXLC"        --  "LLXILC" in my original version
GHCi> r 1983
"MXVIIM"
GHCi> r 259876
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMCXXIVM"

Суму довжини можна оцінити прямолінійно

GHCi> sum . map(length.r) $ [-1000..3000]
25369

Що займає щось в порядку хвилини.


5

C ++, 345 символів коду, 25021 римська цифрова цифра = 25366

int N[]={1,5,10,50,100,500,1000};int V(int s,int L){if(!L)return 0;int K=0,B,m=s%7+1;for(int k=1,b=7;k<L;k++,b*=7){if(s/b%7>=m){K=k;B=b;m=s/b%7;}}return K?V(s/B,L-K)-V(s%B,K):N[s%7]+V(s/7,L-1);}char r[99];char*f(int n){for(int L=1,B=7,i,j;1;L++,B*=7){for(i=0;i<B;i++){if(V(i,L)==n){for(j=0;j<L;j++){r[j]="IVXLCDM"[i%7];i/=7;}r[L]=0;return r;}}}}

трохи знеструмлений, з драйвером:

int N[]={1,5,10,50,100,500,1000};
int V(int s,int L){
  if(!L)return 0;
  int K=0,B,m=s%7+1;
  for (int k=1,b=7;k<L;k++,b*=7) {
    if(s/b%7>=m){K=k;B=b;m=s/b%7;}
  }
  return K ? V(s/B,L-K)-V(s%B,K) : N[s%7]+V(s/7,L-1);
}
char r[99];
char *f(int n){
  for(int L=1,B=7;1;L++,B*=7) {
    for(int i=0;i<B;i++) {
      if(V(i,L)==n){
        for(int j=0;j<L;j++) {
          r[j]="IVXLCDM"[i%7];i/=7;
        }
        r[L]=0;
        return r;
      }
    }
  }
}
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
  printf("%s\n", f(atoi(argv[1])));
}

Vобчислює числове значення заданого римського числового рядка sдовжини L. Рядки кодуються базою 7 (перша цифра - s% 7, друга цифра - s / 7% 7, ...). Кожна цифра кодується I = 0, V = 1, ..., M = 6. fробить перерахування грубої сили можливих римських числових рядків, щоб знайти той, який Vоцінює n.

Загальна кількість римських цифрових цифр є оптимальною. Найдовша римська цифра, необхідна для [-1000,3000], - це 11 цифр (наприклад, -827 = CMDDMLXXIII), що займає на моїй машині близько 5 хвилин.


Зачекайте момент, який не веде себе так, як зазначено. Ваша програма, наприклад, дає LMCLXXIIIвідповідь-777 . Я читав би це, як це дає -50+1000-100+50+10+10+3 = 923 ≠ -777лише лише "найправіше вище значення " замість " найвище " -777. Але це було саме те, що ви просили в коментарях!
перестала повертати проти годинника,

@leftaroundabout: звичайно, ти маєш рацію. Я це виправлю, але зараз немає часу ...
Кіт Рендалл

@leftaroundabout: добре, все виправлено.
Кіт Рендалл

Гаразд. Це не так оптимальна зараз, хоча (наприклад , дає VVVXIдля -4коли IXVXнасправді коротше, так як я тільки що помітив) - але це зовсім законно.
перестала повертати проти годинника,

@leftaroundabout: добре, виправлено ще раз. Сподіваємось, це правда цього разу ...
Кіт Рендалл

2

Рубі, 25987 (= 164 + 25823)

h=->i,d,v,k{i==0?'':i<v ?(a=h[v-i,x=d[1..-1],v/k,k^7]+d[0];i<0?a:(b=h[i,x,v/k,k^7];a.size<b.size ? a :b)):d[0]+h[i-v,d,v,k]}
r=->i{i==0?'DDM':h[i,'MDCLXVI',1000,2]}

Ви можете зателефонувати rбезпосередньо, щоб отримати результати. Сума понад вказаний діапазон дає

> (-1000..3000).map{|i|r[i].size}.reduce &:+
25823

що є оптимальною сумою, як і для інших рішень.


0

C # 23537 (639 символів коду + 22898 символів виводу)

class M
{
    public static string R(int n, int? s = new int?())
    {
        var r = "";
        var D = new Dictionary<int, string> {{ 1000, "M"}, { 900, "CM"},{ 800, "CCM"},{ 500, "D"}, { 400, "CD"},{ 300, "CCD"},{100, "C"}, {90, "XC"},{80, "XXC"},{50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {8, "IIX"}, {5, "V"}, {4, "IV"},{1, "I"}};
        if (n == 0) return "VVX";
        if (n == -1) return "IIIIIIV";
        if (n < 0) return N(n * -1);

        foreach(int k in D.Keys)
        {
            if (s.HasValue && k > s) continue;

            while(k <= n)
            {
                n -= k; 
                r += D[k];
            }
        }

        return r;
    }

    public static string N(int n)
    {
        var D = new Dictionary<int, string> {{1, "I"}, {5, "V"}, {10, "X"}, {50, "L"}, {100, "C"}, { 500, "D"}, {1000, "M"}};

        int i = D.Keys.First(v => v >= n), m = D.Keys.Where(v => v < i).Max();

        return R(n + i, m) + D[i];
    }
}

Для обчислення:

Enumerable.Range(-1000, 3000).Sum(i => M.R(i).Length);


А яка ваша оцінка?
користувач невідомий
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.