Adicionando, à moda antiga


8

Resumo
Os romanos antigos criaram um sistema numérico usando letras latinas, o que lhes serviu bem e ainda é usado pela civilização moderna, embora em um grau muito menor. Na época de seu uso, os romanos precisavam aprender a usar e manipular esses números para serem de grande utilidade para muitas aplicações. Por exemplo, se um homem possuísse 35 bois e ele adquirisse mais 27, como ele saberia o novo total a não ser contar todos? ( Ok, isso e usando um ábaco ... ) Se os romanos pudessem fazê-lo, certamente podemos descobrir também.

Objetivo
Escreva o menor algoritmo / função / programa que adicionará dois algarismos romanos e produza o resultado sem converter a representação em cadeia de uma das entradas em um número.

Regras / restrições
Devido às inconsistências históricas / pré-medievais na formatação, vou descrever algumas regras não-padrão (por uso moderno) para ortografia. Veja o guia de valores abaixo como exemplo.

  • As letras I, X, C e M podem ser repetidas até quatro vezes seguidas, mas não mais. D, L e V nunca podem ser repetidos.
  • A letra imediatamente à direita de outra letra na representação romana terá o mesmo ou menor valor que a esquerda.
    • Em outras palavras, VIIII == 9mas IX != 9e é inválido / não permitido.
  • Todos os valores de entrada serão 2.000 (MM) ou menos; nenhuma representação para números maiores que M é necessária.
  • Todos os valores de entrada serão um número romano válido, de acordo com as regras acima.
  • Você não pode converter nenhum número em decimal, binário ou qualquer outro sistema de números como parte de sua solução (você pode usar esse método para VERIFICAR seus resultados).
  • Isso é código de golfe, então o código mais curto vence.

Guia de Valor

Symbol        Value
I             1
II            2
III           3
IIII          4
V             5
VIIII         9
X             10
XIIII         14
XXXXIIII      44
L             50
LXXXXVIIII    99
C             100
D             500
M             1,000

Exemplos

XII + VIII = XX (12 + 8 = 20)
MCCXXII + MCCXXII = MMCCCCXXXXIIII (1,222 + 1,222 = 2,444)
XXIIII + XXXXII = LXVI (24 + 42 = 66)

Se precisar de mais esclarecimentos, pergunte.


2
XXXXIIII -> 44 ou XIIII -> 14?
Paul Richter

1
Eu acho que ele está se referindo a um erro no guia de valores.
grc 28/05

@grc Oh. Parvo eu ... Consertado.
Gaffi 28/05

1
Por que alguém iria querer converter para uma base diferente? Você realmente quis proibir a análise da string em um número?
31512 Peter Peter Taylor

@ PeterTaylor Sim, está correto.
Gaffi 28/05

Respostas:


5

APL ( 59 56)

,/N⍴⍨¨{∨/K←⍵≥D←7⍴5 2:∇⍵+(1⌽K)-K×D⋄⍵}⊃+/(N←'MDCLXVI')∘=¨⍞

Entrada em uma linha (ou seja XII + XII, embora +não seja necessário).

Edit: shift alterado para girar para salvar três caracteres - só importa quando a resposta for ≥ 5000, o que nunca deve acontecer, pois a pergunta diz que os valores de entrada sempre serão ≤ 2000. O único efeito é que agora "transborda" em 5000, dando 5000 = 1, 5001 = 2, etc.

(Eu realmente não acho que os romanos fizeram dessa maneira ... APL é mais uma coisa para os egípcios antigos, eu acho :))

Explicação:

  • : obter entrada do usuário
  • (N←'MDCLXVI')∘=¨: armazene 'MDCLXVI' em N. Retorne, para cada caractere da sequência de entrada, um vetor com 1 no local em que o caractere corresponde a um dos 'MDCLXVI' e 0 caso contrário.
  • ⊃+/: Soma os vetores e desencapsule. Agora temos um vetor com informações sobre quantos números romanos temos. Ou seja, se a entrada foi XXII XIIII, agora temos:
     MDCLXVI
     0 0 0 0 3 0 6
  • Observe que isso não está convertendo os valores.
  • {... :... ... }é uma função com uma construção if-else.
  • D←7⍴5 2: Dé o vetor 5 2 5 2 5 2 5. É assim que um número romano não é permitido. Ou seja, se você tem 5 Is, isso é demais, e se você tem 2 Vs, também é demais. Esse vetor também é o fator de multiplicação para cada numeral romano, ou seja, a Vvale 5 Is e X2 vale 2 Vs.

  • ∨/K←⍵≥D: Ké o vetor em que existe um 1 se tivermos muitos algarismos romanos de um determinado tipo. ∨/ORs esse vetor juntos.

  • Se esse vetor não tiver todos os zeros:
  • K×D: Multiplique K por D. Esse vetor tem zeros onde não temos muitos algarismos romanos e a quantidade de algarismos romanos onde temos.
  • ⍵+(1⌽K): Gire K para a esquerda por 1 e adicione-o à entrada. Para cada número romano que temos em excesso, isso adicionará um do próximo número mais alto.
  • ⍵+(1⌽K)-K×D: Subtraia isso do outro vetor. O efeito é que, por exemplo, se você tiver 6 Is, ele adicionará um Ve removerá 4 Is.
  • : Recurso.
  • ⋄⍵: Mas se Kforam todos os zeros, ⍵ representa um numeral romano válido, então retorne ⍵.
  • N⍴⍨¨: Para cada elemento do vetor resultante, crie muitos dos números romanos correspondentes.
  • ,/: Junte esses vetores para se livrar dos espaços feios na saída.

5

Python, 100

s="IVXLCDM"
r=raw_input()
a=""
i=2
u=0
for c in s:r+=u/i*c;i=7-i;u=r.count(c);a+=u%i*c
print a[::-1]

Toma uma string da entrada (por exemplo, VIII + XIIou VIII + XII =).


3

Perl, 132 caracteres

sub s{@a=VXLCDM=~/./g;@z=(IIIII,VV,XXXXX,LL,CCCCC,DD);$r=join"",@_;
$r=~s/$a[-$_]/$z[-$_]/gfor-5..0;$r=~s/$z[$_]/$a[$_]/gfor 0..5;$r}

Declara a função sque pega qualquer número de argumentos e os soma. Bem direto: acrescenta a entrada, reduz tudo a Ise repõe imediatamente os algarismos romanos. (Espero que isso não conte como usar o sistema de número unário!)


3

Ruby, 85 82 caracteres

gets;r=0;v=5;puts"IVXLCDM".gsub(/./){|g|r+=$_.count g;t=r%v;r/=v;v^=7;g*t}.reverse

Esta versão recebe a entrada STDIN como uma única sequência (por exemplo XXIIII + XXXXII) e imprime a saída em STDOUT.

f=->*a{r=0;v=5;"IVXLCDM".gsub(/./){|g|r+=(a*'').count g;t=r%v;r/=v;v^=7;g*t}.reverse}

O segundo é uma implementação como uma função. Pega duas (ou mais) seqüências e retorna os valores somados. Uso:

puts f["XXIIII", "XXXXII"]     # -> LXVI

3

GNU Sed, 131 caracteres

:;s/M/DD/;s/D/CCCCC/;s/C/LL/;s/L/XXXXX/;s/X/VV/;s/V/IIIII/;t;s/\W//g;:b;s/IIIII/V/;s/VV/X/;s/XXXXX/L/;s/LL/C/;s/CCCCC/D/;s/DD/M/;tb

1

Python, 174 caracteres

Um algoritmo muito simples - conte cada loop de dígito para manipular o estouro para o próximo, imprima.
Lê da entrada padrão. Algo como XVI + CXXfuncionaria (ignora qualquer coisa, exceto números, portanto +não é realmente necessário).

x="IVXLCDM"
i=raw_input()
d=dict((c,i.count(c))for c in x)
for c in x:
    n=2+3*(c in x[::2])
    if d[c]>=n:d[c]-=n;d[x[x.find(c)+1]]+=1
print"".join(c*d[c]for c in reversed(x))

1

Scala 150

val c="IVXLCDM"
def a(t:String,u:String)=((t+u).sortBy(7-c.indexOf(_))/:c.init)((s,z)=>{val i=c.indexOf(z)+1
s.replaceAll((""+z)*(2+i%2*3),""+c(i))})

invocação:

a("MDCCCCLXXXXVIIII", "MDCCCCLXXXXVIIII")

Versão não destruída:

val c = "IVXLCDM"
def add (t:String, u:String) = (
  (t+u).  // "MDCCCCLXXXXVIIIIMDCCCCLXXXXVIIII"
  sortBy(7-c.indexOf(_)) // MMDDCCCCCCCCLLXXXXXXXXVVIIIIIIII
  /: // left-fold operator to be used: (start /: rest) ((a,b)=> f (a,b)) 
  c.init) /* init is everything except the rest, so c.init = "IVXLCD"
    (because M has no follower to be replaced with */
  ((s, z) => { /* the left fold produces 2 elements in each step, 
    and the result is repeatedly s, on initialisation 
    MMDDCCCCCCCCLLXXXXXXXXVVIIIIIIII 
    and z is the iterated element from c.init, namely I, V, X, L, C, D
    in sequence */
    val i = c.indexOf (z) + 1 // 1, 2, ..., 7
    /* i % 2 produces 1 0 1 0 1 0
       *3 => 3 0 3 0 
       +2 => 5 2 5 2 
       (""+ 'I') * 5 is "IIIII", ("" + 'V') * 2 is "VV", ...
       ""+c(i) is "V", "X", ...
    */ 
    s.replaceAll (("" + z) * (2+i%2*3), "" + c (i))
    }
  )

1

JavaScript 195 179

Meu sistema é bastante rudimentar, reduza todos os números romanos a uma série de Iambos os números, junte-os e inverta o processo transformando certos blocos Iem suas respectivas versões maiores ...

Iteração 1 a="IIIII0VV0XXXXX0LL0CCCCC0DD0M".split(0);d=b=>x.replace(g=RegExp((c=z)>b?a[c][0]:a[c],"g"),c>b?a[b]:a[b][0]);x=prompt().replace("+","");for(z=6;0<z;z--)x=d(z-1);for(z=0;6>z;z++)x=d(z+1);alert(x)

Iteração 2 a="IIIII0VV0XXXXX0LL0CCCCC0DD0M".split(0);x=prompt().replace("+","");for(z=-6;6>z;z++)b=0>z?-z:z,c=0>z?~z:z+1,x=x.replace(g=RegExp(b>c?a[b][0]:a[b],"g"),b>c?a[c]:a[c][0]);alert(x)

Recursos:

  • Cadeia de caracteres de matriz delimitada zero, mais rápido do que configurar uma matriz de cadeia de caracteres.
  • Reduziu as duas recursões para aquela e recalculou os parâmetros para a regex específica.
  • Mais operadores ternários do que você pode mexer!

A entrada é inserida via prompt na forma de <first roman number>+<second roman number>(sem espaços) e a saída na forma de um alerta.

por exemplo

XVI+VII // alert shows XXIII, correct!
MCCXXXIIII+DCCLXVI // alert shows MM, also correct!

1

VBA, 187 caracteres

Function c(f,s)
a=Split("I,V,X,L,C,D,M",",")
z=f & s
For i=0 To 6
x=Replace(z,a(i),"")
n=Len(z)-Len(x)+m
r=IIf(i Mod 2,2,5)
o=n Mod r
m=Int(n/r)
c=String(o,a(i)) & c
z=x
Next
End Function

Como osó é usado uma vez, você latas salvar 3 bytes, removendo a atribuição e avaliação do mesmo e ligar n Mod rdiretamente para a String(chamada de função
Taylor Scott

1

JavaScript, 190

x=prompt(r=[]);p=RegExp;s="MDCLXVI";for(i=-1;++i<7;){u=x.match(new p(s[i],'g'));if(u)r=r.concat(u)};for(r=r.join("");--i>0;){r=r.replace(new p(s[i]+'{'+(i%2==0?5:2)+'}','g'),s[i-1])}alert(r)

Colocar algumas instruções dentro do terceiro slot do for operador, deixe-me economizar alguns pontos e vírgulas!

Quando a entrada solicita, você insere os dois números (os +espaços e não são necessários, mas se você os colocar, não receberá um erro). Em seguida, o alerta mostra a soma.


0

C ++, 319 caracteres

#define A for(j=0;j<
#define B .length();j++){ 
#define C [j]==a[i]?1:0);}
#include <iostream>
int main(){std::string x,y,s,a="IVXLCDM";int i,j,k,l,m=0,n;std::cin>>x>>y;for(i=0;i<7;i++){k=0;l=0;A x B k+=(x C A y B l+=(y C n=k+l+m;m=(n)%(i%2?2:5);for(j=0;j<m;j++){s=a[i]+s;}m=(n)/(i%2?2:5);}std::cout<<s<<"\n";return 0;}

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.