Contare da 1 a 100 ... in numeri romani


29

Scrivi un programma che conta da 1 a 100 in numeri romani e stampa questi numeri in output standard. Ognuno dei numeri deve essere separato da spazi.

Non è possibile utilizzare alcuna funzione incorporata per trasformarsi in numeri romani, né applicazioni esterne o librerie per farlo.

Il risultato desiderato è

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

Dato che si tratta di una sfida di golf del codice, vince il codice più corto .


4
39 manca un X.
Thor,

@Thor Risolto, grazie;)
Averroes il

1
Voglio davvero usare INTERCAL per questo.
Weijun Zhou

può essere separato da newline? E che dire del trailing / spazi iniziali / newline?
FantaC

Risposte:


68

Perl 69 byte

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

Funziona con la formula magica. L'espressione "32e$&"%72726trasforma ogni cifra nel modo seguente:
0⇒32, 1⇒320, 2⇒3200, 3⇒32000, 4⇒29096, 5⇒56, 6⇒560, 7⇒5600, 8⇒56000, 9⇒50918

Dopo aver applicato la traduzione y/016/IXV/, abbiamo invece questo:
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

Il resto delle cifre ( 2-57-9) vengono rimosse. Si noti che questo potrebbe essere migliorata un byte utilizzando una formula che traduce 012invece 016, semplificando /XVI60-9/al /XVI0-9/. Non sono riuscito a trovarne uno, ma forse avrai più fortuna.

Una volta che una cifra è stata trasformata in questo modo, il processo si ripete per la cifra successiva, aggiungendo il risultato e traducendo le XVIs precedenti CLXcontemporaneamente alla traduzione per la nuova cifra.

Aggiorna La
ricerca esaustiva non ha rivelato niente di più breve. Tuttavia, ho trovato una soluzione alternativa a 69 byte:

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

Questo utilizza una 0-2sostituzione per IXV, ma ha un modulo più lungo di una cifra.


Aggiornamento: 66 65 byte

Questa versione è notevolmente diversa, quindi dovrei probabilmente dire qualche parola al riguardo. La formula che utilizza è in realtà un byte in più!

Incapace di abbreviare la formula più di quello che è, ho deciso di giocare a golf quello che avevo. Non passò molto tempo che mi ricordai del mio vecchio amico $\. Quando printviene emessa una dichiarazione, $\viene automaticamente aggiunta alla fine dell'output. Sono stato in grado di liberarmi della $a[$_]costruzione scomoda per un miglioramento di due byte:

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

Molto meglio, ma $\=!print$"sembrava ancora un po 'prolisso. Ho poi ricordato una formula alternativa di uguale lunghezza che avevo trovato che non conteneva il numero 3in nessuna delle sue trasformazioni di cifre. Quindi, dovrebbe essere possibile utilizzare $\=2+printinvece e sostituire il risultato 3con uno spazio:

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

Anche 67 byte, a causa dello spazio bianco necessario tra printe for.

Modifica : questo può essere migliorato di un byte, spostando printin primo piano:

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

Poiché la sostituzione deve essere valutata completamente prima di print, l'assegnazione a $\verrà comunque eseguita per ultima. La rimozione dello spazio tra gee forgenererà un avviso di deprecazione, ma è altrimenti valida.

Ma, se esistesse una formula che non utilizzava un 1punto qualsiasi, $\=2+printdiventa $\=printun risparmio di altri due byte. Anche se fosse più lungo di un byte, sarebbe comunque un miglioramento.

A quanto pare, esiste una formula del genere, ma è più lunga di un byte rispetto all'originale, con un punteggio finale di 65 byte :

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

Metodologia

Alla domanda è stato chiesto come si potrebbe andare a trovare una tale formula. In generale, trovare una formula magica per generalizzare qualsiasi insieme di dati è una questione di probabilità. Cioè, vuoi scegliere una forma che è il più probabile possibile per produrre qualcosa di simile al risultato desiderato.

Esporre i primi numeri romani:

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

c'è un po 'di regolarità da vedere. In particolare, da 0-3 e poi di nuovo da 5-8 , ogni termine successivo aumenta in lunghezza di un numero. Se volessimo creare una mappatura da cifre a numeri, vorremmo avere un'espressione che aumenti anche in lunghezza di una cifra per ogni termine successivo. Una scelta logica è k • 10 d dove d è la cifra corrispondente e k è qualsiasi costante intera.

Funziona per 0-3 , ma 4 deve interrompere il modello. Quello che possiamo fare qui è virare su un modulo:
k • 10 d % m , dove m è da qualche parte tra k • 10 3 e k • 10 4 . Questo lascerà intatto l'intervallo 0-3 e modificherà 4 in modo tale che non contenga quattro Isecondi. Se limitiamo ulteriormente il nostro algoritmo di ricerca in modo tale che il residuo modulare di 5 , chiamiamolo j , sia inferiore a m / 1000 , ciò assicurerà che anche la regolarità sia compresa tra 5-8 . Il risultato è qualcosa del genere:

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

Come puoi vedere, se sostituiamo 0con I, 0-3 e 5-8 sono tutti garantiti per essere mappati correttamente! Tuttavia, i valori per 4 e 9 devono essere forzati brutalmente. In particolare, 4 deve contenere uno 0e uno j(in quell'ordine) e 9 deve contenere uno 0, seguito da un'altra cifra che non appare da nessun'altra parte. Certamente, ci sono un certo numero di altre formule, che per un caso di coincidenza potrebbero produrre il risultato desiderato. Alcuni potrebbero anche essere più brevi. Ma non credo che ci siano quelli che hanno le stesse probabilità di successo come questo.

Ho anche sperimentato più sostituzioni per Ie / o Vcon un certo successo. Ma ahimè, niente di più breve di quello che avevo già. Ecco un elenco delle soluzioni più brevi che ho trovato (il numero di soluzioni 1-2 byte più pesanti sono troppe da elencare):

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

3
Come hai trovato la formula magica?
Ruben Verborgh,

1
@RubenVerborgh Presto aggiornerò il mio post con ulteriori informazioni sulla metodologia.
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​}

Produzione

Elenco numerato con numeri romani

...

Demo su JSBin


1
Versione solo JS da 81 byte: document.write('<ol>'+"<li style='list-style:upper-roman'/>".repeat(100)+'</ol>')(ES6)
Paperjam,

o 66 in Chromedocument.write("<li style='list-style:upper-roman'/>".repeat(100))
Slai,

10

Python 116

migliore codice di golf della risposta di 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'

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'

6

C, 177 160 147 caratteri

Ci sono soluzioni più brevi, ma nessuna in C, quindi ecco il mio tentativo.

Nuova soluzione, completamente diversa dalla mia precedente:

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

Soluzione precedente (160 caratteri):

Logica:
1. fstampa un numero da 1 a 10. csono le cifre utilizzate, che possono essere IVXo XLC. Chiamato una volta per le decine una volta per quelle.
2. If n%5==0: non stampa nulla o c[n/5]quale è Io V(o Lo C).
3. Se n%4==4- 4o 9- stampa I(o X), entro n+1.
4. If n>4- stampa 5(cioè Vo L) quindi n-5.
5. If n<4- stampa Iquindi n-1(cioè nvolte 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);
}

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

Ispirato da una versione più lunga mi sono imbattuto in un newsgroup polacco (almeno, Chrome pensava che fosse polacco).

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)

5

Q ( 81 80)

2o taglio:

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

1o taglio:

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

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]),

Spiegazione

Usando questi valori, prendi il valore più grande non maggiore di n e sottralo da n. Ripeti fino a quando n è 0.

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

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""salva due personaggi. Altrimenti molto bello.
cemper93,

4

Ruby 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}

Questo conta letteralmente da 1 a 100 in numeri romani. Inizia con una stringa vuota, quindi passa in rassegna aggiungendo "I" e quindi applicando ripetutamente una serie di regole di sostituzione, aggiungendo effettivamente 1.

Modifica: Aggiunto il numero di versione, poiché ?Ifunziona solo in 1.9, e ha usato le modifiche di @ Howard per tagliare alcuni caratteri.


Puoi salvare due caratteri: r while-> 0while, r.sub!(*q)-> r.sub! *q. Puoi anche trascinare la stampa all'interno del loop e utilizzare al 100.times{...}posto dell'istruzione map.
Howard,

(%w[IIII VIV XXXX LXL]<</(.)((?!\1)[^I])\1/).zip(%w(IV IX XL XC)<<'\2')salva 7 caratteri.
steenslag,

4

Ruby 112 caratteri

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}

Fondamentalmente usando il to_romanmetodo spiegato qui , ma usando un array zippato per brevità.


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

numeri romani


Soluzione integrata : IntegerString38 caratteri

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

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;

golfed:

@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;

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

Stesso algoritmo di @cardboard_box, da cui ho tratto la spiegazione alla lettera -

Spiegazione

Usando questi valori, prendi il valore più grande non maggiore di n e sottralo da n. Ripeti fino a quando n è 0.

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

2

R , 85 byte

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

Provalo online!

Utilizza la utilsvariabile pacchetto casuale .romansper ottenere i valori dei numeri romani, ma esegue la conversione da sola; l'approccio incorporato sarebbe di 20 byte:cat(as.roman(1:100))


Sorprendentemente, l'approccio incorporato che stai citando non funziona come è ... bisogna digitare cat(paste(as.roman(1:100)))o semplicemente as.roman(1:100). Strano.
JayCe,

@JayCe dispari; Non devo averlo effettivamente testato ... i documenti per catindicano che esegue meno conversioni di printe funziona solo su atomicvettori - questo spiega questo!
Giuseppe,

1

APL 128

Ho provato una soluzione di indicizzazione in 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              

Può essere più corto di 4 byte nell'origine dell'indice 0 invece di 1 ma il vero spazio hog è la generazione della matrice dell'indice tramite:

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

Finora non sono stato in grado di generare gli indici al volo!


1

LaTeX (138)

\documentclass{minimal}
\usepackage{forloop}
\begin{document}
\newcounter{i}
\forloop{i}{1}{\value{i} < 101}{\roman{i}\par}
\end{document}

1
-1: la domanda dice "Non puoi usare nessuna funzione incorporata per trasformare in numeri romani"
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]

1

PHP, 38 37 byte

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

-1 byte grazie a @manatwork

Stessa idea della risposta di Patrick , ma in un linguaggio più compatto. Beats Mathematica !

Provalo online!


Termina la dichiarazione con ;, quindi non è necessario ?>.
arte

1

VBA (Excel), 245 byte

Funzione creata per ripetizione e sostituzione - 91 byte

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

utilizzando la finestra immediata ( 154 byte )

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


0

Java (OpenJDK 8) , 152 byte

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

Provalo online!

Spiegazione:

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

0

TeX, 354 byte

\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

Qualche spiegazione: TeX fornisce un comando integrato \romannumeralper convertire i numeri in numeri romani. Poiché la domanda non consente di utilizzare le funzioni integrate, il codice sopra riportato è una versione golfata dello stesso algoritmo utilizzato dal compilatore TeX originale di Knuth \romannumeral(vedi TeX: Il programma , § 69,print_roman_int ) reimplementato in TeX.

Mentre vuole lasciare la gioia di sconcertare il modo in cui questo codice funziona per il lettore, Knuth si rifiuta di dare una spiegazione di questa parte del codice. Quindi seguirò l'esempio e darò solo una versione non modificata e leggermente modificata, che è più vicina all'originale del codice sopra:

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