Cuenta de 1 a 100 ... en números romanos

29

Escriba un programa que cuente del 1 al 100 en números romanos e imprima estos números por salida estándar. Cada uno de los números debe estar separado por espacios.

No puede utilizar ninguna función integrada para transformar a números romanos ni una aplicación externa o biblioteca para hacerlo.

El resultado deseado es

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

Como es un desafío de código de golf, gana el código más corto .

Averroes
fuente
44
A 39 le falta una X.
Thor
@Thor Fixed, gracias;)
Averroes
1
Realmente quiero usar INTERCAL para este.
Weijun Zhou
¿Se puede separar por nuevas líneas? Además, ¿qué pasa con espacios finales / iniciales / líneas nuevas?
FantaC

Respuestas:

68

Perl 69 bytes

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

Funciona a modo de fórmula mágica. La expresión "32e$&"%72726transforma cada dígito de la siguiente manera:
0⇒32, 1⇒320, 2⇒3200, 3⇒32000, 4⇒29096, 5⇒56, 6⇒560, 7⇒5600, 8⇒56000, 9⇒50918

Después de aplicar la traducción y/016/IXV/, tenemos esto en su lugar:
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

El resto de los dígitos ( 2-57-9) se eliminan. Tenga en cuenta que esto podría mejorarse en un byte mediante el uso de una fórmula que se traduce en 012lugar de 016, simplificando /XVI60-9/a /XVI0-9/. No pude encontrar uno, pero quizás tengas mejor suerte.

Una vez que un dígito se ha transformado de esta manera, el proceso se repite para el siguiente dígito, agregando el resultado y traduciendo los XVIs anteriores al CLXmismo tiempo que ocurre la traducción para el nuevo dígito.

Actualización La
búsqueda exhaustiva no reveló nada más corto. Sin embargo, encontré una solución alternativa de 69 bytes:

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

Este usa una 0-2sustitución para IXV, pero tiene un módulo que es un dígito más largo.


Actualización: 66 65 bytes

Esta versión es notablemente diferente, por lo que probablemente debería decir algunas palabras al respecto. ¡La fórmula que usa es en realidad un byte más!

Incapaz de acortar la fórmula más de lo que es, decidí jugar al golf con lo que tenía. No pasó mucho tiempo hasta que recordé a mi viejo amigo $\. Cuando printse emite una declaración, $\se agrega automáticamente al final de la salida. Pude deshacerme de la $a[$_]construcción incómoda para una mejora de dos bytes:

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

Mucho mejor, pero eso $\=!print$"todavía se veía un poco detallado. Entonces recordé una fórmula alternativa de igual longitud que había encontrado que no contenía el número 3en ninguna de sus transformaciones de dígitos. Por lo tanto, debería ser posible usar $\=2+printen su lugar, y sustituir el resultado 3con un espacio:

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

También 67 bytes, debido al espacio en blanco necesario entre printy for.

Editar : Esto se puede mejorar en un byte, moviendo printal frente:

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

Debido a que la sustitución debe evaluarse completamente antes de la print, la asignación a $\ocurrirá en último lugar. Eliminar el espacio en blanco entre gey foremitirá una advertencia de desaprobación, pero por lo demás es válido.

Pero, si hubiera una fórmula que no utilizara un 1lugar, se $\=2+printconvierte $\=printen otros dos bytes de ahorro. Incluso si fuera un byte más, aún sería una mejora.

Como resultado, dicha fórmula existe, pero es un byte más larga que la original, lo que resulta en una puntuación final de 65 bytes :

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

Metodología

Se hizo la pregunta de cómo se puede encontrar una fórmula de este tipo. En general, encontrar una fórmula mágica para generalizar cualquier conjunto de datos es una cuestión de probabilidad. Es decir, desea elegir una forma que sea lo más probable posible para producir algo similar al resultado deseado.

Examing los primeros números romanos:

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

Hay cierta regularidad para ser visto. Específicamente, de 0-3 y luego nuevamente de 5-8 , cada término sucesivo aumenta en longitud en un número. Si quisiéramos crear un mapeo de dígitos a números, querríamos tener una expresión que también aumente su longitud en un dígito por cada término sucesivo. Una opción lógica es k • 10 d donde d es el dígito correspondiente yk es cualquier constante entera.

Esto funciona para 0-3 , pero 4 necesita romper el patrón. Lo que podemos hacer aquí es tachuela en un módulo:
k • 10 d % m , donde m es un lugar entre k • 10 3 y k • 10 4 . Esto dejará el rango 0-3 intacto y modificará 4 de manera tal que no contendrá cuatro Is. Si además restringimos nuestro algoritmo de búsqueda de modo que el residuo modular de 5 , llamémoslo j , sea menor que m / 1000 , esto asegurará que también tengamos regularidad de 5-8 . El resultado es algo como esto:

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

Como puede ver, si reemplazamos 0con I, ¡ 0-3 y 5-8 están garantizados para mapearse correctamente! Sin embargo, los valores para 4 y 9 deben ser forzados. Específicamente, 4 debe contener uno 0y uno j(en ese orden), y 9 debe contener uno 0, seguido de otro dígito que no aparece en ningún otro lugar. Ciertamente, hay una serie de otras fórmulas, que por casualidad podrían producir el resultado deseado. Algunos de ellos incluso pueden ser más cortos. Pero no creo que haya ninguno que tenga tanto éxito como este.

También experimenté con múltiples reemplazos para Iy / o Vcon algún éxito. Pero, por desgracia, nada más corto que lo que ya tenía. Aquí hay una lista de las soluciones más cortas que encontré (la cantidad de soluciones de 1-2 bytes más pesadas son demasiadas para enumerarlas):

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
fuente
3
¿Cómo encontraste la fórmula mágica?
Ruben Verborgh
1
@RubenVerborgh Actualizaré mi publicación con más información sobre la metodología pronto.
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​}

Salida

Lista numerada con números romanos.

...

Demo en JSBin

Patrick Oscity
fuente
1
Versión de solo 81 bytes JS: document.write('<ol>'+"<li style='list-style:upper-roman'/>".repeat(100)+'</ol>')(ES6)
Paperjam
o 66 en Chromedocument.write("<li style='list-style:upper-roman'/>".repeat(100))
Slai
10

Python 116

mejor código de golf de la respuesta de 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
fuente
8

Pitón, 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'
Scleaver
fuente
6

C, 177 160 147 caracteres

Hay soluciones más cortas, pero ninguna en C, así que aquí está mi intento.

Nueva solución, completamente diferente de la 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");
}

Solución anterior (160 caracteres):

Lógica:
1. fimprime un número del 1 al 10. cson los dígitos utilizados, que pueden ser IVXo XLC. Llamado una vez por las decenas una vez por las.
2. If n%5==0: no imprime nada o c[n/5]cuál es Io V(o Lo C).
3. Si n%4==4- 4o 9- imprime I(o X), por n+1.
4. If n>4- imprimir 5(es decir, Vo L) entonces n-5.
5. If n<4- imprime Ientonces n-1(es decir, nveces 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
fuente
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
5

JavaScript, 123

Inspirado por una versión más larga que encontré en un grupo de noticias polaco (al menos, Chrome pensó que era polaco).

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
fuente
5

Q ( 81 80)

2do corte:

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

1er corte:

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

Pitón, 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]),

Explicación

Usando estos valores, tome el valor más grande no mayor que n y reste de n. Repita hasta que n sea 0.

'C'  = 100
'XC' = 90
'L'  = 50
'XL' = 40
'X'  = 10
'IX' = 9
'V'  = 5
'IV' = 4
'I'  = 1
caja de cartón
fuente
1
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""Guarda dos personajes. De lo contrario, muy agradable.
cemper93
4

Rubí 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}

Esto literalmente cuenta del 1 al 100 en números romanos. Comienza con una cadena en blanco, luego se repite agregando "I" y luego aplica repetidamente una serie de reglas de sustitución, agregando efectivamente 1.

Editar: se agregó el número de versión, ya que ?Isolo funciona en 1.9, y se usaron los cambios de @ Howard para recortar algunos caracteres.

histocrat
fuente
Puede guardar dos caracteres: r while-> 0while, r.sub!(*q)-> r.sub! *q. También puede arrastrar la impresión dentro del bucle y usarla en 100.times{...}lugar de la declaración del mapa.
Howard
(%w[IIII VIV XXXX LXL]<</(.)((?!\1)[^I])\1/).zip(%w(IV IX XL XC)<<'\2')ahorra 7 caracteres.
steenslag
4

Ruby 112 caracteres

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}

Básicamente usando el to_romanmétodo explicado aquí , pero usando una matriz comprimida por brevedad.

steenslag
fuente
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


Solución incorporada : IntegerString38 caracteres

IntegerString[k, "Roman"]~Table~{k, 100}
DavidC
fuente
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;

Golfizado:

@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
fuente
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 " "

Mismo algoritmo que @cardboard_box, de quien tomé la explicación literalmente:

Explicación

Usando estos valores, tome el valor más grande no mayor que n y reste de n. Repita hasta que n sea 0.

'C'  = 100
'XC' = 90
'L'  = 50
'XL' = 40
'X'  = 10
'IX' = 9
'V'  = 5
'IV' = 4
'I'  = 1
psr
fuente
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(" ")}

Pruébalo en línea!

Utiliza la utilsvariable de paquete aleatorio .romanspara obtener los valores de los números romanos, pero realiza la conversión por sí mismo; el enfoque integrado sería de 20 bytes:cat(as.roman(1:100))

Giuseppe
fuente
Sorprendentemente, el enfoque integrado que está mencionando no funciona como está ... uno tiene que escribir cat(paste(as.roman(1:100)))o simplemente as.roman(1:100). Extraño.
JayCe
@JayCe impar; Realmente no debo haberlo probado ... los documentos para catindican que realiza menos conversión que printy solo funciona en atomicvectores, ¡eso explica esto!
Giuseppe
1

APL 128

Probé una solución de indexación en 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              

Puede ser 4 bytes más corto en el índice de origen 0 en lugar de 1, pero el espacio real es la generación de la matriz de índice a través de:

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

¡Hasta ahora no he podido generar los índices sobre la marcha!

Graham
fuente
1

LaTeX (138)

\documentclass{minimal}
\usepackage{forloop}
\begin{document}
\newcounter{i}
\forloop{i}{1}{\value{i} < 101}{\roman{i}\par}
\end{document}
Patrick Oscity
fuente
1
-1: la pregunta dice "No se puede usar ninguna función integrada para transformar a números 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]
La cripta
fuente
1

PHP, 38 37 bytes

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

-1 byte gracias a @manatwork

La misma idea que la respuesta de Patrick , pero en un lenguaje más compacto. Beats Mathematica !

Pruébalo en línea!

geokavel
fuente
Termine la declaración con ;, luego no es necesario ?>.
manatwork
1

VBA (Excel), 245 bytes

Función creada para repetición y reemplazo - 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 la ventana inmediata ( 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

remodelar
fuente
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";}

Pruébalo en línea!

Explicación:

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
fuente
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

Alguna explicación: TeX proporciona un comando incorporado \romannumeral para convertir números a números romanos. Como la pregunta no permite usar funciones integradas, el código anterior es una versión desarrollada del mismo algoritmo que el compilador TeX original de Knuth usa para \romannumeral(ver TeX: El Programa , § 69, print_roman_int) re-implementado en TeX.

Como quiere dejar la alegría de desconcertar cómo funciona este código para el lector, Knuth se niega a dar una explicación de esta parte del código. Así que haré lo mismo y solo daré una versión no modificada y ligeramente modificada, que está más cerca del original que el código anterior:

\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
fuente