Retina, 56 37 bytes
This solution works with all the required input values.
The biggest problem Retina faces in this challenge is the fact that its strings have a maximum length of 2^30 characters, so the usual way of dealing with numbers (unary representation) doesn't work with values greater than 2^30.
In order to solve this problem I adopted a different approach, keeping a sort of decimal representation of numbers, but where each digit is written in unary (I'll call this representation digitunary). For example the number 341
would be written as 111#1111#1#
in digitunary. With this representation we can now work with numbers of up to 2^30/10
digits (~ a hundred million digits). It is less practical than standard unary for arbitrary arithmetic, but with a bit of effort we could do any kind of operations.
NOTE: digitunary in theory could use any other base (e.g. binary 110
would be 1#1##
in base 2 digitunary), but since Retina has builtins to convert between decimal and unary and no direct way to deal with other bases, decimal is probably the most manageable base.
The algorithm I used is making successive integer divisions by two until we reach zero, the number of divisions we made is the number of bits needed to represent this number.
So, how do we divide by two in digitunary? Here's the Retina snippet that does it:
(1*)(1?)\1# We divide one digit, the first group captures the result, the second group captures the remainder
$1#$2$2$2$2$2 The result is put in place of the old number, the remainder passes to the next digit (so it is multiplied by 10) and is divided by two there -> 5 times the remainder goes to the next digit
This replacement is enough to divide a digitunary number by 2, we just need to remove possible .5s from the end if the original number was odd.
So, here's the full code, we keep dividing by two until there are still digits in the number, and put a literal n
in front of the string at each iteration: the number of n
at the end is the result.
. |
$*1# Convert to digitunary
{`^(.*1) Loop:|
n$1 add an 'n'
(1*)(1?)\1# |
$1#$2$2$2$2$2 divide by 2
)`#1*$ |
# erase leftovers
n Return the number of 'n's in the string
Try it online!
Updated solution, 37 bytes
Big refactoring with many good ideas that golfed about a third of the length, all thanks to Martin Ender!
The main idea is to use _
as our unary symbol: in this way we can use regular digits in our string, as long as we convert them back to _
s when it is needed: this lets us save many bytes on division and on insertion of multiple digits.
Here's the code:
<empty line> |
# put a # before each digit and at the end of the string
{`\d Loop:|
$*_ Replace each digit with the corrisponding number of _
1`_ |
n_ Add an 'n' before the first _
__ |
1 Division by 2 (two _s become a 1)
_# |
#5 Wherever there is a remainder, add 5 to the next digit
}`5$ |
Remove the final 5 you get when you divide odd numbers
n Return the number of 'n's in the string
Try it online!
floor(log2(num))+1
\$\endgroup\$num
is a power of two. \$\endgroup\$