Optimaler Kurzzeigergenerator für römische Zahlen


21

Ziel:
Schreiben Sie eine Funktion, die eine Zahl als Eingabe annimmt und eine kurze römische Zahl für diese Zahl als Ausgabe zurückgibt.

Römische Zahlensymbole:

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

Als Beispiel für das, was ich meine, wenn ich "römische Kurzzahlen" sage, wollen wir in Betracht ziehen, eine römische Zahl zu finden, die 1983 repräsentiert, weil dies das Jahr ist, in dem ich geboren wurde. Eine Möglichkeit ist, dies wie gewohnt zu tun (10 Buchstaben):

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

Die andere Möglichkeit besteht darin, es auf kurze Weise zu tun (6 Zeichen):

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

Weißt du was das heißt?!?!!?? Wenn ich Römer wäre, hätte ich jedes Mal, wenn ich mein Geburtsdatum schrieb, 4 Zeichen speichern können! Woot Woot !!

Bevor ich mich jedoch in Aufregung zurückmische, muss ich eine Frage schreiben, daher sollte ich wahrscheinlich die Regeln für römische Kurzzahlen definieren, damit wir uns alle auf einer Seite befinden:

Kurzschrift-Regeln für römische Ziffern:

  1. Berücksichtigen Sie Symbole immer von links nach rechts, bis keine Zeichen mehr zu berücksichtigen sind.
  2. Wenn sich rechts neben dem aktuellen Symbol keine höherwertigen Symbole befinden:
    • Addieren Sie den Wert des aktuellen Symbols zur laufenden Summe dieser römischen Ziffer.
  3. Wenn sich rechts neben dem Symbol höherwertige Symbole befinden, überlegen Sie:
    • Suchen Sie das Symbol mit dem höchsten Wert ganz rechts neben dem aktuellen Symbol
    • Betrachten Sie alle Zeichen bis zu diesem Symbol als eine römische Ziffer
    • Berechnen Sie den Wert dieser römischen Ziffer mit diesen Schritten
    • Subtrahieren Sie den Wert dieser römischen Ziffer von der laufenden Summe dieser römischen Ziffer.
    • Gehen Sie zum nächsten Symbol nach der gerade betrachteten Gruppe
  4. Jede römische Ziffer muss mindestens 1 Symbol enthalten.
  5. Das ist es! Alle folgenden Regeln werden akzeptiert!

Beispiele:

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

Frageregeln:

  1. Erstellen Sie eine Funktion, die eine einzelne Zahl als Eingabe annimmt und eine römische Zahl für diese Zahl als Ausgabe zurückgibt, indem Sie die obigen Regeln anwenden. Berechnen Sie den CodeGolfScore dieser Funktion.

    example input: 2011
    example possible output: MMXI
    another possible output: MMVVIVV     //(2000 + 10 - 4 + 5) 
    
  2. Generieren Sie mit Ihrer Funktion aus Regel 1 die römischen Ziffern zwischen -1000 (das ist richtig, NEGATIV eintausend) und 3000. Addieren Sie dann die Zeichenlänge dieser römischen Ziffern, um Ihren totalCharacterCount zu erhalten . Hier ist ein Pseudocode zur Verdeutlichung:

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

  4. Der niedrigste FinalScore gewinnt!

Hinweis: Da die Anzahl der totalCharacter-Zeichen im Bereich von Zehntausenden + liegt, sollte der Algorithmus für die Zeichenlänge oberste Priorität haben. Code-Golf-Scores sind nur der Auslöser für den Fall, dass mehrere Benutzer den optimalen Algorithmus oder die optimalen Algorithmen finden, die nahe beieinander liegen.

Viel Glück und viel Spaß bei Ihren MMXII-Feierlichkeiten morgen Abend !!!


1
Tolle Aufgabe! Können Sie jedoch ein Beispiel geben, wie eine negative römische Kurzschrift aussieht? Steht DDDDMfür -1000?
pimvdb

@pimvdb Du hast es verstanden!
Briguy37

Eine Frage zum Sonderfall Null: Ist ""für Null erlaubt oder müssen wir VVXetwas Äquivalentes verwenden?
Howard

@Howard: Tolle Frage, daran hatte ich nicht gedacht! Ich habe die römische Ziffer Regel 4 hinzugefügt, um diesen Fall zu klären.
Briguy37

1
"Finde das am weitesten rechts stehende Symbol mit dem höchsten Wert rechts neben dem aktuellen Symbol" - welches gewinnt, am weitesten rechts oder am höchsten? dh, ist IXV = -(-1 + 10) + 5 = -4(ganz rechts gewinnt) oder IXV = -1 + 10 + 5 = 14(am höchsten bewertete Gewinne)?
Keith Randall

Antworten:


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

verwendet werden als zb

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"

Mit dem können Sie die Längensumme direkt auswerten

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

Das dauert in der Größenordnung von einer Minute.


5

C ++, 345 Codezeichen, 25021 römische Ziffern = 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;}}}}

ein bisschen deobfuscated, mit einem Fahrer:

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

Vberechnet den numerischen Wert eines gegebenen römische Ziffer Zeichenkette sder Länge L. Zeichenfolgen sind mit der Basis 7 codiert (erste Ziffer ist s% 7, zweite Ziffer ist s / 7% 7, ...). Jede Ziffer ist mit I = 0, V = 1, ..., M = 6 codiert. fFührt eine Brute-Force-Aufzählung möglicher römischer Ziffernfolgen durch, um eine zu finden, die Vausgewertet wird n.

Die Gesamtzahl der römischen Ziffern ist optimal. Die längste römische Ziffer, die für [-1000,3000] benötigt wird, ist 11 Ziffern (z. B. -827 = CMDDMLXXIII), was auf meinem Computer ungefähr 5 Minuten dauert.


Warte einen Moment, das verhält sich nicht so wie angegeben. Dein Programm gibt zB LMCLXXIIIals Antwort auf -777. Ich würde das so lesen, dass es -50+1000-100+50+10+10+3 = 923 ≠ -777nur mit "ganz rechts höher bewertet" statt "am höchsten " gibt -777. Aber genau darum haben Sie in den Kommentaren gebeten!
hörte am

@leftaroundabout: natürlich hast du recht. Ich werde es reparieren, aber im Moment ist keine Zeit ...
Keith Randall

@leftaroundabout: ok, alles behoben.
Keith Randall

Gut. Es ist jetzt allerdings nicht optimal (zB gibt VVVXIan, -4wann IXVXes tatsächlich kürzer ist, wie ich gerade bemerkt habe) - aber das ist vollkommen legal.
hörte am

@leftaroundabout: ok, wieder behoben. Hoffentlich ist es diesmal richtig ...
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]}

Sie können rdirekt anrufen , um die Ergebnisse zu erhalten. Die Summe über dem angegebenen Bereich ergibt

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

das ist die optimale Summe wie bei den anderen Lösungen.


0

C # 23537 (639 Zeichen Code + 22898 Zeichen Ausgabe)

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

Berechnen:

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


Und was ist deine Punktzahl?
Benutzer unbekannt
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.