Policz od 1 do 100… cyframi rzymskimi

29

Napisz program liczący od 1 do 100 cyframi rzymskimi i wydrukuj te liczby na standardowym wyjściu. Każda z liczb musi być oddzielona spacjami.

Nie można użyć żadnej wbudowanej funkcji do przekształcenia na cyfry rzymskie ani zewnętrznej aplikacji lub biblioteki.

Pożądanym rezultatem jest

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

Ponieważ jest to wyzwanie dla golfa, wygrywa najkrótszy kod .

Averroes
źródło
4
39 brakuje X.
Thor
@Thor Naprawiono, dziękuję;)
Averroes,
1
Naprawdę chcę użyć INTERCAL do tego.
Weijun Zhou
czy można go oddzielić znakami nowej linii? A co z końcowymi / wiodącymi spacjami / znakami nowej linii?
FantaC

Odpowiedzi:

68

Perl 69 bajtów

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

Działa na podstawie magicznej formuły. Wyrażenie "32e$&"%72726przekształca każdą cyfrę w następujący sposób:
0 32 32, 1 20 320, 2 32 3200, 3 32 32000, 4 2 29096, 5 56 56, 6 5 560, 7 56 5600, 8 56 56000, 9 ⇒ 50918

Po zastosowaniu tłumaczenia y/016/IXV/otrzymujemy zamiast tego:
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

Reszta cyfr ( 2-57-9) jest usuwana. Należy zauważyć, że to może być poprawiona przez jeden bajt przy użyciu wzoru, co przekłada 012zamiast 016upraszcza /XVI60-9/się /XVI0-9/. Nie udało mi się go znaleźć, ale może będziesz miał więcej szczęścia.

Po przekształceniu jednej cyfry w ten sposób proces powtarza się dla następnej cyfry, dołączając wynik i tłumacząc poprzednie XVIs CLXw tym samym czasie, gdy następuje tłumaczenie nowej cyfry.

Aktualizacja
Wyczerpujące wyszukiwanie nie ujawniło nic krótszego. Znalazłem jednak alternatywne 69-bajtowe rozwiązanie:

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

Ten używa 0-2podstawienia IXV, ale ma moduł dłuższy o jedną cyfrę.


Aktualizacja: 66 65 bajtów

Ta wersja jest wyraźnie inna, więc prawdopodobnie powinienem powiedzieć o niej kilka słów. Formuła, której używa, jest w rzeczywistości o jeden bajt dłużej!

Nie mogąc skrócić formuły dłużej, zdecydowałem się na grę w golfa. Nie trwało długo, zanim przypomniałem sobie mojego starego przyjaciela $\. Po printwydaniu zestawienia jest $\on automatycznie dołączany na końcu danych wyjściowych. Byłem w stanie pozbyć się niewygodnej $a[$_]konstrukcji, aby uzyskać dwubajtową poprawę:

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

Znacznie lepiej, ale $\=!print$"wciąż wyglądało to trochę gadatliwie. Potem przypomniałem sobie znalezioną przeze mnie alternatywną formułę równej długości, która nie zawierała liczby 3w żadnej z cyfr. Dlatego powinno być możliwe użycie $\=2+printzamiast tego i zastąpienie wynikowej 3spacją:

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

Również 67 bajtów, ze względu na niezbędną spację między printi for.

Edycja : Można to poprawić o jeden bajt, przesuwając printdo przodu:

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

Ponieważ podstawienie musi zostać całkowicie ocenione przed print, przypisanie do $\będzie nadal występować jako ostatnie. Usunięcie białych znaków między gei forspowoduje wyświetlenie ostrzeżenia o wycofaniu, ale w innym przypadku jest poprawne.

Ale gdyby istniała formuła, która 1nigdzie nie była używana , $\=2+printstaje się $\=printwarta kolejne dwa bajty oszczędności. Nawet gdyby był o jeden bajt dłużej, nadal byłaby to poprawa.

Jak się okazuje, taka formuła istnieje, ale jest o jeden bajt dłuższa niż oryginał, co daje końcowy wynik 65 bajtów :

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

Metodologia

Zadano pytanie, w jaki sposób można znaleźć taką formułę. Ogólnie rzecz biorąc, znalezienie magicznej formuły uogólniającej dowolny zestaw danych jest kwestią prawdopodobieństwa. Oznacza to, że chcesz wybrać formę, która najprawdopodobniej da coś podobnego do pożądanego rezultatu.

Sprawdzanie pierwszych kilku cyfr rzymskich:

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

należy zauważyć pewną prawidłowość. W szczególności, od 0-3, a następnie ponownie od 5-8 , każdy kolejny element zwiększa długość o jedną cyfrę. Gdybyśmy chcieli utworzyć odwzorowanie z cyfr na cyfry, chcielibyśmy mieć wyrażenie, które również zwiększa długość o jedną cyfrę dla każdego kolejnego terminu. Logicznym wyborem jest k • 10 d, gdzie d jest odpowiednią cyfrą, a k jest dowolną stałą całkowitą.

Działa to dla 0-3 , ale 4 musi przerwać wzór. Możemy tutaj zastosować hals na module:
k • 10 d % m , gdzie m jest gdzieś pomiędzy k • 10 3 i k • 10 4 . Spowoduje to pozostawienie zakresu 0-3 nietkniętego i zmodyfikuje 4 tak, aby nie zawierał czterech Is. Jeśli dodatkowo ograniczymy nasz algorytm wyszukiwania tak, że reszta modułowa 5 , nazwijmy to j , jest mniejsza niż m / 1000 , zapewni to, że również będziemy mieć regularność od 5-8 . Rezultat jest mniej więcej taki:

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

Jak widać, jeśli zastąpimy 0z I, 0-3 i 5-8 są gwarantowane być odwzorowane prawidłowo! Wartości 4 i 9 muszą być jednak brutalnie wymuszone. W szczególności 4 musi zawierać jeden 0i jeden j(w tej kolejności), a 9 musi zawierać jeden 0, a następnie kolejną cyfrę, która nie pojawia się nigdzie indziej. Z pewnością istnieje wiele innych formuł, które przez pewien przypadek mogą przynieść pożądany rezultat. Niektóre z nich mogą być nawet krótsze. Ale nie sądzę, aby były takie, które odniosą tak duży sukces jak ten.

Eksperymentowałem również z wieloma zamiennikami Ii / lub Vz pewnym sukcesem. Ale niestety, nie mniej niż to, co już miałem. Oto lista najkrótszych rozwiązań, jakie znalazłem (liczba rozwiązań o 1-2 bajty większa jest za duża, aby je wymienić):

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
źródło
3
Jak znalazłeś magiczną formułę?
Ruben Verborgh,
1
@RubenVerborgh Niedługo zaktualizuję mój post z dodatkowymi informacjami na temat metodologii.
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​}

Wydajność

Lista numerowana z cyframi rzymskimi

...

Demo na JSBin

Patrick Oscity
źródło
1
81-bajtowa wersja tylko dla JS: document.write('<ol>'+"<li style='list-style:upper-roman'/>".repeat(100)+'</ol>')(ES6)
Paperjam,
lub 66 w Chromedocument.write("<li style='list-style:upper-roman'/>".repeat(100))
Slai
10

Python 116

lepszy golfowy kod odpowiedzi scleavera:

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
źródło
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'
scleaver
źródło
6

C, 177 160 147 znaków

Istnieją krótsze rozwiązania, ale żadne w C, więc oto moja próba.

Nowe rozwiązanie, zupełnie inne od mojego poprzedniego:

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

Poprzednie rozwiązanie (160 znaków):

Logika:
1. fwypisuje liczbę od 1 do 10. cużyte cyfry, które mogą być IVXlub XLC. Raz nazywany dziesiątkami raz.
2. Jeśli n%5==0- nic nie drukuj lub c[n/5]który jest Ilub V(lub Llub C).
3. Jeśli n%4==4- 4lub 9- wydrukuj I(lub X), przez n+1.
4. Jeśli n>4- wydrukuj 5(tj. VLub L), to n-5.
5. Jeśli n<4- wydrukuj Iwtedy n-1(tj. nRazy 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
źródło
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

Zainspirowany dłuższą wersją natknąłem się na polską grupę dyskusyjną (przynajmniej Chrome myślał, że to polska).

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
źródło
5

Q ( 81 80)

2. cięcie:

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

1. cięcie:

1_,/'[$:[``X`XX`XXX`XL`L`LX`LXX`LXXX`XC cross``I`II`III`IV`V`VI`VII`VIII`IX]],"C"
scottstein37
źródło
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]),

Wyjaśnienie

Korzystając z tych wartości, weź największą wartość nie większą niż n i odejmij ją od n. Powtarzaj, aż n wynosi 0.

'C'  = 100
'XC' = 90
'L'  = 50
'XL' = 40
'X'  = 10
'IX' = 9
'V'  = 5
'IV' = 4
'I'  = 1
pudełko kartonowe
źródło
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""zapisuje dwie postacie. W przeciwnym razie bardzo miło.
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}

To dosłownie liczy się od 1 do 100 cyframi rzymskimi. Zaczyna się od pustego ciągu, a następnie wykonuje pętlę przez dopisywanie „I”, a następnie wielokrotnie stosuje serię reguł podstawiania, skutecznie dodając 1.

Edycja: Dodano numer wersji, ponieważ ?Idziała tylko w wersji 1.9, i wykorzystał zmiany @ Howarda do przycięcia niektórych postaci.

histocrat
źródło
Możesz zapisać dwa znaki: r while-> 0while, r.sub!(*q)-> r.sub! *q. Możesz także przeciągnąć wydruk w pętli i użyć 100.times{...}zamiast instrukcji map.
Howard,
(%w[IIII VIV XXXX LXL]<</(.)((?!\1)[^I])\1/).zip(%w(IV IX XL XC)<<'\2')zapisuje 7 znaków.
steenslag,
4

Ruby 112 znaków

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}

Zasadniczo za pomocą to_romanmetody opisanej tutaj , ale za pomocą zwięzłej tablicy dla zwięzłości.

steenslag
źródło
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}]

cyfry rzymskie


Wbudowany w roztworze : IntegerString38 znaków

IntegerString[k, "Roman"]~Table~{k, 100}
DavidC
źródło
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;

Gra w golfa:

@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
źródło
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 " "

Ten sam algorytm jak @ cardboard_box, od którego wziąłem wyjaśnienie dosłownie -

Wyjaśnienie

Korzystając z tych wartości, weź największą wartość nie większą niż n i odejmij ją od n. Powtarzaj, aż n wynosi 0.

'C'  = 100
'XC' = 90
'L'  = 50
'XL' = 40
'X'  = 10
'IX' = 9
'V'  = 5
'IV' = 4
'I'  = 1
psr
źródło
2

R , 85 bajtów

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

Wypróbuj online!

Używa losowej utilszmiennej pakietowej, .romansaby uzyskać wartości liczb rzymskich, ale sama dokonuje konwersji; wbudowane podejście miałoby 20 bajtów:cat(as.roman(1:100))

Giuseppe
źródło
O dziwo, wbudowane podejście, o którym wspominasz, nie działa tak, jak jest ... trzeba wpisać cat(paste(as.roman(1:100)))lub po prostu as.roman(1:100). Dziwne.
JayCe
@JayCe odd; Nie musiałem go tak naprawdę testować ... Dokumenty catwskazują, że wykonuje mniejszą konwersję printi działa tylko na atomicwektorach - więc to wyjaśnia!
Giuseppe
1

APL 128

Próbowałem rozwiązania indeksowania w 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              

Może być o 4 bajty krótszy na początku indeksu 0 zamiast 1, ale prawdziwym kosmicznym świnią jest generowanie macierzy indeksów poprzez:

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

Jak dotąd nie byłem w stanie generować wskaźników w locie!

Graham
źródło
1

LaTeX (138)

\documentclass{minimal}
\usepackage{forloop}
\begin{document}
\newcounter{i}
\forloop{i}{1}{\value{i} < 101}{\roman{i}\par}
\end{document}
Patrick Oscity
źródło
1
-1: pytanie brzmi: „Nie możesz użyć żadnej wbudowanej funkcji do przekształcenia cyfr rzymskich”
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
źródło
1

PHP, 38 37 bajtów

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

-1 bajt dzięki @manatwork

Ten sam pomysł, co odpowiedź Patryka , ale w bardziej zwartym języku. Bije Mathematica !

Wypróbuj online!

geokavel
źródło
Zakończyć instrukcję za pomocą ;, a następnie nie ma potrzeby ?>.
manatwork
1

VBA (Excel), 245 bajtów

utworzono funkcję powtarzania i zamiany - 91 bajtów

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

za pomocą bezpośredniego okna ( 154 bajtów )

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
źródło
0

Java (OpenJDK 8) , 152 bajty

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

Wypróbuj online!

Wyjaśnienie:

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
źródło
0

TeX, 354 bajty

\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

Kilka wyjaśnień: TeX zapewnia wbudowane polecenie \romannumeraldo konwersji liczb na cyfry rzymskie. Ponieważ pytanie nie pozwala na użycie wbudowanych funkcji, powyższy kod jest golfową wersją tego samego algorytmu, z którego korzysta oryginalny kompilator TeXa Knutha \romannumeral(patrz TeX: Program , § 69 print_roman_int) ponownie zaimplementowany w TeX.

Ponieważ chce pozostawić czytelnikowi radość z zastanawiania się, jak ten kod działa, Knuth odmawia wyjaśnienia tej części kodu. Więc pójdę za tym i dam po prostu nie zmienioną i nieco zmodyfikowaną wersję, która jest bliższa oryginałowi niż powyższy kod:

\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
źródło