Gerador ideal de números romanos para mãos curtas

21

Objetivo:
Escreva uma função que aceite um número como entrada e retorne um número romano abreviado para esse número como saída.

Símbolos de números romanos:

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

Para um exemplo do que quero dizer quando digo "numerais romanos com letras curtas", vamos considerar encontrar um numeral romano para representar 1983, porque esse é o ano em que nasci. Uma opção é fazer isso da maneira normal (10 letras):

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

A outra opção é fazê-lo da maneira abreviada (6 caracteres):

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

Você sabe o que isso significa?!?!!?? Se eu fosse romano, poderia ter salvo quatro caracteres toda vez que escrevi minha data de nascimento! Woot Woot !!

No entanto, antes de me excitar, tenho uma pergunta a escrever, por isso provavelmente devo definir as regras dos números romanos abreviados para que todos fiquemos na mesma página:

Regras do numeral romano de mão curta:

  1. Sempre considere os símbolos da esquerda para a direita até que não haja mais caracteres a serem considerados.
  2. Se não houver símbolos de valor mais alto à direita do símbolo atual:
    • Adicione o valor do símbolo atual ao total atual deste numeral romano.
  3. Se houver símbolos de valor mais alto à direita do símbolo, você está considerando:
    • Localize o símbolo de maior valor à direita, à direita do símbolo atual
    • Considere todos os caracteres até esse símbolo como um numeral romano
    • Calcule o valor desse numeral romano usando estas etapas
    • Subtraia o valor desse numeral romano do total corrente desse numeral romano.
    • Vá para o próximo símbolo após o grupo que você acabou de considerar
  4. Cada numeral romano deve ter pelo menos 1 símbolo.
  5. É isso aí! Qualquer coisa que siga estas regras será aceita!

Exemplos:

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

Regras da pergunta:

  1. Faça uma função que use um número único como entrada e retorne um número romano para esse número como saída usando as regras acima. Calcule o codeGolfScore dessa função.

    example input: 2011
    example possible output: MMXI
    another possible output: MMVVIVV     //(2000 + 10 - 4 + 5) 
    
  2. Usando sua função da regra 1, gere os algarismos romanos entre -1000 (isso é certo, NEGATIVO mil) e 3000. Em seguida, some o tamanho dos caracteres desses algarismos romanos para obter seu totalCharacterCount . Aqui estão alguns pseudocódigo para esclarecer:

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

  4. Menor pontuação final ganha!

Nota: Como a contagem total de caracteres estará entre os dez mil +, o algoritmo de comprimento de caractere deve ser a principal prioridade. As pontuações de código de golfe são apenas o desempate, caso vários usuários encontrem o algoritmo ideal ou algoritmos que estão próximos um do outro.

Boa sorte e divirta-se nas suas celebrações MMXII amanhã à noite !!!

Briguy37
fonte
11
Grande tarefa! No entanto, você poderia dar um exemplo de como uma taquigrafia romana negativa se parece? Será que DDDDMrepresentam -1000?
pimvdb
@pimvdb Você conseguiu!
Briguy37
Uma pergunta sobre o caso especial zero: é ""permitido zero ou temos que usar VVXou algo equivalente?
Howard
@ Howard: Ótima pergunta, eu não tinha pensado nisso! Eu adicionei a regra 4 do numeral romano para a qual esclarece esse caso.
Briguy37
11
"Localize o símbolo de maior valor à direita, à direita do símbolo atual" - que vence, mais à direita ou com maior valor? ou seja, é IXV = -(-1 + 10) + 5 = -4(vitórias à direita) ou IXV = -1 + 10 + 5 = 14(vitórias com maior valor)?
Keith Randall

Respostas:

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

para ser usado como por exemplo

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"

Você pode avaliar a soma do comprimento com o método

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

O que leva algo na ordem de um minuto.

deixou de girar contra-relógio
fonte
5

C ++, 345 caracteres do código, 25021 algarismos romanos = 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;}}}}

um pouco ofuscado, com um 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])));
}

Vcalcula o valor numérico de uma determinada sequência numérica romana sde comprimento L. As strings são codificadas na base 7 (o primeiro dígito é s% 7, o segundo dígito é s / 7% 7, ...). Cada dígito é codificado com I = 0, V = 1, ..., M = 6. ffaz uma enumeração de força bruta de possíveis seqüências de números romanos para encontrar uma que Vavalie n.

O número total de dígitos numéricos romanos é ideal. O maior número romano necessário para [-1000,3000] é de 11 dígitos (por exemplo, -827 = CMDDMLXXIII), o que leva cerca de 5 minutos na minha máquina.

Keith Randall
fonte
Espere um momento, que não se comporta da maneira especificada. Seu programa fornece, por exemplo, LMCLXXIIIcomo resposta -777. Eu li isso como se fosse -50+1000-100+50+10+10+3 = 923 ≠ -777apenas " avaliado mais à direita " em vez de " mais alto " -777. Mas foi exatamente o que você pediu nos comentários!
deixou de vez counterclockwis
@leftaroundabout: é claro que você está certo. Vou corrigi-lo, mas não há tempo agora ...
Keith Randall
@leftaroundabout: ok, tudo corrigido.
Keith Randall
Tudo certo. É não ideal agora, embora (por exemplo, dá VVVXIpara -4quando IXVXé realmente mais curto, como eu notei) - mas isso é perfeitamente legal.
deixou de vez counterclockwis
@leftaroundabout: ok, corrigido novamente. Esperemos que seja corrigir este tempo ...
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]}

Você pode ligar rdiretamente para obter os resultados. A soma acima do intervalo especificado gera

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

que é a soma ideal, como nas outras soluções.

Howard
fonte
0

C # 23537 (639 caracteres do código + 22898 caracteres da saída)

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

Calcular:

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

mizer
fonte
E qual é a sua pontuação?
usuário desconhecido