The 9 Billion Names of God

74

20

The 9 Billion Names of God is a short story by Arthur C. Clarke. It's about a group of Tibetan monks whose order is devoted to writing down all the possible names of God, written in their own alphabet. Essentially, they are devoted to writing every possible permutation of their alphabet, restricted by a few rules. In the story, the monastery hires some engineers to write a program to do all the work for them. Your goal is to write that program.

Rules:

  • The monk's alphabet uses 13 characters (according to my estimations). You may use ABCDEFGHIJKLM or some other set of 13 characters.

  • The minimum length of a possible name is 1 character. The maximum length is 9 characters.

  • No character may repeat more than 3 times in succession. AAABA is a valid name, but AAAAB is not.

  • Your program should print out (to a file) every possible name in sequence from A to MMMLMMMLM, separated by any character not in the alphabet (newlines, semi-colons, whatever).

  • This is code-golf, and you can use any language. The shortest solution by June 1st 2014 wins.

Edit: The names should start with A and end with MMMLMMMLM, progressing through all the billions of names sequentially. But the particular sequence is up to you. You can print out all the 1-letter names first, then all the 2-letter names, etc. Or you can print all the names starting with A, then all the ones starting with B, or some other pattern. But a human should be able to read through the file and confirm they are all there and in whatever logical order you choose, assuming they have the time.

CSturgess

Posted 2014-05-30T18:19:29.053

Reputation: 843

1

This verifies that the number of names in the present problem is indeed 11,459,252,883 (as found in edc65's C program). Implementing Ross Millikan's solution at MathSE generates the following polynomial formula for the number of names with length <= 9, for variable alphabet size k: f(k) = k^9 + k^8 + k^7 - 5*k^6 + k^5 + k^4 + 4*k^3 - 2*k^2 + k. Sage implementation: https://goo.gl/0srwhq

– r.e.s. – 2014-06-06T04:01:37.243

29Are you trying to end the universe, sir? – Boluc Papuccuoglu – 2014-06-02T11:21:47.783

8Link to the story, for anyone interested. – ThatOneGuy – 2014-06-02T19:40:00.960

"The 11,459,252,883 Names of God"? It would be cool if the number of names were independently verified. It's 11,459,252,883 according to @edc65 's C answer, but the other answers appear too slow for this. (Except by actually enumerating the names, I don't know how to obtain the number.) – r.e.s. – 2014-06-03T13:00:23.197

There's definitely a way to calculate it. Of course, in the story, the monks had more rules, lowering the number of possibilities. – CSturgess – 2014-06-03T19:57:10.007

@CSturgess: no, in the story the monks had the limit of 3 letters repeated and no more. Where Clarke wrote the story, maybe 40 years ago, he could just estimate the number of possibilities (short of calling IBM and renting some months of machine power) – edc65 – 2014-06-04T09:15:11.867

1953 if fact ... – edc65 – 2014-06-04T09:22:18.077

@edc65 No, they had more rules than that. They just said that one as an example. They never elaborated on the others. – CSturgess – 2014-06-04T14:34:37.237

@CSturgess - Certainly there's a combinatorial-style proof of the correct number of names defined by your rule-set, but we don't have such a proof at hand. (It might be a good question for Math SE.) The next best thing would be independent brute-force computations to verify that it's 11,459,252,883.

– r.e.s. – 2014-06-04T21:17:18.693

@CSturgess - sorry, going by memory, and I read that story 30 years ago. I can't believe that in the last 60 years nobody tried to verify the number. – edc65 – 2014-06-05T18:22:06.653

1

Here it is! http://math.stackexchange.com/a/34292

– edc65 – 2014-06-05T18:34:59.923

Wait a minute, a british billion is different from an American billion? That changes everything. That would mean a 28 character alphabet instead of 13. I wonder if that would change the scope of the problem at all? – CSturgess – 2014-06-05T19:20:53.490

@edc65 - See my "answer" below, which uses a Sage implementation to generate the polynomial formula for the number of names as a function of variable alphabet size.

– r.e.s. – 2014-06-06T04:06:25.653

@edc65 - A better answer from that question is this one, I think. It points out: Everyone here seems to think that (in the story) the two hired engineers printed out a 9 billion word list. People here are trying to calculate which n-letter alphabet system gives 9 billion words. The list that the engineers printed was much longer than 9 billion. The 11,459,252,883 possibilities just included the 9 billion names.

– Bobson – 2014-06-27T15:54:05.937

I'd recommend against generating the list. According to my calculations the table is around 107GB (ignoring the 3 character repetition rule, including a newline characters after each name). – recursion.ninja – 2014-06-27T16:33:15.460

@awashburn 113,637,155,697 including the 3 characters repetion rule, newlines and no padding. I actually built the file. [http://codegolf.stackexchange.com/a/28876/21348] – edc65 – 2014-06-27T16:37:03.867

3@edc65 So 105.8GB all said and done! I'm glad the stars didn't go out... or maybe you have to print the list for that to happen...? – recursion.ninja – 2014-06-27T16:53:30.827

Answers

34

Ruby, 46

?A.upto(?M*9){|s|s[/(.)\1{3}|[N-Z]/]||puts(s)}

My original, similar solution was longer and wrong (it output base13 numbers, which isn't quite all of them due to leading zeroes), but I'll leave it here because it got votes anyway.

1.upto(13**9){|i|(s=i.to_s 13)[/(.)\1{3}/]||puts(s)}

histocrat

Posted 2014-05-30T18:19:29.053

Reputation: 20 600

Should be base14 – edc65 – 2014-05-30T19:34:57.873

Yeah, it belatedly occurs to me that this omits names of God that have leading 0's. I don't think base14 works out of the box either though, since it's a 14-character alphabet. – histocrat – 2014-05-30T19:41:03.610

14Well I ran your code for about an hour and got up to 2 billion names and a 21 GB text file before seeing this and quitting it. I underestimated just how large the file would be. – CSturgess – 2014-05-30T19:49:15.913

2@CSturgess Well, Ruby isn't the fastest language for this sort of thing out there... – BenjiWiebe – 2014-05-31T13:33:50.590

8@BenjiWiebe But still faster than being handwritten by monks! – Turophile – 2014-06-01T22:24:07.173

1Accepting this one because it has more votes. – CSturgess – 2014-06-02T14:21:05.237

4Not posting this as a separate answer, as it requires an immensely huge amount of memory (~30 TB, if my calculation is correct), but in theory you can shorten this to 43 characters with k=*?A..?M*9;puts k-k.grep(/(.)\1{3}|[N-Z]/) – Ventero – 2014-06-02T19:24:41.317

Ran it to 5 characters and it looks like a new winner, tied with DigitalTrauma! Unfortunately it's past the deadline. – CSturgess – 2014-06-03T17:46:38.197

24

C 140 177 235

Good old procedural style, no fancyness.
It counts (no write) 11,459,252,883 names in 8 minutes.
Next edit with the runtime and size of names file. Watch the sky...
Runtime 57 minutes, file size 126,051,781,713 (9 chars+crlf per row). Please tell me the monks' email address, so that I can send them the zipped file, for manual check...

Edit Golfed a little more, reworked the check for repeated letters.
Still not the shortest, but at least this one terminates and generates the required output.
Runtime 51 min, file size 113,637,155,697 (no leading blanks this time)

A side note: obviously the output file is very compressible, still I had to kill 7zip, after working 36 hours it was at 70%. Weird.

char n[]="@@@@@@@@@@";p=9,q,r;main(){while(p)if(++n[p]>77)n[p--]=65;else for(r=q=p=9;r&7;)(r+=r+(n[q]!=n[q-1])),n[--q]<65&&puts(n+q+1,r=0);}

Ungolfed

char n[]="@@@@@@@@@@";
p=9,q,r;
main()
{
    while (p)
    {
        if (++n[p] > 77)
        {
            n[p--] = 65; // when max reached, set to min and move pointer to left
        }
        else 
        {
            for (r=q=p=9; r & 7 ;) // r must start as any odd number
            {
                r += r+(n[q]!=n[q-1])); // a bitmap: 1 means a difference, 000 means 4 letters equal
                n[--q] < 65 && puts(n+q+1,r=0);
            }
        }
    }
}

edc65

Posted 2014-05-30T18:19:29.053

Reputation: 31 086

no #includes? – Simon Kuang – 2014-06-01T04:33:44.890

@SimonKuang, some compilers will put basic ones (stdio) in automatically. – Paul Draper – 2014-06-01T06:50:27.100

1@Simon it's C standard. By default, global objects are int and global functions return int. Visual studio outputs C4013 warning about 'puts' not defined, but it's valid anyway. – edc65 – 2014-06-01T07:15:21.280

4fits into a tweet! – CincauHangus – 2014-06-02T09:47:05.800

19

Golfscript, 58 47 characters

"A"13
9?,{13base{65+}%n+}%{`{\4*/,}+78,1/%1-!},

Thanks to Peter Taylor, I am spared from the seppuku from not beating the Ruby solution! Run the code up to 10 yourself, and here is proof it skips the four-in-a-row numbers.

Claudiu

Posted 2014-05-30T18:19:29.053

Reputation: 3 870

1Easy saving: use n+ instead of ''+n. I think it's within the rules to use an alphabet with control characters, so you could also replace 65+ with 13+ and save another character by naming 13:^. And I think that 13,{ stuff [...] could be 13,1/{ stuff 4*. – Peter Taylor – 2014-05-30T19:35:19.963

1My initial thought was that the saving would be via a filter, and with a bit of work it can be done. From 13, on can be replaced with {65+}%n+}%{ backtick {\4*/,}+78,1/%1-!}, for a total saving of 8, saving your life. – Peter Taylor – 2014-05-30T19:46:02.920

As long as the character is something that you can physically see, it will work. Really you could even include newlines as a character. Just so long as there's a sequence to the characters. – CSturgess – 2014-05-30T19:52:51.637

@PeterTaylor: You are a gentleman and a scholar! – Claudiu – 2014-05-30T20:07:56.433

After AAAM it should be AAABA, and not BAAAB, right? – justhalf – 2014-06-02T04:08:16.707

18

Bash+Linux command line utils, 43 bytes

jot -w%x $[16**9]|egrep -v "[0ef]|(.)\1{3}"

This uses a similar technique to my answer below, but just counts in base 16, and strips out all "names" containing 0, e or f as well those with more than 3 same consecutive digits.

Convert to the monk's alphabet as follows:

jot -w%x $[16**9]|egrep -v "[0ef]|(.)\1{3}" | tr 1-9a-d A-M

Bash+coreutils (dc and egrep), 46 bytes

Edit - corrected version

dc<<<Edo9^[p1-d0\<m]dsmx|egrep -v "0|(.)\1{3}"

This'll take a while to run but I think its correct.

dc counts downwards from 14^9 to 1 and outputs in base 14. egrep filters out the numbers with more than 3 consecutive same digits. We also filter out any names with "0" digits, so we get the correct set of letters in the names.

The question specifies that any alphabet may be used, so I am using [1-9][A-D]. But for testing, this can be transformed to [A-M] using tr:

dc<<<Edo9^[p1-d0\<m]dsmx|egrep -v "0|(.)\1{3}" | tr 1-9A-D A-M

This yields the sequence:

MMMLMMMLM MMMLMMMLL MMMLMMMLK ... AC AB AA M L K ... C B A

Note this dc command requires tail recursion to work. This works on dc version 1.3.95 (Ubuntu 12.04) but not 1.3 (OSX Mavericks).

Digital Trauma

Posted 2014-05-30T18:19:29.053

Reputation: 64 644

10

APL (59)

↑Z/⍨{~∨/,↑⍷∘⍵¨4/¨⎕A[⍳13]}¨Z←⊃,/{↓⍉⎕A[1+(⍵/13)⊤¯1⌽⍳13*⍵]}¨⍳9

Written in its own alphabet :) It's a bit long. It also takes a long time to run with 9, try it with a lower number to test if you want.

Explanation:

  • {...}¨⍳9: for each number from 1 to 9:
    • ⍳13*⍵: get all numbers from 1 to 13^⍵
    • ¯1⌽: rotate the list to the left by 1 (so we have 13^⍵, 1, 2, ..., 13^⍵-1, which turns into 0, 1, 2 ... modulo 13^⍵).
    • (⍵/13)⊤: encode each number in base 13 using digits
    • ⎕A[1+...]: add one (arrays are 1-indexed) and look up in ⎕A (the alphabet)
    • ↓⍉: turn the matrix into a vector of vectors along the columns.
  • Z←⊃,/: join each inner vector of vectors together, giving us a list of possible names (but it doesn't meet the rules yet).
  • {...: for each name, test if it meets the 4-repeated-chars rule:
    • 4/¨⎕A[⍳13]: for each character, generate a string of 4 of that character
    • ⍷∘⍵¨: for each string, test if it is present in
    • ∨/,↑: take the logical or of all these tests,
    • ~: and invert it, so that 1 means that it meets the rules and 0 means it doesn't.
  • Z/⍨: select from Z all the elements that meet the ruels
  • : display each one on a separate line

marinus

Posted 2014-05-30T18:19:29.053

Reputation: 30 224

9I'm disappointed. Given its reputation, you'd think APL would have a one-character solution for this, that no keyboard could type. – Mark – 2014-06-01T05:32:29.217

7@Mark, I am certain that APL does have that, but no one knows what the character is :) – Paul Draper – 2014-06-01T06:49:35.753

1one should write this onto a stone, and when future humans find this, they might just think it's just primitive written language. – CincauHangus – 2014-06-02T09:51:56.537

9

Perl, 70 68 66 50 characters

$"=",";map/(.)\1{3}/||say,glob$i.="{@a}"for@a=A..M

Usage:

$ perl -E 'code' > output_file

The nice thing is that the prints are buffered, so you get all 1-character solutions printed first, followed by 2-character words and so on.

Zaid

Posted 2014-05-30T18:19:29.053

Reputation: 1 015

The best thing about this solution is the boob to the left of the 1. – Dan Hanly – 2014-06-28T13:02:08.757

8

Perl - 35 bytes

#!perl -l
/(.)\1{3}|[N-Z]/||print for A..1x9

Counting the shebang as one byte.

This is a loose translation of histocrat's answer.

A..1x9 is a bit of an oddity; this is shorthand for 'A'..'111111111'. The accumulator will never actually reach the terminal value (it contains only upper-case letters), but it will still terminate once it becomes longer than 9 characters long. This can be tested, for example, by using 1x4 instead.

primo

Posted 2014-05-30T18:19:29.053

Reputation: 30 891

Respect! Now why didn't I think of that? ;) – Zaid – 2014-06-06T10:07:50.520

Note that Ruby doesn't have to create the entire range in order to iterate it, either. The only reason the code in my comment requires such a huge amount of memory is that it turns the range into an Array (so that it can use Array#-). – Ventero – 2014-06-06T16:01:29.910

@Ventero Ahh yes, grep will do that. I'm not entirely fluent in Ruby. – primo – 2014-06-06T16:40:06.230

6

Pyg (Waaay too long, for a language made for golfing)

whispers: 101...

Pe(*ItCh(((J(x)for x in ItPr("ABCDEFGHIJKLM",repeat=j)if not An((i*3 in x)for i in x))for j in R(14))))

Even though this is close to how I would actually do it in Python:

from itertools import *
for i in range(14):
    for j in ("".join(k) for k in product("ABCDEFGHIJKLM",repeat=i) if not any((i*3 in k) for i in k)):
        print j

Minus the long line complication of course ;)

ɐɔıʇǝɥʇuʎs

Posted 2014-05-30T18:19:29.053

Reputation: 4 449

3

Pyth, 34 characters

Kf<T"n"GJKFbJI>lb9Bb~Jm+bdfXVb*Y3K

Explanation:

Kf<T"n"G        K = list of letters in the alphabet before n.
JK              J = copy of K
FbJ             For b in J:
I>lb9B          If length of b > 9: break
b               print(b)
~J              J+=
~Jm+bd          J+=map(lambda d:b+d,
       XVb*Y3   index of Y*3 in reversed(b)
      fXVb*Y3K  filter for non-zero for Y in K on function index of Y*3 in reversed(b)
~Jm+bdfXVb*Y3K  J+=map(lambda d:b+d, filter(lambda Y:index of Y*3 in reversed(b), K))

isaacg

Posted 2014-05-30T18:19:29.053

Reputation: 39 268

2

Python 2 - 212 bytes

from itertools import chain,product as p
a='ABCDEFGHIJKLM'
q={c*4 for c in a}
c=0
for n in chain(*(p(*([a]*l)) for l in range(1,10))):
 n=''.join(n)
 if not any(u in n for u in q):print n
 c+=1
 if c==10**9:break

Zac Crites

Posted 2014-05-30T18:19:29.053

Reputation: 201

0

Japt, 21 bytes

Ep9 osE kè/0|(.)\1{3}

Try it online! (the link only computes up to 14**4.)

How it works

Ep9 osE kè/0|(.)\1{3}/

Ep9  14**9
osE  Range from 0 to n (exclusive), mapped through base-14 conversion
kè   Remove elements that match at least once...
/0|(.)\1{3}/  the regex that matches a zero or four times same char.

Assumes a standard ECMAScript 2017 implementation as the JS layer (and enough memory to store the array), where an Array object can have maximum of 2**53-1 length.

Bubbler

Posted 2014-05-30T18:19:29.053

Reputation: 16 616