최적의 짧은 로마 숫자 생성기


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. 각 로마 숫자에는 적어도 하나의 기호가 있어야합니다.
  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 (즉, NEGATIVE 1000)과 3000 사이의 로마 숫자를 생성합니다. 그런 다음 로마 숫자의 문자 길이를 합하여 totalCharacterCount 를 얻 습니다 . 명확히하는 의사 코드는 다음과 같습니다.

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

  4. 가장 낮은 finalScore가 승리합니다!

참고 : totalCharacter 수는 10 만 + 이상이므로 문자 길이 알고리즘이 최우선 순위 여야합니다. 코드 골프 점수는 여러 사용자가 서로 가까이있는 최적의 알고리즘을 찾은 경우를 대비 한 평가 기준입니다.

행운을 빌어 내일 밤 MMXII 축제에서 즐거운 시간을 보내십시오!


1
좋은 일! 그러나 부정적인 로마 약식이 어떻게 보이는지 예를 들어 줄 수 있습니까? 하는가 DDDDM에 대한 서 -1000?
pimvdb

@pimvdb 당신은 그것을 얻었다!
Briguy37

특별한 경우 0에 관한 질문 : 0이 ""허용 VVX됩니까, 아니면 사용해야 합니까?
Howard

@Howard : 좋은 질문, 나는 그것을 생각하지 않았다! 나는 그 경우를 명확히하기 위해 로마 숫자 규칙 4를 추가했습니다.
Briguy37

1
"가장 오른쪽에있는 가장 높은 값을 가진 기호를 현재 기호의 오른쪽에 위치"-가장 오른쪽에있는 값 또는 가장 높은 값에 해당하는 것은 무엇입니까? 즉, IXV = -(-1 + 10) + 5 = -4(가장 오른쪽 승리) 또는 IXV = -1 + 10 + 5 = 14(가치가 높은 승리)입니까?
Keith Randall

답변:


5

하스켈, 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

1 분 정도 걸립니다.


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])));
}

Vslength 의 주어진 로마 숫자 문자열 의 숫자 값을 계산합니다 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전용 "오른쪽으로, 더 높은 "-valued "대신 최고 "가 제공 않습니다 -777. 그러나 그것은 당신이 의견에서 요구 한 것입니다!
counterclockwis 설정을 중단

@leftaroundabout : 물론 그렇습니다. 내가 고칠 것이지만 지금은 시간이 없다 ...
Keith Randall

@ leftaroundabout : 좋아, 모두 고정되었습니다.
Keith Randall

괜찮아. 그것은의 하지 (예 : 제공하지만, 지금은 최적의 VVVXI를 위해 -4IXVX난 그냥 눈치, 실제로 짧은) -하지만 완벽하게 합법적이다.
반 시계 회전을 중지

@ leftaroundabout : 좋아, 다시 고쳐. 잘만되면 이번에
Keith Randall

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


그리고 당신의 점수는 무엇입니까?
사용자가 알 수 없음
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.