Count from 1 to 100... in Roman Numerals
Perl 69 bytes
s;.;y/XVI60-9/CLXVIX/dfor$a[$_].="32e$&"%72726;gefor 1..100;print"@a"
Works by way of magic formula. The expression "32e$&"%72726
transforms each digit in the following manner:
0⇒32, 1⇒320, 2⇒3200, 3⇒32000, 4⇒29096, 5⇒56, 6⇒560, 7⇒5600, 8⇒56000, 9⇒50918
After applying the translation y/016/IXV/
, we have this instead:
0⇒32, 1⇒32I, 2⇒32II, 3⇒32III, 4⇒29I9V, 5⇒5V, 6⇒5VI, 7⇒5VII, 8⇒5VIII, 9⇒5I9X8
The rest of the digits (2-57-9
) are removed. Note that this could be improved by one byte by using a formula which translates 012
instead of 016
, simplifying /XVI60-9/
to /XVI0-9/
. I wasn't able to find one, but maybe you'll have better luck.
Once one digit has been transformed in this manner, the process repeats for the next digit, appending the result, and translating the previous XVI
s to CLX
at the same time the translation for the new digit occurs.
Update
Exhaustive search didn't reveal anything shorter. I did, however, find an alternative 69 byte solution:
s;.;y/XVI0-9/CLXIXV/dfor$a[$_].="57e$&"%474976;gefor 1..100;print"@a"
This one uses a 0-2
substitution for IXV
, but has a modulo that's one digit longer.
Update: 66 65 bytes
This version is notably different, so I should probably say a few words about it. The formula it uses is actually one byte longer!
Unable to shorten the formula any more than it is, I decided to golf down what I had. It wasn't long until I remembered my old friend $\
. When a print
statment is issued, $\
is automatically appended to the end of the output. I was able to get rid of the awkward $a[$_]
construction for a two byte improvement:
s;.;y/XVI60-9/CLXVIX/dfor$\.="32e$&"%72726;ge,$\=!print$"for 1..100
Much better, but that $\=!print$"
still looked a bit verbose. I then remembered an alternative, equal length formula I had found which didn't contain the number 3
in any of its digit transforms. So, it should be possible to use $\=2+print
instead, and substitute the resulting 3
with a space:
s;.;y/XVI0-9/CLXIIX V/dfor$\.="8e$&"%61535;ge,$\=2+print for 1..100
Also 67 bytes, due to the necessary whitespace between print
and for
.
Edit: This can be improved by one byte, by moving the
$\=2+print!s;.;y/XVI0-9/CLXIIX V/dfor$\.="8e$&"%61535;gefor 1..100
Because the substitution needs to evaluate completely before the
$\
will still occur last. Removing the whitespace betweenge
andfor
will issue a deprecation warning, but is otherwise valid.
But, if there were a formula which didn't use a 1
anywhere, $\=2+print
becomes $\=print
for another two bytes worth of savings. Even if it were one byte longer, it'd still be an improvement.
As it turns out, such a formula does exist, but it is one byte longer than the original, resulting in a final score of 65 bytes:
$\=print!s;.;y/XVI60-9/CLXXI V/dfor$\.="37e$&"%97366;gefor 1..100
Methodology
The question was asked how one might go about finding such a formula. In general, finding a magic formula to generalize any set of data is matter of probability. That is, you want to choose a form which is as likely as possible to produce something similar to the desired result.
Examing the first few roman numerals:
0:
1: I
2: II
3: III
4: IV
5: V
6: VI
7: VII
8: VIII
9: IX
there is some regularity to be seen. Specifically, from 0-3 and then again from 5-8, each successive term increases in length by one numeral. If we wanted to create a mapping from digits to numerals, we would want to have an expression that also increases in length by one digit for each successive term. A logical choice is k • 10d where d is the corresponding digit, and k is any integer constant.
This works for 0-3, but 4 needs to break the pattern. What we can do here is tack on a modulo:
k • 10d % m, where m is somewhere between k • 103 and k • 104. This will leave the range 0-3 untouched, and modify 4 such that it won't contain four I
s. If we additionally constrain our search algorithm such that the modular residue of 5, let's call it j, is less than m / 1000, this will ensure that we also have regularity from 5-8 as well. The result is something like this:
0: k
1: k0
2: k00
3: k000
4: ????
5: j
6: j0
7: j00
8: j000
9: ????
As you can see, if we replace 0
with I
, 0-3 and 5-8 are all guaranteed to be mapped correctly! The values for 4 and 9 need to be brute forced though. Specifically, 4 needs to contain one 0
and one j
(in that order), and 9 needs to contain one 0
, followed by one other digit that doesn't appear anywhere else. Certainly, there are a number of other formulae, which by some fluke of a coincidence might produce the desired result. Some of them may even be shorter. But I don't think there are any which are as likely to succeed as this one.
I also experimented with multiple replacements for I
and/or V
with some success. But alas, nothing shorter than what I already had. Here is a list of the shortest solutions I found (the number of solutions 1-2 bytes heavier are too many to list):
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
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}
Output
...
Demo on JSBin
Python 116
better golfed code of scleaver's answer:
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'