Conte de 1 a 100 ... em algarismos romanos

29

Escreva um programa que conte de 1 a 100 em algarismos romanos e imprima esses números pela saída padrão. Cada um dos números deve ser separado por espaços.

Você não pode usar nenhuma função interna para transformar em algarismos romanos, nem aplicativo ou biblioteca externa para fazer isso.

O resultado desejado é

I II III IV V VI VII VIII IX X XI XII XIII XIV XV XVI XVII XVIII XIX XX XXI XXII XXIII XXIV XXV XXVI XXVII XXVIII XXIX XXX XXXI XXXII XXXIII XXXIV XXXV XXXVI XXXVII XXXVIII XXXIX XL XLI XLII XLIII XLIV XLV XLVI XLVII XLVIII XLIX L LI LII LIII LIV LV LVI LVII LVIII LIX LX LXI LXII LXIII LXIV LXV LXVI LXVII LXVIII LXIX LXX LXXI LXXII LXXIII LXXIV LXXV LXXVI LXXVII LXXVIII LXXIX LXXX LXXXI LXXXII LXXXIII LXXXIV LXXXV LXXXVI LXXXVII LXXXVIII LXXXIX XC XCI XCII XCIII XCIV XCV XCVI XCVII XCVIII XCIX C

Por se tratar de um desafio de código de golfe, o código mais curto vence .

Averroes
fonte
4
39 está faltando um X.
Thor
@Thor Fixed, thanks;)
Averroes
11
Eu realmente quero usar INTERCAL para este.
Weijun Zhou
pode ser separado por novas linhas? E quanto a espaços à direita / à esquerda / novas linhas?
FantaC 19/0318

Respostas:

68

Perl 69 bytes

s;.;y/XVI60-9/CLXVIX/dfor$a[$_].="32e$&"%72726;gefor 1..100;print"@a"

Funciona por meio de fórmula mágica. A expressão "32e$&"%72726transforma cada dígito da seguinte maneira:
0⇒32, 1⇒320, 2⇒3200, 3⇒32000, 4⇒29096, 5⇒56, 6⇒560, 7⇒5600, 8⇒56000, 9⇒50918

Depois de aplicar a tradução y/016/IXV/, temos o seguinte:
0⇒32, 1⇒32 I , 2⇒32 II , 3⇒32 III , 4⇒29 I 9 V , 5⇒5 V , 6⇒5 VI , 7⇒5 VII , 8⇒5 VIII , 9⇒5 I 9 X 8

O restante dos dígitos ( 2-57-9) é removido. Observe que isso pode ser melhorado em um byte usando uma fórmula que traduz em 012vez de 016, simplificando /XVI60-9/para /XVI0-9/. Não consegui encontrar um, mas talvez você tenha mais sorte.

Depois que um dígito é transformado dessa maneira, o processo se repete para o próximo dígito, acrescentando o resultado e convertendo os XVIs anteriores para CLXao mesmo tempo em que ocorre a tradução para o novo dígito.

Atualizar
Pesquisa exaustiva não revelou nada mais curto. No entanto, encontrei uma solução alternativa de 69 bytes:

s;.;y/XVI0-9/CLXIXV/dfor$a[$_].="57e$&"%474976;gefor 1..100;print"@a"

Este usa uma 0-2substituição para IXV, mas possui um módulo com um dígito a mais.


Atualização: 66 65 bytes

Esta versão é notavelmente diferente, então eu provavelmente deveria dizer algumas palavras sobre ela. A fórmula usada é na verdade um byte a mais!

Incapaz de encurtar a fórmula mais do que é, decidi jogar golfe o que tinha. Não demorou muito até eu lembrar do meu velho amigo $\. Quando uma printdeclaração é emitida, ela $\é automaticamente anexada ao final da saída. Consegui me livrar da $a[$_]construção estranha de uma melhoria de dois bytes:

s;.;y/XVI60-9/CLXVIX/dfor$\.="32e$&"%72726;ge,$\=!print$"for 1..100

Muito melhor, mas isso $\=!print$"ainda parecia um pouco detalhado. Lembrei-me de uma fórmula alternativa de igual comprimento que encontrei que não continha o número 3em nenhuma de suas transformações de dígitos. Portanto, deve ser possível usá-lo $\=2+printe substituir o resultante 3por um espaço:

s;.;y/XVI0-9/CLXIIX V/dfor$\.="8e$&"%61535;ge,$\=2+print for 1..100

Também 67 bytes, devido ao espaço em branco necessário entre printe for.

Editar : Isso pode ser melhorado em um byte, movendo o printpara a frente:

$\=2+print!s;.;y/XVI0-9/CLXIIX V/dfor$\.="8e$&"%61535;gefor 1..100

Como a substituição precisa avaliar completamente antes de print, a atribuição a$\ ainda ocorrerá por último. A remoção do espaço em branco entre gee foremitirá um aviso de reprovação, mas é válido.

Mas, se houvesse uma fórmula que não usasse um 1 em qualquer lugar, $\=2+printtorna-se $\=printpor mais dois bytes valor de poupança. Mesmo se fosse um byte mais longo, ainda seria uma melhoria.

Acontece que essa fórmula existe, mas é um byte mais longo que o original, resultando em uma pontuação final de 65 bytes :

$\=print!s;.;y/XVI60-9/CLXXI V/dfor$\.="37e$&"%97366;gefor 1..100

Metodologia

A pergunta foi feita sobre como alguém poderia encontrar essa fórmula. Em geral, encontrar uma fórmula mágica para generalizar qualquer conjunto de dados é uma questão de probabilidade. Ou seja, você deseja escolher um formulário com a maior probabilidade possível de produzir algo semelhante ao resultado desejado.

Examinando os primeiros algarismos romanos:

0:
1: I
2: II
3: III
4: IV
5: V
6: VI
7: VII
8: VIII
9: IX

há alguma regularidade a ser vista. Especificamente, de 0 a 3 e, novamente, de 5 a 8 , cada termo sucessivo aumenta de comprimento em um numeral. Se quisermos criar um mapeamento de dígitos para numerais, gostaríamos de ter uma expressão que também aumente em um dígito para cada termo sucessivo. Uma escolha lógica é k • 10 d, em que d é o dígito correspondente ek é qualquer constante inteira.

Isso funciona para 0-3 , mas 4 precisa quebrar o padrão. O que podemos fazer aqui é aderir a um módulo:
k • 10 d % m , onde m está em algum lugar entre k • 10 3 e k • 10 4 . Isso deixará o intervalo de 0 a 3 intocado e modificará 4 para que não contenha quatro Is. Se restringirmos adicionalmente nosso algoritmo de pesquisa, de modo que o resíduo modular de 5 , vamos chamá-lo de j , seja menor que m / 1000 , isso garantirá que também tenhamos regularidade de 5 a 8 também. O resultado é algo como isto:

0: k
1: k0
2: k00
3: k000
4: ????
5: j
6: j0
7: j00
8: j000
9: ????

Como você pode ver, se substituirmos 0por I, 0-3 e 5-8 têm a garantia de serem mapeados corretamente! Os valores para 4 e 9 precisam ser forçados brutalmente. Especificamente, 4 precisa conter um 0e um j(nessa ordem) e 9 precisa conter um0 , seguido por outro dígito que não apareça em nenhum outro lugar. Certamente, existem várias outras fórmulas que, por alguma coincidência, podem produzir o resultado desejado. Alguns deles podem até ser mais curtos. Mas não creio que exista alguém com tanta probabilidade de sucesso como este.

Também experimentei várias substituições Ie / ou Vcom algum sucesso. Mas, infelizmente, nada menor do que o que eu já tinha. Aqui está uma lista das soluções mais curtas que encontrei (o número de soluções de 1 a 2 bytes mais pesadas são muitas para listar):

y/XVI60-9/CLXVIX/dfor$\.="32e$&"%72726
y/XVI0-9/CLXIXV/dfor$\.="57e$&"%474976
y/XVI0-9/CLXIVXI/dfor$\.="49e$&"%87971

y/XVI0-9/CLXIIXIV/dfor$\.="7e$&"%10606  #
y/XVI0-9/CLXIIXIV/dfor$\.="7e$&"%15909  # These are all essentially the same
y/XVI0-9/CLXIIXIV/dfor$\.="7e$&"%31818  #

y/XVI0-9/CLXIIX V/dfor$\.="8e$&"%61535  # Doesn't contain 3 anywhere

y/XVI60-9/CLXXI V/dfor$\.="37e$&"%97366 # Doesn't contain 1 anywhere
primo
fonte
3
Como você encontrou a fórmula mágica?
Ruben Verborgh
11
@RubenVerborgh Vou atualizar meu post com mais informações sobre a metodologia em breve.
Primo
15

HTML + JavaScript + CSS (137)

HTML (9)

<ol></ol>

JavaScript (101)

for(i=1;i<=100;i++){document.getElementsByTagName('ol')[0].appendChild(document.createElement('li'))}

CSS (27)

ol{list-style:upper-roman​}

Saída

Lista numerada com algarismos romanos

...

Demonstração no JSBin

Patrick Oscity
fonte
11
Versão apenas de JS de 81 bytes: document.write('<ol>'+"<li style='list-style:upper-roman'/>".repeat(100)+'</ol>')(ES6)
Paperjam
ou 66 no Chromedocument.write("<li style='list-style:upper-roman'/>".repeat(100))
Slai
10

Python 116

melhor código de golfe da resposta do scleaver:

r=lambda a,b,c:('',a,2*a,3*a,a+b,b,b+a,b+a+a,b+3*a,a+c);print' '.join(i+j for i in r(*'XLC')for j in r(*'IVX'))+' C'
Daniel
fonte
8

Python, 139

print' '.join(' '.join(i+j for  j in ' _I_II_III_IV_V_VI_VII_VIII_IX'.split('_'))for i in ' _X_XX_XXX_XL_L_LX_LXX_LXXX_XC'.split('_'))+' C'
cortador
fonte
6

C, 177 160 147 caracteres

Existem soluções mais curtas, mas nenhuma em C, então aqui está minha tentativa.

Nova solução, completamente diferente da anterior:

char*c;
f(n){
    printf("%.*s",n%5>3?2:n%5+n/5,c+=n%5>3?n%4*4:2-n/5);
}
main(i){
        for(;i<100;putchar(32))
                c="XLXXXC",f(i/10),
                c="IVIIIX",f(i++%10);
        puts("C");
}

Solução anterior (160 caracteres):

Lógica:
1. fimprime um número de 1 a 10. csão os dígitos usados, que podem ser IVXou XLC. Chamado uma vez para as dezenas uma vez para os.
2. Se n%5==0- imprima nada ou o c[n/5]que é Iou V(ou Lou C).
3. Se n%4==4- 4ou 9- imprimir I(ou X), por n+1.
4. Se n>4- imprima 5( Vou seja L) então n-5.
5. Se n<4- imprima Ientão n-1(ie nhoras I).

char*c;
p(c){putchar(c);}
f(n){
        n%5?
                n%5>3?
                        f(1),f(n+1):
                        n>4?
                                f(5),f(n-5):
                                f(n-1,p(*c)):
                n&&p(c[n/5]);
}
main(i){
        for(;++i<101;p(32))
                c="XLC",f(i/10),
                c="IVX",f(i%10);
        p(10);
}
Ugoren
fonte
137:f(c,n){printf("%.*s",n%5>3?2:n%5+n/5,"XLXXXCIVIIIX "+c+(n%5>3?n%4*4:2-n/5));}main(i){for(;i<100;f(12,4))f(0,i/10),f(6,i++%10);puts("C");}
gastropner 28/01
5

JavaScript, 123

Inspirado por uma versão mais longa, me deparei com um grupo de notícias polonês (pelo menos, o Chrome pensava que era polonês).

for(i=100,a=[];n=i--;a[i]=r)
  for(r=y='',x=5;n;y++,x^=7)
    for(m=n%x,n=n/x^0;m--;)
      r='IVXLC'[m>2?y+n-(n&=-2)+(m=1):+y]+r;
alert(a)
Paul Walls
fonte
5

Q ( 81 80)

2º corte:

1_,/'[($)``X`XX`XXX`XL`L`LX`LXX`LXXX`XC cross``I`II`III`IV`V`VI`VII`VIII`IX],"C"

1º corte:

1_,/'[$:[``X`XX`XXX`XL`L`LX`LXX`LXXX`XC cross``I`II`III`IV`V`VI`VII`VIII`IX]],"C"
scottstein37
fonte
4

Python, 168

r=lambda n,l,v:(r(n,l[1:],v[1:])if n<v[0]else l[0]+r(n-v[0],l,v))if n else''
for i in range(1,101):print r(i,'C XC L XL X IX V IV I'.split(),[100,90,50,40,10,9,5,4,1]),

Explicação

Usando esses valores, pegue o maior valor que não seja maior que n e subtraia-o de n. Repita até n ser 0.

'C'  = 100
'XC' = 90
'L'  = 50
'XL' = 40
'X'  = 10
'IX' = 9
'V'  = 5
'IV' = 4
'I'  = 1
caixa de papelão
fonte
11
r=lambda n,l,v:n and(n<v[0]and r(n,l[1:],v[1:])or l[0]+r(n-v[0],l,v))or""salva dois caracteres. Caso contrário, muito bom.
cemper93
4

Rubi 1.9, 140 132

r=" "
100.times{r+=?I
0while[[?I*4,"IV"],["VIV","IX"],[?X*4,"XL"],["LXL","XC"],[/(.)((?!\1)[^I])\1/,'\2']].any?{|q|r.sub! *q}
$><<r}

Isso literalmente conta de 1 a 100 em algarismos romanos. Inicia com uma sequência em branco e, em seguida, passa pelo anexo "I" e aplica repetidamente uma série de regras de substituição, adicionando efetivamente 1.

Edit: Adicionado o número da versão, já que ?Ifunciona apenas em 1.9, e usou as alterações de @ Howard para aparar alguns caracteres.

histocrata
fonte
Você pode salvar dois caracteres: r while-> 0while, r.sub!(*q)-> r.sub! *q. Você também pode arrastar a impressão para dentro do loop e usar em 100.times{...}vez da instrução map.
Howard
(%w[IIII VIV XXXX LXL]<</(.)((?!\1)[^I])\1/).zip(%w(IV IX XL XC)<<'\2')salva 7 caracteres.
steenslag
4

Ruby 112 chars

101.times{|n|r=' ';[100,90,50,40,10,9,5,4,1].zip(%w(C XC L XL X IX V IV I)){|(k,v)|a,n=n.divmod k;r<<v*a};$><<r}

Basicamente, usando o to_romanmétodo explicado aqui , mas usando uma matriz compactada para resumir.

steenslag
fonte
4

Mathematica 159 150 142

c = {100, 90, 50, 40, 10, 9, 5, 4, 1};
Table["" <> Flatten[ConstantArray @@@ Thread@{StringSplit@"C XC L XL X IX V IV I", 
  FoldList[Mod, k, Most@c]~Quotient~c}], {k, 100}]

números romanos


Solução integrada : IntegerString38 caracteres

IntegerString[k, "Roman"]~Table~{k, 100}
DavidC
fonte
2

perl 205

@r = split //, "IVXLC";
@n = (1, 5, 10, 50, 100);

for $num (1..100) {
  for($i=@r-1; $i>=0; $i--) {
    $d = int($num / $n[$i]);
    next if not $d;
    $_ .= $r[$i] x $d;
    $num -= $d * $n[$i];
  }
  $_ .= " ";
}
s/LXXXX/XC/g;
s/XXXX/XL/g;
s/VIIII/IX/g;
s/IIII/IV/g;
print;

Golfe:

@r=split//,"IVXLC";@n=(1,5,10,50,100);for$num(1..100){for($i=@r-1;$i>=0;$i--){$d=int($num/$n[$i]);next if!$d;$_.=$r[$i]x$d;$num-=$d*$n[$i];}$_.=" ";}s/LXXXX/XC/g;s/XXXX/XL/g;s/VIIII/IX/g;s/IIII/IV/g;print;
Thor
fonte
2

MUMPS 184

S V(100)="C",V(90)="XC",V(50)="L",V(40)="XL",V(10)="X",V(9)="IX",V(5)="V",V(4)="IV",V(1)="I" F I=1:1:100 S S=I,N="" F  Q:'S  S N=$O(V(N),-1) I S&(S'<N ) S S=S-N W V(N) S N="" w:'S " "

O mesmo algoritmo que @cardboard_box, de quem eu tirei a explicação literalmente -

Explicação

Usando esses valores, pegue o maior valor que não seja maior que n e subtraia-o de n. Repita até n ser 0.

'C'  = 100
'XC' = 90
'L'  = 50
'XL' = 40
'X'  = 10
'IX' = 9
'V'  = 5
'IV' = 4
'I'  = 1
psr
fonte
2

R , 85 bytes

R=.romans
for(r in 1:100){while(r>0){cat(names(R[I<-R<=r][1]))
r=r-R[I][1]}
cat(" ")}

Experimente online!

Usa a utilsvariável de pacote aleatório .romanspara obter os valores dos números romanos, mas faz a conversão por si só; a abordagem integrada seria 20 bytes:cat(as.roman(1:100))

Giuseppe
fonte
Surpreendentemente, a abordagem interna que você está mencionando não funciona como está ... é preciso digitar cat(paste(as.roman(1:100)))ou simplesmente as.roman(1:100). Esquisito.
Jayce
@JayCe odd; Na verdade, não devo tê-lo testado ... os documentos catindicam que ele realiza menos conversões do que printe só funciona em atomicvetores - o que explica isso!
Giuseppe
1

APL 128

Eu tentei uma solução de indexação no APL:

r←⍬                                                                             
i←1                                                      
l:r←r,' ',('   CXI LV CX'[,⍉((1+(4 4 2 2⊤0 16 20 22 24 32 36 38 39 28)[;1+(3⍴10)⊤i])×3)-4 3⍴2 1 0])~' '
→(100≥i←i+1)/l                                                                  
r              

Pode ser 4 bytes mais curto na origem do índice 0 em vez de 1, mas o espaço real é a geração da matriz de índices via:

4 4 2 2⊤0 16 20 22 24 32 36 38 39 28

Até agora não consegui gerar os índices rapidamente!

Graham
fonte
1

LaTeX (138)

\documentclass{minimal}
\usepackage{forloop}
\begin{document}
\newcounter{i}
\forloop{i}{1}{\value{i} < 101}{\roman{i}\par}
\end{document}
Patrick Oscity
fonte
11
-1: a questão diz "Você não pode usar qualquer construído em função de transformar em algarismos romanos"
izabera
1

Python, 125

' '.join(i+j for i in['']+'X XX XXX XL L LX LXX LXXX XC C'.split()for j in['']+'I II III IV V VI VII VIII IX'.split())[1:-38]
TheCrypt
fonte
1

PHP, 38 37 bytes

<ol type=I><?=str_repeat("<li>",100);

-1 byte graças a @manatwork

Mesma ideia da resposta de Patrick , mas em uma linguagem mais compacta. Beats Mathematica !

Experimente Online!

geokavel
fonte
Encerre a declaração com ;, então não há necessidade ?>.
manatwork
1

VBA (Excel), 245 bytes

Função criada para Repetição e Substituição - 91 bytes

Function s(a,b):s=String(a,b):End Function Function b(x,y,z):b=Replace(x,y,z):End Function

usando janela imediata ( 154 bytes )

p="I":for x=1to 100:?b(b(b(b(b(b(b(b(s(x,p),s(100,p),"C"),s(90,p),"XC"),s(50,p),"L"),s(40,p),"XL"),s(10,p),"X"),s(9,p),"IX"),s(5,p),"V"),s(4,p),"IV"):next

remoel
fonte
0

Java (OpenJDK 8) , 152 bytes

a->{String[] t=",X,XX,XXX,XL,L,LX,LXX,LXXX,XC,,I,II,III,IV,V,VI,VII,VIII,IX".split(",");for(int i=1;i<100;i++){a+=t[i/10]+t[i%10+10]+" ";}return a+"C";}

Experimente online!

Explicação:

String[] t=",X,XX,XXX,XL,L,LX,LXX,LXXX,XC,,I,II,III,IV,V,VI,VII,VIII,IX".split(",");
//Create an array of numerals, first half represents tens place, second half represents ones place
    for(int i=1;i<100;i++){             
//Loop 99 times
        a+=t[i/10]+t[i%10+10]+" ";   
//Add tens place and ones place to the string
    }return a+"C";                         
//Add numeral for 100 and return the string
X1M4L
fonte
0

TeX, 354 bytes

\let~\let~\d\def~\a\advance~\b\divide~\x\expandafter~\f\ifnum{~~\newcount~\n~\i~\j~\k~\u~\v}~~\or\d\p#1{\ifcase#1C~2~L~5~X~2~V~5~I\fi}\d\q#1{\p{#1~}}\d\r{\j0
\v100\d\m{\d\w{\f\n<\v\else\p\j\a\n-\v\x\w\fi}\w\f\n>0\k\j\u\v\d\g{\a\k2\b\u\q\k}\g\f\q\k=2\g\fi\a\n\u\f\n<\v\a\n-\u\a\j2\b\v\q\j\else\p\k\fi\x\m\fi}\m}\i1\d\c{
\f\i<101 \n\i\r\a\i1 \x\c\fi}\c\bye

Alguma explicação: O TeX fornece um comando \romannumeralinterno para converter números em números romanos. Como a pergunta não permite o uso de funções internas, o código acima é uma versão em golf do mesmo algoritmo que o compilador TeX original de Knuth usa para \romannumeral(consulte TeX: The Program , § 69,print_roman_int ) reimplementado no TeX.

Como ele quer deixar a alegria de descobrir como esse código funciona para o leitor, Knuth se recusa a dar uma explicação para essa parte do código. Então, seguirei o exemplo e darei uma versão não-gasta e levemente modificada, mais próxima do original do que o código acima:

\newcount\n
\newcount\j
\newcount\k
\newcount\u
\newcount\v

\def\chrnum#1{\ifcase#1m\or 2\or d\or 5\or c\or 2\or l\or 5\or x\or 2\or v\or 5\or i\fi}
\def\chrnumM#1{\chrnum{#1\or}}

\def\roman#1{%
    \n=#1\relax
    \j=0\relax
    \v=1000\relax
    \def\mainloop{%
        \def\while{%
            \ifnum\n<\v
            \else
                \chrnum\j
                \advance\n -\v
                \expandafter\while
            \fi
        }\while
        \ifnum\n>0\relax
            \k=\j \advance\k 2\relax
            \u=\v \divide\u \chrnumM\k
            \ifnum\chrnumM\k=2\relax
                \advance\k 2\relax
                \divide\u \chrnumM\k
            \fi
            \advance\n \u
            \ifnum\n<\v
                \advance\n -\u
                \advance\j 2\relax
                \divide\v \chrnumM\j
            \else
                \chrnum\k
            \fi
            \expandafter\mainloop
        \fi
    }\mainloop
}

\newcount\i \i=1
\def\countloop{%
    \ifnum\i<100\relax
        \roman\i\ 
        \advance\i 1
        \expandafter\countloop
    \fi
}\countloop
\bye
siracusa
fonte