Generatore numerico romano a lancetta ottimale


21

Obiettivo:
scrivere una funzione che accetta un numero come input e restituisce un numero romano abbreviato per quel numero come output.

Simboli numerici romani:

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

Per un esempio di cosa intendo quando dico "numeri romani a mano corta", consideriamo di trovare un numero romano che rappresenti il ​​1983, perché quello è l'anno in cui sono nato. Un'opzione è di farlo normalmente (10 lettere):

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

L'altra opzione è di farlo nel modo breve (6 caratteri):

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

Sai cosa significa questo?!?!!?? Se fossi stato romano avrei potuto salvare 4 caratteri ogni volta che ho scritto la mia data di nascita! Woot Woot !!

Tuttavia, prima di superare l'eccitazione, ho una domanda da scrivere, quindi dovrei probabilmente definire le regole numeriche romane a breve termine in modo da essere tutti sulla stessa pagina:

Regole numeriche romane di breve durata:

  1. Considera sempre i simboli da sinistra a destra fino a quando non ci sono più caratteri da considerare.
  2. Se non ci sono simboli di valore superiore a destra del simbolo corrente:
    • Aggiungi il valore del simbolo corrente al totale parziale di questo numero romano.
  3. Se ci sono simboli di valore più alto a destra del simbolo che stai considerando:
    • Individua il simbolo più alto più a destra alla destra del simbolo corrente
    • Considera tutti i caratteri fino a quel simbolo come un numero romano
    • Calcola il valore di quel numero romano usando questi passaggi
    • Sottrai il valore di quel numero romano dal totale parziale di questo numero romano.
    • Passa al simbolo successivo dopo il gruppo che hai appena considerato
  4. Ogni numero romano deve contenere almeno 1 simbolo.
  5. Questo è tutto! Tutto ciò che segue queste regole sarà accettato!

Esempi:

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

Regole delle domande:

  1. Crea una funzione che accetta un singolo numero come input e restituisce un numero romano per quel numero come output utilizzando le regole precedenti. Calcola il codeGolfScore di questa funzione.

    example input: 2011
    example possible output: MMXI
    another possible output: MMVVIVV     //(2000 + 10 - 4 + 5) 
    
  2. Usando la tua funzione dalla regola 1, genera i numeri romani tra -1000 (esatto, NEGATIVO mille) e 3000. Quindi riassumi la lunghezza dei caratteri di questi numeri romani per ottenere totalCharacterCount . Ecco alcuni pseudocodici per chiarire:

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

  4. Vince il FinalScore più basso !

Nota: poiché il conteggio di totalCharacter sarà compreso tra le diecimila +, l'algoritmo di lunghezza dei caratteri dovrebbe avere la massima priorità. I punteggi di code-golf sono solo il pareggio nel caso in cui più utenti trovino l'algoritmo o gli algoritmi ottimali vicini l'uno all'altro.

Buona fortuna e divertiti alle tue celebrazioni MMXII domani sera !!!


1
Ottimo compito! Tuttavia, potresti dare un esempio di come appare una stenografia romana negativa? Sta DDDDMper -1000?
pimvdb,

@pimvdb Hai capito!
Briguy37,

Una domanda relativa al caso speciale zero: è ""consentito per zero o dobbiamo usare VVXo qualcosa di equivalente?
Howard,

@Howard: Ottima domanda, non ci avevo pensato! Ho aggiunto la regola numerica romana 4 per la quale chiarisce quel caso.
Briguy37,

1
"Individua il simbolo più alto più a destra alla destra del simbolo attuale" - che vince, più a destra o più valutato? vale a dire, IXV = -(-1 + 10) + 5 = -4(vittorie più a destra) o IXV = -1 + 10 + 5 = 14(vittorie con il punteggio più alto)?
Keith Randall,

Risposte:


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

da utilizzare come ad es

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"

È possibile valutare la somma della lunghezza con il semplice

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

Il che richiede qualcosa nell'ordine di un minuto.


5

C ++, 345 caratteri di codice, 25021 cifre in numeri romani = 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;}}}}

deo-offuscato un po ', con un driver:

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

Vcalcola il valore numerico di una determinata stringa numerica romana sdi lunghezza L. Le stringhe sono codificate in base 7 (la prima cifra è s% 7, la seconda cifra è s / 7% 7, ...). Ogni cifra è codificata con I = 0, V = 1, ..., M = 6. ffa una numerazione a forza bruta di possibili stringhe numerali romane per trovarne unaV valuti n.

Il numero totale di cifre numerali romane è ottimale. Il numero romano più lungo necessario per [-1000,3000] è di 11 cifre (es. -827 = CMDDMLXXIII), che impiega circa 5 minuti sulla mia macchina.


Aspetta un momento, che non si comporti nel modo specificato. Il tuo programma fornisce ad esempio LMCLXXIIIcome risposta a -777. L'avrei letto come -50+1000-100+50+10+10+3 = 923 ≠ -777, solo con "più alto più alto- valutato" invece di " più alto " dà -777. Ma era proprio quello che hai chiesto nei commenti!
cessò di girare in senso antiorario il

@leftaroundabout: ovviamente hai ragione. Lo aggiusterò, ma non c'è tempo in questo momento ...
Keith Randall

@leftaroundabout: ok, tutto risolto.
Keith Randall,

Tutto ok. E ' non è ottimale adesso, anche se (per esempio, dà VVVXIper -4quando IXVXè in realtà più breve, come ho appena notato) - ma questo è perfettamente legale.
cessò di girare in senso antiorario il

@leftaroundabout: ok, riparato di nuovo. Spero sia corretto questa volta ...
Keith Randall il

2

Rubino, 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]}

Puoi chiamare rdirettamente per ottenere i risultati. La somma supera l'intervallo specificato

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

quale è la somma ottimale come con le altre soluzioni.


0

C # 23537 (639 caratteri di codice + 22898 caratteri di output)

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

Calcolare:

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


E qual è il tuo punteggio?
utente sconosciuto
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.