4
\$\begingroup\$

A string of digits (positive integer) can be converted to plain English in various ways. For instance, 115 can be read as "hundredfifteen", "oneonefive", "elevenfive", "twoonesfive". So question's challenge is to output the shortest string representation for a given digits string.

Rules:

  • The input string can start with a zero and the output should account for that.
  • Maximum length of input is 18. So you deal with numbers less than a Quintillion.
  • You should use only the following mathematical words for numbers in your output: zero, one, ..., nine, ten, eleven, ..., twenty, ..., ninety, hundred, million, billion, trillion. You can use their plural forms for sequences (i.e. "fivetens" for 1010101010). No synonyms (i.e. "oh" for 0) or slang (i.e. legs for 11) are allowed. You don't need to include a "one" prefix for cases like 100, just "hundred" is fine.
  • You don't need to put spaces between different words in your output.
  • The output shouldn't be ambigous. This means, from the output string one should be able to determine the input unambigously. So for 705 you can't output "seventyfive" since it may be the output of 75 also. You may use "and" if that'll remove ambiguity, otherwise you don't need to include "and" in your output.
  • Winner is the entry that will have the shortest code length excluding import statements and if not some user finds out a deficit in the code (i.e. a shorter representation than the program spits out for a particular input ).
  • Please post your output for the following test cases to make evaluation easier: 1234, 00505, 122333, 565577555555666888, 1000010, 10001000, 10101010.
  • No reading from an input file, so usage of a dictionary or hash map should count towards your code length.

edit1: Changed the competition type to code-golf per comments and added test cases.

edit2: Restricted the words that can be used in the output. Thanks to @PeterTaylor for the examples.

\$\endgroup\$
10
  • 6
    \$\begingroup\$ Could you put a hard limit on the input numbers? Or do I have to support "Septendecillion"? Furthermore, I think with a slightly more rigid spec this would be a great code-golf question. I don't really see why it has to popularity-contest. The current spec is already too tight to leave a lot of room for the kind of creativity that is rewarded by popularity contests. \$\endgroup\$ Commented May 30, 2014 at 22:32
  • \$\begingroup\$ @m.buettner, do you think changing the criteria to "output longest string" will enable more creativity and make it more suitable to popularity-contest? Secondly, can you elaborate on how we can make the spec more rigid to make it a good code-golf question? Having a limit makes sense for inputs like 1000000 so I'll add it. Thanks for pointing. \$\endgroup\$
    – pembeci
    Commented May 31, 2014 at 1:37
  • 1
    \$\begingroup\$ I think this would work best as a code challenge. You have a secret list of 5000 numbers, after a week you reveal the numbers and whichever program represents them with the least total characters wins. \$\endgroup\$
    – Hovercouch
    Commented May 31, 2014 at 3:29
  • 3
    \$\begingroup\$ Why isn't "18403" the shortest way to say the number 18403? \$\endgroup\$
    – Glen O
    Commented May 31, 2014 at 11:15
  • 1
    \$\begingroup\$ The spec is too imprecise as it stands, so it just sets up lots of arguments as to whether a string represents the given number or not. \$\endgroup\$ Commented May 31, 2014 at 11:36

2 Answers 2

7
\$\begingroup\$

4

This is cheeky and a cheat. I wrote this script about a year ago based off of this comic (tad of profanity).

The idea goes like this. Given any number as a string, adding the number of characters will eventually condense to 4. Four itself is 4 letters long and thus the end of the line.

For example:

  • 10 -> 'ten'
  • 'ten' -> 3 (letters long)
  • 3 -> 'three'
  • 'three' -> 5 (letters long)
  • 5 -> 'five'
  • 'five' -> 4 (letters long)

I verified up to 100,000. Very hacky but got the job done:

to20 = [
        'zero'
        ,'one'
        ,'two'
        ,'three'
        ,'four'
        ,'five'
        ,'six'
        ,'seven'
        ,'eight'
        ,'nine'
        ,'ten'
        ,'eleven'
        ,'twelve'
        ,'thirteen'
        ,'fourteen'
        ,'fifteen'
        ,'sixteen'
        ,'seventeen'
        ,'eighteen'
        ,'nineteen'
        ]

by10s = ['twenty'
         ,'thirty'
         ,'forty'
         ,'fifty'
         ,'sixty'
         ,'seventy'
         ,'eighty'
         ,'ninety'
         ]

by10powers = [
             'hundred'
             ,'thousand'
             ]

def cosmetize(number):

    # the number is too damn high!
    if number >= 100000:
        return "Too high"

    numstr = str(number)

    # Do ones
    if number < 10:
        return to20[number]

    # Pshh.. who needs zero anymore?
    to20[0] = ''

    # Do tens
    numstr10 = numstr[-2:]
    if int(numstr10) < 20:
        numstr10 = to20[int(numstr10)]
    else:
        numstr10 = by10s[int(numstr10[0])-2] + to20[int(numstr10[1])]
    if number < 100:
        return numstr10

    # Do hundreds
    numstr100 = numstr[-3]
    if numstr10 == '':
        numstr10 = "" 
        numstr100 = to20[int(numstr100)] + by10powers[0]
    elif int(numstr[-2:]) > 19:
        numstr100 = to20[int(numstr100)] + by10powers[0]
        numstr100 += 'and'
    else:
        numstr100 = 'and'
    if number < 1000:
        return numstr100 + numstr10

    # Do thousands
    numstr1000 = numstr[:-3]
    if numstr100 == 'hundred':
        numstr100 = ""
    if int(numstr1000) < 20:
        numstr1000 = to20[int(numstr1000)] + by10powers[1]
    else:
        numstr1000 = by10s[int(numstr1000[0])-2] + by10powers[1]

    return numstr1000 + numstr100 + numstr10


i = 0
mx = []
z = 0
while i < 100000:
    length = cosmetize(i)
    x = [length]
    while not len(length) == 4:
        length = cosmetize(len(length))
    i += 1

# No inifinite loop? what magic is this?
print "OMG"
\$\endgroup\$
2
\$\begingroup\$

Python - (191 - 38) = 153 bytes

This code has the worst case length of 18 for input of 000000000000000000. I'm not sure if this is system dependant. If it's not, I could remove a couple characters.

from string import*
from math import*
s=digits+lowercase+uppercase
c=raw_input()
b=len(s)
a=int(c)
print'-'*(len(c)-len(c.lstrip('0')))+''.join(s[a/b**i%b]for i in range(int(log(a,b)),-1,-1))

For the last line I have to give credit to this fellow. Example outputs:

> 1234
9j
> 00505
--3Z
> 122333
6Vn
> 565577555555666888
5haShÄøŠé
> 1000010
êÌõ
> 10001000
48Šõ
> 10101010
4ewª

Disclaimer: This answer is not a serious answer and it's not trying to compete. This is taking advantage of ambiguity of the rules.

\$\endgroup\$
2
  • \$\begingroup\$ Clever one but "êÌõ" is not plain English as mentioned in the first sentence. I thought it was clear from the examples but it seems like I need to state this explicitly. \$\endgroup\$
    – pembeci
    Commented Jun 2, 2014 at 7:54
  • \$\begingroup\$ @pembeci As I said, the point of this was to show how ambiguous the rules are. \$\endgroup\$
    – seequ
    Commented Jun 2, 2014 at 8:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.