最佳速记罗马数字生成器


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个字符!呜呜呜!

但是,在兴奋起来之前,我有一个问题要写,所以我可能应该定义速记罗马数字规则,以便我们都在同一页面上:

简写罗马数字规则:

  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. 使用上面的规则,使一个函数以单个数字作为输入,并返回该数字的罗马数字作为输出。计算此函数的codeGolfScore

    example input: 2011
    example possible output: MMXI
    another possible output: MMVVIVV     //(2000 + 10 - 4 + 5) 
    
  2. 使用规则1中的函数,生成-1000(正确的,负一千)和3000之间的罗马数字。然后将这些罗马数字的字符长度相加即可得出totalCharacterCount。这是一些伪代码来说明:

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

  4. 最低的finalScore获胜!

注意:由于totalCharacter计数将在万以上,因此字符长度算法应具有最高优先级。如果多个用户找到了彼此接近的最佳算法,则代码高尔夫球分数只是平局。

祝您好运,明天晚上在您的MMXII庆祝活动中玩得开心!!!


1
辛苦了!但是,您能否举例说明一个否定的罗马速记形式?是否DDDDM代表-1000
pimvdb 2011年

@pimvdb知道了!
Briguy37 2011年

关于特殊情况零的问题:是否""允许为零,还是必须使用VVX或等效的东西?
霍华德

@霍华德:很好的问题,我没有想到这一点!我添加了罗马数字规则4,以澄清这种情况。
Briguy37 2011年

1
“将最右边的最高值符号置于当前符号的右边” –哪个获胜,最右边还是最高值?即IXV = -(-1 + 10) + 5 = -4(最右边的胜利)或IXV = -1 + 10 + 5 = 14(最有价值的胜利)?
基思·兰德尔

Answers:


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计算给定的罗马数字字符串slength 的数值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:您当然是对的。我会修复它,但现在没有时间...
Keith Randall

@leftaroundabout:好的,已修复。
基思·兰德尔

行。这是不是最佳的现在,虽然(例如,给出VVVXI-4IXVX实际要短,因为我只注意到) -但这是完全合法的。
停止转动逆时针

@leftaroundabout:好的,再次修复。希望这次是正确的...
Keith Randall

2

Ruby,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);


你的分数是多少?
用户未知
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.