24
\$\begingroup\$

The Animal-Alphabetical Sequence is an infinite string of letters built accordingly to the following procedure:

  1. Start with the letter A;

  2. Replace each letter with the name of the animal starting with such letter in the table below;

  3. Go back to step 2.

For instance, the first four steps of the procedure give:

  • A

  • ADDAX

  • ADDAXDINGODINGOADDAXXERUS

  • ADDAXDINGODINGOADDAXXERUSDINGOINDRINYALAGECKOOTTERDINGOINDRINYALAGECKOOTTERADDAXDINGODINGOADDAXXERUSXERUSEAGLEROBINURIALSQUID

Note that the string obtained at each step is a prefix of the string obtained at the next step. Hence, the procedure does indeed converge to a well-defined infinite string:

ADDAXDINGODINGOADDAXXERUSDINGOIND...

The Challenge

Write a function that takes as input an integer n in the range [0, 2^31 - 1] and returns as output the n-th letter of the Animal-Alphabetical Sequence.

Notes

  • The first letter is the 0-th.

  • Letters can be uppercase or lowercase.

  • It must be possible to run the program in the Try It Online interpreter and get the result in at most 5 minutes.

Test Cases

1511763812 -> M
1603218999 -> I
2049234744 -> X
2060411875 -> K
2147483647 -> D

Table of Animal Names

ADDAX
BISON
CAMEL
DINGO
EAGLE
FOSSA
GECKO
HORSE
INDRI
JAMBU
KOALA
LEMUR
MOUSE
NYALA
OTTER
PRAWN
QUAIL
ROBIN
SQUID
TIGER
URIAL
VIXEN
WHALE
XERUS
YAPOK
ZEBRA
\$\endgroup\$
9
  • 7
    \$\begingroup\$ Note that the 3rd rule can't be enforced on TIO, which times out at 1 minute. \$\endgroup\$
    – Arnauld
    Dec 18 '20 at 10:57
  • 11
    \$\begingroup\$ Small golfing tip for anyone doing this challenge: FOSSA, JAMBU, VIXEN, and ZEBRA can be ignored, because none of the other animal names contain these letters and it always starts with A. (And as mentioned by @Arnauld, TIO always times out after 60 seconds, so is the time limit we'll have to work with 60 seconds or 300 seconds?) \$\endgroup\$ Dec 18 '20 at 11:04
  • 3
    \$\begingroup\$ Instead of indexing can we just return the string? Some languages (Haskell) allow for infinite strings. \$\endgroup\$
    – Wheat Wizard
    Dec 18 '20 at 11:18
  • 5
    \$\begingroup\$ "The first letter is the 0-th." - it's probably best to just allow 1-indexing (where the first letter is the 1st, like "first" suggests :)). \$\endgroup\$ Dec 18 '20 at 12:53
  • 5
    \$\begingroup\$ "It must be possible to run the program in the Try It Online interpreter and get the result in at most 5 minutes." - TIO times out at 1 minute. \$\endgroup\$ Dec 18 '20 at 12:54
4
\$\begingroup\$

Charcoal, 83 bytes

≔AηF↨N⁵≔§⁺η§⪪”$⌊∧X;F‖ρ=JD θ⊘⊙~'ΣV⦄K◨|]≕‖◨⟧TJρ¿¿C´!⁸⁶S/U¶V×W⧴a“,=6;‴*Þ↓h!�{≦”⁴⌕αηιηη

Try it online! Link is to verbose version of code. Explanation:

≔Aη

Start with A at the 0th index.

F↨N⁵

Input n, convert it to base 5 and loop over the digits.

≔§⁺η§⪪”...”⁴⌕αηιη

Get the current animal from the compressed string of all animal suffixes (excluding EBRA) and lookup the appropriate letter from the current digit.

η

Output the final letter.

\$\endgroup\$
3
\$\begingroup\$

JavaScript (Node.js), 168 bytes

n=>[...n.toString(5)].reduce((c,k)=>'DIAIA-EON-OEOYTRUOQIR-HEADSMNG-CRD-AMUATAABUGI-ARPAOEGL-KSR-LUSLEWIIIEA-LUOXNLOE-OEI-AREARNLNDRL-ESK'[Buffer(c)[0]-90+k*25]||c,'A')

Try it online!

How?

The lookup string (let's call it S) is built as 4 sub-strings of 25 characters as follows:

ABCDEFGHIJKLMNOPQRSTUVWXY
-------------------------
DIAIA-EON-OEOYTRUOQIR-HEA  --> 2nd letter of each word
DSMNG-CRD-AMUATAABUGI-ARP  --> 3rd letter of each word
AOEGL-KSR-LUSLEWIIIEA-LUO  --> 4th letter of each word
XNLOE-OEI-AREARNLNDRL-ESK  --> 5th letter of each word

Therefore:

S[Buffer(c)[0] - 90 + k * 25] // equivalent to S[Buffer(c)[0] - 65 + (k - 1) * 25]

is undefined if k = 0, in which case we leave c unchanged as expected (the first letter of the word that starts with c is c).

\$\endgroup\$
3
\$\begingroup\$

Haskell, 170 162 bytes

Thanks to AZTECCO for helping me index the lookup shorter

f 0='A'
f n|x<-f$div n 5=(x:drop(4*length['B'..x])"DDAXISONAMELINGOAGLEOSSAECKOORSENDRIAMBUOALAEMUROUSEYALATTERRAWNUAILOBINQUIDIGERRIALIXENHALEERUSAPOK")!!mod n 5

Try it online!

This exceeds TIO's output capacity nearly instantly, so I think this is safe on timing. My quick analysis tells me the algorithm should be \$O(n)\$. That is if you input a number of length \$n\$ bits it should take about the \$n\$ time to find the answer.

I'm not the best at golfing data encoding so I think that is where I am losing the most bytes.

This gets a lot easier to read if we just say we have a function u which gives the word for a letter. Then it is:

f 0='A'
f n=u(f$div n 5)!!mod n 5
\$\endgroup\$
5
  • \$\begingroup\$ Can't you get rid of 'Y' in ['A'..'Y'] ? \$\endgroup\$
    – AZTECCO
    Dec 18 '20 at 13:23
  • \$\begingroup\$ @AZTECCO I think it would theoretically work however it needs to iterate through all of the characters which I think is quite a big number so it times out on TIO. \$\endgroup\$
    – Wheat Wizard
    Dec 18 '20 at 13:40
  • 1
    \$\begingroup\$ Oh, it worked for small numbers, anyway you can use 4*length['B'..x] instead of sum[4|l<-['A'..'Y'],l<x] \$\endgroup\$
    – AZTECCO
    Dec 18 '20 at 14:57
  • \$\begingroup\$ How can the complexity be so low? The solutions based on base-5 representation have complexity O(log(n)). \$\endgroup\$
    – Jyenas
    Dec 18 '20 at 16:54
  • 1
    \$\begingroup\$ @Jyenas You are right in a way, this is O(n). This is also base 5 representation and I suspect they are also O(n) as well. This is because with n bits the largest number that can be represented is (2^n)-1, and since each number recurrs with 1/5 it's size the result is the logarithm of that. O(log_5(2^n)) is equivalent by base conversion to O(n). \$\endgroup\$
    – Wheat Wizard
    Dec 18 '20 at 17:46
2
\$\begingroup\$

05AB1E, 78 77 bytes

'aI5вvA…fjvмSD.•13ôºå}Æ ë¾‚pu®×ÌмĀαù·o×#·Îg4™Ā8%+ÚONp:rƒÜ∊₁Œ¿ÝøuÏādûá•4ôøJ‡yè

-1 byte thanks to @Arnauld.

Output in lowercase.

Try it online or verify all test cases.

Explanation:

'a             '# Push string "a"
  I             # Push the input-integer
   5в           # Convert it to base-5 as list
     v          # Loop `y` over each digit in this list:
      A         #  Push the lowercase alphabet
       …fjvм    #  Remove the letters 'f', 'j', and 'v'
            S   #  Convert it to a list of characters
             D  #  Duplicate this list
      .•13ôºå}Æ ë¾‚pu®×ÌмĀαù·o×#·Îg4™Ā8%+ÚONp:rƒÜ∊₁Œ¿ÝøuÏādûá•
                #  Push compressed string "ddaxisonamelingoagleeckoorsendrioalaemurouseyalatterrawnuailobinquidigerrialhaleerusapok"
        4ô      #  Split the string into parts of 4 characters
          ø     #  Create pairs with the alphabet-list (the trailing 'z' is ignored)
           J    #  Join each pair together to a single 5-char string
            ‡   #  Transliterate the current character to the animal name 
                #  (again ignoring the trailing 'z')
             yè #  And index digit `y` into this string for the next iteration
                # (after the loop, the resulting character is output implicitly)

See this 05AB1E tip of mine (section How to compress strings not part of the dictionary?) to understand why .•13ôºå}Æ ë¾‚pu®×ÌмĀαù·o×#·Îg4™Ā8%+ÚONp:rƒÜ∊₁Œ¿ÝøuÏādûá• is "ddaxisonamelingoagleeckoorsendrioalaemurouseyalatterrawnuailobinquidigerrialhaleerusapok".

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Do you really need to remove the 'z'? \$\endgroup\$
    – Arnauld
    Dec 18 '20 at 12:49
  • \$\begingroup\$ @Arnauld Ah, good point, thanks for -1! :) The ø will simply ignore the trailing parts of unequal length lists, so the 'z' is simply removed, and the will ignore that trailing 'z' as well. \$\endgroup\$ Dec 18 '20 at 13:16
2
\$\begingroup\$

Perl 5, 175 bytes

sub f{my$n=pop;$_=substr$n?f(1,$n/5):ADDAX,$n%5,1;@_?$_.[DDAXISONAMELINGOAGLE_ECKOORSENDRI_OALAEMUROUSEYALATTERRAWNUAILOBINQUIDIGERRIAL_HALEERUSAPOK=~/_|.{4}/g]->[-65+ord]:$_}

Try it online!

Somewhat ungolfed. Added whitespace and moved dictionary string into variable $animals:

sub f {
  $animals='DDAXISONAMELINGOAGLE_ECKOORSENDRI_OALAEMUROUSE'
          .'YALATTERRAWNUAILOBINQUIDIGERRIAL_HALEERUSAPOK';
  my $n=pop;                          #arg
  $_=substr                           #put letter into $_
    $n ? f(1,$n/5) : ADDAX,           #next word is word at n/5 or first word
    $n%5,1;                           #get letter at pos n%5 of word
  @_                                  #if recursed ...
  ?$_.[$animals=~/_|.{4}/g]->[-65+ord]#then return letter + its 4 letters in dict
  :$_                                 #else return just letter
}

About runtime: on my "vintage" laptop the five test cases spends about 0.025 sec in total. If I change $n/5 into int$n/5 this is reduced to 0.0016 sec (because then it doesn't need to keep dividing by 5 until the decimals disappears, thus far fewer recursions)

\$\endgroup\$
2
\$\begingroup\$

C# (Visual C# Interactive Compiler), 254 234 230 229 228 209 199 198 bytes

string f(int v,int p=-1){var h=v>-p?f(v/5,v%5):"ADDAX";return p<0?h[..1]:h[p]+"DDAXISONAMELINGOAGLEOSSAECKOORSENDRIAMBUOALAEMUROUSEYALATTERRAWNUAILOBINQUIDIGERRIALIXENHALEERUSAPOK"[(h[p]*4-260)..];}

Try it online!

How?

char f(int v, int p = -1)
{
    var h = v > -p ? f(v / 5, v % 5) : "ADDAX";
    return p < 0 ? h[..1] : h[p] + "DDAXISONAMELINGOAGLEOSSAECKOORSENDRIAMBUOALAEMUROUSEYALATTERRAWNUAILOBINQUIDIGERRIALIXENHALEERUSAPOK"[(h[p] * 4 - 260)..];
}

This version is a complete rewrite of my original solution. Inspired from other submissions, I decided to use a recursive function. The gist is still converting v to base 5 and use the digits to point to the correct words. When v and p are both equal to 0, the conversion is complete, so we take ADDAX and stop the recursion.

Zebra was removed from the list because it is not reachable.

Updates:

  • Saved 20 bytes removing the first letter from each word in the string literal.
  • Replacing do/while with a for loop reduced the size by 4 bytes
  • -1 byte using a tuple to assign b and g
  • -1 byte replacing 'A' with 65
  • -1 byte replacing [((h[b] - 65) * 4)..] with [(h[b]*4 - 260)..]
  • -10 bytes using a recursive function
  • -1 byte replacing v+p>0 with v>-p (thanks to @ceilingcat)
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to Code Golf, nice first answer! You may want to check out our Tips for golfing in C# (although this already looks really well golfed!). \$\endgroup\$ Dec 21 '20 at 0:50
2
\$\begingroup\$

Python 3 2, 160 159 158 bytes

s="ABCDEGHIKLMNOPQRSTUWXYDIAIAEONOEOYTRUOQIRHEADSMNGCRDAMUATAABUGIARPAOEGLKSRLUSLEWIIIEALUOXNLOEOEIAREARNLNDRLESK"
f=lambda n:s[n and s.find(f(n/5))::22][n%5]

Try it online!

-1 thanks to @ovs

\$\endgroup\$
1
  • \$\begingroup\$ You can save one byte with str.find. \$\endgroup\$
    – ovs
    Jan 24 at 10:23
1
\$\begingroup\$

Julia 1.0, 153 bytes

f(n)=*('A':'Y'...,"DIAIAOEONAOEOYTRUOQIRIHEADSMNGSCRDMAMUATAABUGIXARPAOEGLSKSRBLUSLEWIIIEAELUOXNLOEAOEIUAREARNLNDRLNESK")[n<1||f(n÷5)-'@':25:end][1+n%5]

Try it online!

\$\endgroup\$
1
\$\begingroup\$

PowerShell, 185 182 179 177 174 173 Bytes

-1 byte thanks to mazzy!

for($f=[char]65;!($x=$f[$args])){$f=$f|%{,$_+"DDAXISONAMELINGOAGLEOSSAECKOORSENDRIAMBUOALAEMUROUSEYALATTERRAWNUAILOBINQUIDIGERRIALIXENHALEERUSAPOK"[($i=$_%65*4)..($i+3)]}}$x

Could probably be shortened, but I got stumped. If 0 can be ignored as an input, you can drop the [char] in [char]65 to save 4 bytes.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ nice golf. you can save 1 byte if you move the $f=$f|... to the for-body Try it online! \$\endgroup\$
    – mazzy
    Dec 19 '20 at 12:19
  • \$\begingroup\$ @mazzy thanks again! \$\endgroup\$ Dec 20 '20 at 20:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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