从1到100…在罗马数字中计数


29

编写一个从1到100的罗马数字计数程序,并通过标准输出打印这些数字。每个数字必须用空格分隔。

您不能使用任何内置函数来转换为罗马数字,也不能使用外部应用程序或库来进行转换。

理想的结果是

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

因为这是一场高尔夫挑战赛,所以最短的比赛胜出


4
39缺少一个X.
托尔

@Thor固定,谢谢;)
Averroes

1
我真的很想使用INTERCAL。

可以用换行符分隔吗?还有尾随/前导空格/换行符呢?
FantaC

Answers:


68

Perl 69字节

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

通过魔术公式起作用。该表达式"32e$&"%72726以以下方式转换每个数字:
0⇒32、1⇒320、2⇒3200、3⇒32000、4⇒29096、5⇒56、6⇒560、7⇒5600、8⇒56000、9⇒50918

将翻译后y/016/IXV/,我们有这个:
0⇒32,1⇒32 ,2⇒32 II,3⇒32 III,4⇒29 9 V,5⇒5 V,6⇒5 VI,7⇒5 VII,8⇒5 VIII,9⇒5 9 X 8

其余的数字(2-57-9)将被删除。请注意,可以使用转换012而不是016简化/XVI60-9/为的公式将其提高一个字节/XVI0-9/。我找不到一个,但也许您会走运。

一旦以这种方式转换了一个数字,则对下一个数字重复该过程,将结果附加,然后将前一个XVIs CLX转换为新数字的转换。

更新
穷举搜索没有发现更短的内容。但是,我确实找到了替代的69字节解决方案:

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

这个用0-2代替IXV,但模数长一位。


更新:66 65字节

这个版本明显不同,所以我可能应该说几句话。它使用的公式实际上长了一个字节!

由于无法再缩短公式,我决定打高尔夫球。不久,我就想起了我的老朋友$\。当print发出statment,$\被自动附加到输出的结尾。我能够摆脱笨拙的$a[$_]构造,从而改进了两个字节:

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

好多了,但是$\=!print$"看起来仍然有些冗长。然后,我想起了我发现的另一个等长公式,该公式3在其任何数字转换中都不包含数字。因此,应该可以使用$\=2+print代替,并将结果3替换为空格:

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

由于print和之间必须有空格,因此也是67个字节for

编辑:通过将其print移到最前面,可以提高一个字节:

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

由于替换需要在之前进行完全评估print,因此对的分配$\仍将最后进行。卸下之间的空白ge,并for会发出弃用警告。否则,是有效的。

但是,如果有一个不在1任何地方使用的公式,$\=2+print$\=print可以节省另外两个字节。即使它增加了一个字节,它仍然是一个改进。

事实证明,确实存在这样的公式,但是它比原始公式长了一个字节,最终得分为65个字节

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

方法

有人问这个问题,如何找到这样一个公式。通常,找到一个魔术公式来概括任何数据集都是概率问题。也就是说,您希望选择一种形式,以尽可能产生与期望结果相似的形式。

检查前几个罗马数字:

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

有一定规律性可以看到。具体来说,从0-3开始,然后再从5-8,每个连续项的长度增加一个数字。如果我们想创建一个从数字到数字的映射,我们希望有一个表达式,对于每个连续项,其长度增加一位。逻辑选择是k•10 d,其中d是相应的数字,而k是任何整数常数。

这适用于0-3,但4需要打破模式。我们在这里可以做的是求模数:
k•10 d%m,其中mk•10 3k•10 4之间。这将使范围0-3保持不变,并修改4使其不包含四个Is。如果我们进一步限制搜索算法,使得5的模残差(称为j)小于m / 1000,这将确保我们也具有5-8的正则性。结果是这样的:

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

如您所见,如果我们将替换0I,则可以保证0-35-8均正确映射!不过,必须强制使用49的值。具体来说,4需要包含一个0和一个j(按此顺序),而9需要包含一个和0,后跟一个在其他任何地方都没有出现的数字。当然,还有许多其他公式,巧合的话可能会产生预期的结果。其中一些甚至可能更短。但是我认为没有什么比这成功的可能性更大。

我还尝试了多种替代方法I和/或V取得了一些成功。但是可惜的是,没有什么比我已经拥有的短了。这是我找到的最短解决方案的列表(较重的1-2个字节的解决方案数量太多,无法列出):

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
您是如何找到魔术公式的?
Ruben Verborgh

1
@RubenVerborgh我将尽快更新有关该方法的更多信息。
primo 2012年

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

输出量

带有罗马数字的编号列表

...

JSBin演示


1
81字节纯JS版本:document.write('<ol>'+"<li style='list-style:upper-roman'/>".repeat(100)+'</ol>')(ES6)
Paperjam

或在Chrome中为66document.write("<li style='list-style:upper-roman'/>".repeat(100))
Slai

10

Python 116

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个字符

有更短的解决方案,但是在C中没有,所以这是我的尝试。

新的解决方案,与我以前的解决方案完全不同:

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

先前的解决方案(160个字符):

逻辑:
1. f打印从1到10 c的数字。使用的数字可以是IVXXLC。一次叫十个。
2.如果n%5==0-打印没有或c[n/5]IV(或LC)。
3.如果n%4==4- 49-打印I(或X)通过n+1
4.如果n>4打印5(即VL),然后n-5
5.如果n<4-打印I,然后n-1(即nI)。

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

受较长版本的启发,我在波兰新闻组中遇到了这个问题(至少Chrome认为它是波兰语)。

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

问(81 80)

第二次裁切:

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

第一切:

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

说明

使用这些值,取不大于n的最大值,然后从n中减去。重复直到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""保存两个字符。否则非常好。
cemper93

4

红宝石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}

从字面上看,罗马数字从1到100。以空白字符串开头,然后循环通过添加“ I”,然后重复应用一系列替换规则,有效地添加1。

编辑:添加了版本号,因为?I仅在1.9中有效,并使用@Howard的更改来修剪某些字符。


您可以保存两个字符:r while-> 0whiler.sub!(*q)-> r.sub! *q。您也可以将打印内容拖到循环中,并使用100.times{...}而不是map语句。
霍华德

(%w[IIII VIV XXXX LXL]<</(.)((?!\1)[^I])\1/).zip(%w(IV IX XL XC)<<'\2')节省7个字符。
steenslag

4

Ruby 112个字符

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}

基本上使用此处说明to_roman方法,但为了简洁起见,使用压缩数组。


4

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

罗马数字


内置解决方案IntegerString,38个字符

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;

打高尔夫球:

@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

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

与@cardboard_box相同的算法,我逐字逐句地进行了解释-

说明

使用这些值,取不大于n的最大值,然后从n中减去。重复直到n为0。

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

2

R,85字节

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

在线尝试!

使用random utilspackage变量.romans获取罗马数字的,但自行进行转换;内置方法为20个字节:cat(as.roman(1:100))


令人惊讶的是,您提到的内置方法无法按原样工作……必须输入cat(paste(as.roman(1:100)))或简单地输入as.roman(1:100)。奇怪的。
JayCe

@JayCe奇怪; 我一定没有实际测试过...文档cat表明它执行的转换少于矢量,print并且只能在atomic矢量上使用-因此可以解释这一点!
朱塞佩

1

APL 128

我在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              

索引原点0可以比其短4个字节,而不是1个字节,但实际的空间消耗是通过以下方式生成索引矩阵:

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

到目前为止,我还无法即时生成索引!



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

VBA(Excel),245个字节

创建的重复和替换功能-91字节

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

使用即时窗口(154个字节

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字节

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

在线尝试!

说明:

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个字节

\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

一些解释:TeX提供了一个内置命令,\romannumeral用于将数字转换为罗马数字。由于该问题不允许使用内置函数,因此上述代码是Knuth原始TeX编译器用于在TeX中重新实现的同一算法的通用版本\romannumeral(请参阅TeX:程序,§69 ,print_roman_int)。

由于他想让读者困惑于这段代码的工作原理,因此Knuth拒绝对这段代码进行解释。因此,我会照做,只给出一个未经修改的略有修改的版本,该版本比上面的代码更接近原始版本:

\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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.