Trình tạo số La Mã tay ngắn tối ưu


21

Mục tiêu:
Viết hàm lấy một số làm đầu vào và trả về một số La Mã tay ngắn cho số đó làm đầu ra.

Biểu tượng số La Mã:

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

Để biết ví dụ về ý tôi muốn nói khi tôi nói "chữ số La Mã tay ngắn", hãy xem xét việc tìm một chữ số La Mã để đại diện cho năm 1983, vì đó là năm tôi được sinh ra. Một lựa chọn là làm điều này theo cách thông thường (10 chữ cái):

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

Tùy chọn khác là thực hiện theo cách ngắn gọn (6 ký tự):

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

Bạn có biết nó có nghĩa là gì không?!?!!?? Nếu tôi là người La Mã, tôi có thể đã lưu 4 ký tự mỗi lần tôi viết ngày sinh của mình! Woot Woot !!

Tuy nhiên, trước khi tôi vượt lên chính mình trong sự phấn khích, tôi có một câu hỏi để viết, vì vậy tôi có lẽ nên xác định các quy tắc số La Mã tay ngắn để tất cả chúng ta đều trên cùng một trang:

Quy tắc số La Mã tay ngắn:

  1. Luôn xem xét các biểu tượng từ trái sang phải cho đến khi không còn ký tự nào nữa.
  2. Nếu không có biểu tượng có giá trị cao hơn ở bên phải biểu tượng hiện tại:
    • Thêm giá trị của biểu tượng hiện tại vào tổng số đang chạy của chữ số La Mã này.
  3. Nếu có các biểu tượng có giá trị cao hơn ở bên phải của biểu tượng bạn đang xem xét:
    • Xác định vị trí biểu tượng có giá trị cao nhất bên phải ở bên phải biểu tượng hiện tại
    • Hãy xem tất cả các ký tự cho đến ký hiệu đó là một chữ số La Mã
    • Tính giá trị của chữ số La Mã đó bằng các bước sau
    • Trừ giá trị của chữ số La Mã đó khỏi tổng số của chữ số La Mã này.
    • Di chuyển đến biểu tượng tiếp theo sau nhóm bạn vừa xem xét
  4. Mỗi chữ số La Mã phải có ít nhất 1 ký hiệu trong đó.
  5. Đó là nó! Bất cứ điều gì theo các quy tắc này sẽ được chấp nhận!

Ví dụ:

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

Quy tắc câu hỏi:

  1. Tạo một hàm lấy một số duy nhất làm đầu vào và trả về một số La Mã cho số đó làm đầu ra bằng các quy tắc trên. Tính toán mãGolfScore của hàm này.

    example input: 2011
    example possible output: MMXI
    another possible output: MMVVIVV     //(2000 + 10 - 4 + 5) 
    
  2. Sử dụng chức năng của bạn từ quy tắc 1, tạo các chữ số La Mã trong khoảng -1000 (đúng, NEGECT một nghìn) và 3000. Sau đó, tổng hợp độ dài ký tự của các chữ số La Mã này để lấy TotalCharacterCount của bạn . Đây là một số mã giả để làm rõ:

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

  4. Chiến thắng chung cuộc thấp nhất !

Lưu ý: Vì tổng số lượng vi khuẩn sẽ ở mức mười nghìn +, thuật toán độ dài ký tự phải là ưu tiên hàng đầu. Điểm số golf-golf chỉ là điểm phá vỡ trong trường hợp nhiều người dùng tìm thấy thuật toán tối ưu hoặc thuật toán gần nhau.

Chúc may mắn và vui chơi tại lễ kỷ niệm MMXII của bạn vào tối mai !!!


1
Nhiệm vụ tuyệt vời! Tuy nhiên, bạn có thể đưa ra một ví dụ về cách một tốc ký La Mã tiêu cực trông như thế nào không? Có DDDDMđứng cho -1000?
pimvdb

@pimvdb Bạn hiểu rồi!
Briguy37

Một câu hỏi liên quan đến trường hợp đặc biệt bằng 0: Được ""phép bằng 0 hoặc chúng ta phải sử dụng VVXhoặc một cái gì đó tương đương?
Howard

@Howard: Câu hỏi tuyệt vời, tôi đã không nghĩ về điều đó! Tôi đã thêm quy tắc số La Mã 4 để làm rõ trường hợp đó.
Briguy37

1
"Xác định vị trí biểu tượng có giá trị cao nhất bên phải ở bên phải biểu tượng hiện tại" - chiến thắng nào, có giá trị cao nhất hay bên phải nhất? tức là IXV = -(-1 + 10) + 5 = -4(chiến thắng ngoài cùng bên phải), hay IXV = -1 + 10 + 5 = 14(chiến thắng có giá trị cao nhất)?
Keith Randall

Câu trả lời:


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

được sử dụng như vd

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"

Bạn có thể đánh giá tổng chiều dài với đơn giản

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

Mà mất một cái gì đó theo thứ tự của một phút.


5

C ++, 345 ký tự mã, 25021 chữ số La Mã = 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;}}}}

deobfuscated một chút, với một trình điều khiển:

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

Vtính giá trị số của một chuỗi chữ số la mã cho schiều dài L. Chuỗi được mã hóa cơ sở 7 (chữ số đầu tiên là s% 7, chữ số thứ hai là s / 7% 7, ...). Mỗi chữ số được mã hóa với I = 0, V = 1, ..., M = 6. fthực hiện một phép liệt kê các chuỗi số La Mã có thể để tìm một chuỗi Vđánh giá n.

Tổng số chữ số La Mã là tối ưu. Chữ số La Mã dài nhất cần thiết cho [-1000,3000] là 11 chữ số (ví dụ: -827 = CMDDMLXXIII), mất khoảng 5 phút trên máy của tôi.


Đợi một chút, điều đó không hành xử theo cách được chỉ định. Chương trình của bạn đưa ra ví dụ LMCLXXIIInhư câu trả lời -777. Tôi đã đọc rằng -50+1000-100+50+10+10+3 = 923 ≠ -777, chỉ với " giá trị cao nhất bên phải " thay vì " cao nhất " thì nó mới đưa ra -777. Nhưng đó chỉ là những gì bạn yêu cầu trong các ý kiến!
đã ngừng quay ngược chiều

@leftaroundabout: tất nhiên bạn đúng. Tôi sẽ sửa nó, nhưng không có thời gian ngay bây giờ ...
Keith Randall

@leftaroundabout: ok, tất cả đều cố định.
Keith Randall

Được rồi Đó là không tối ưu bây giờ, mặc dù (ví dụ như cung cấp cho VVVXIcho -4khi IXVXthực sự là ngắn hơn, như tôi chỉ nhận thấy) - nhưng đó là hoàn toàn hợp pháp.
đã ngừng quay ngược chiều

@leftaroundabout: ok, sửa lại. Hy vọng lần này là chính xác ...
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]}

Bạn có thể gọi rtrực tiếp để có kết quả. Tổng trên năng suất phạm vi được chỉ định

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

đó là tổng tối ưu như với các giải pháp khác.


0

C # 23537 (639 ký tự mã + 22898 ký tự đầu ra)

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

Tính toán:

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


Và điểm số của bạn là gì?
người dùng không xác định
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.