22
\$\begingroup\$

As we learned from The Holy Numbers, there are 5 holy digits (0, 4, 6, 8, 9), and positive integers consisting solely of those digits are holy. Additionally, the holiness of a number is the sum of the holes in the number (+2 for every 0 or 8, and +1 otherwise).

Now, there is an additional property to take into consideration, to truly and accurately represent the holiness of a number. You see, it's not just the number of holes in the digit that matters, but also where in the number it occurs.

Consider the number 88. By our old rules, it would have a holiness of 4. But that's hardly fair! The 8 on the left is doing more work than the other 8 - 10 times the work! It should be rewarded for its work. We will reward it with extra holiness points equal to the total holiness of all the digits to its right (including the extra holiness points granted by this rule to the digits on its right), minus 1.

Here are more examples to take into consideration:

Number: 8080
Digital holiness: (2 + 7 - 1) + (2 + 3 - 1) + (2 + 1 - 1) + (2 + 0 - 1)
Total holiness: 15

Number: 68904
Digital holiness: (1 + 5 - 1) + (2 + 2 - 1) + (1 + 1 - 1) + (2 + 0 - 1) + (1 + 0 - 1)
Total holiness: 10

All of the digits are appropriately rewarded for their work with extra holiness, and all is well. We shall call this property "enhanced holarity".

In the great language Python, an algorithm for calculating enhanced holarity might look something like this:

# assumes n is a holy number
def enhanced_holarity(n):
    if n < 10:
        return 1 if n in [0, 8] else 0
    else:
        digits = list(map(int,str(n)[::-1]))
        res = []
        for i,x in enumerate(digits):
            res.append(enhanced_holarity(x))
            if i > 0:
                res[i] += sum(res[:i])
        return sum(res)

The Challenge

Given an integer n > 0, output the first n Holy Numbers, sorted by ascending enhanced holarity, using numeric value as a tiebreaker. You may assume that the input and output will be no greater than the maximum representable integer in your language or 2^64 - 1, whichever is less.

For reference, here are some test cases (input, followed by output):

25
4, 6, 9, 44, 46, 49, 64, 66, 69, 94, 96, 99, 0, 8, 84, 86, 89, 40, 48, 60, 68, 90, 98, 80, 88

100
4, 6, 9, 44, 46, 49, 64, 66, 69, 94, 96, 99, 444, 446, 449, 464, 466, 469, 494, 496, 499, 644, 646, 649, 664, 666, 669, 694, 696, 699, 0, 8, 84, 86, 89, 844, 846, 849, 864, 866, 869, 894, 896, 899, 40, 48, 60, 68, 90, 98, 404, 406, 409, 484, 486, 489, 604, 606, 609, 684, 686, 689, 80, 88, 804, 806, 809, 884, 886, 889, 440, 448, 460, 468, 490, 498, 640, 648, 660, 668, 690, 698, 840, 848, 860, 868, 890, 898, 400, 408, 480, 488, 600, 608, 680, 688, 800, 808, 880, 888

200
4, 6, 9, 44, 46, 49, 64, 66, 69, 94, 96, 99, 444, 446, 449, 464, 466, 469, 494, 496, 499, 644, 646, 649, 664, 666, 669, 694, 696, 699, 944, 946, 949, 964, 966, 969, 994, 996, 999, 4444, 4446, 4449, 4464, 4466, 4469, 4494, 4496, 4499, 4644, 4646, 4649, 4664, 4666, 4669, 4694, 4696, 4699, 0, 8, 84, 86, 89, 844, 846, 849, 864, 866, 869, 894, 896, 899, 40, 48, 60, 68, 90, 98, 404, 406, 409, 484, 486, 489, 604, 606, 609, 684, 686, 689, 904, 906, 909, 984, 986, 989, 4044, 4046, 4049, 4064, 4066, 4069, 4094, 4096, 4099, 80, 88, 804, 806, 809, 884, 886, 889, 440, 448, 460, 468, 490, 498, 640, 648, 660, 668, 690, 698, 940, 948, 960, 968, 990, 998, 4404, 4406, 4409, 4484, 4486, 4489, 4604, 4606, 4609, 4684, 4686, 4689, 840, 848, 860, 868, 890, 898, 400, 408, 480, 488, 600, 608, 680, 688, 900, 908, 980, 988, 4004, 4006, 4009, 4084, 4086, 4089, 800, 808, 880, 888, 4440, 4448, 4460, 4468, 4490, 4498, 4640, 4648, 4660, 4668, 4690, 4698, 4040, 4048, 4060, 4068, 4090, 4098, 4400, 4408, 4480, 4488, 4600, 4608, 4680, 4688, 4000, 4008, 4080, 4088
\$\endgroup\$
  • 10
    \$\begingroup\$ This hole idea is holarious. \$\endgroup\$ – Calvin's Hobbies Feb 24 '16 at 8:32
  • \$\begingroup\$ What do you mean by "output will be no greater than..."? As in the output won't have any number greater than 2^64 - 1? If that's the case it's probably worth figuring out which input first generates such numbers, so people can test their answers. \$\endgroup\$ – FryAmTheEggman Feb 24 '16 at 14:34
  • \$\begingroup\$ @FryAmTheEggman No greater than means less than or equal to. I'll update the post with some maximums for various integer sizes. \$\endgroup\$ – Mego Feb 24 '16 at 20:48
  • \$\begingroup\$ Your python code does not work for 6, it produces a holines of 0. \$\endgroup\$ – shrx Feb 25 '16 at 12:43
2
\$\begingroup\$

Python 2, 138 122 bytes

This looks for holy numbers up to 5N for an input N, which is ridiculously slow:

e=lambda s:s and(s[0]in'08')+e(s[1:])*2or 0
lambda N:sorted([`x`for x in range(5**N)if set(`x`)<=set('04689')][:N],key=e)

Here the limit is 5N2, and you can actually run the test cases, at the cost of a single byte:

e=lambda s:s and(s[0]in'08')+e(s[1:])*2or 0
lambda N:sorted([`x`for x in range(5*N*N)if set(`x`)<=set('04689')][:N],key=e)

The first snippet is valid, as 5N ≥ 5N2 for all positive integers N.

\$\endgroup\$
  • \$\begingroup\$ Oh, wait, I missed something.. Too tired for this. \$\endgroup\$ – seequ Feb 25 '16 at 21:40
3
\$\begingroup\$

Lua, 317 Bytes

I had some troubles doing this, some things in Lua don't work as I think it does. I will have to try and play with them if I want to golf this down. You can test lua online by replacing arg[1] by the number of elements you want :).

function f(y)h=0(y..''):reverse():gsub(".",function(c)h=c:find("[08]")and 1+h or h end)return h end
x,a=0,{}while(#a<arg[1]+0)do a[#a+1],x=(x..''):find("^[04689]*$")and x or nil,x+1 end
for i=1,#a do m=1
for j=1,#a do x=a[m]m=(f(x)~=f(a[j])and f(x)>f(a[j])or x>a[j])and j or m
end end print(a[m])table.remove(a,m)end

Ungolfed and explanations

function f(y)                     -- function returning the enhanced holiness of a holy number
  h=0                             -- h is the cumulated holyness of processed digits
  (y..''):reverse()               -- reverse the digits in y
         :gsub(".",function(c)    -- iterate over each digits
     h=c:find("[08]")and 1+h or h -- ternary based on the digit being [08] or [469]
   end)                           
  return h                        -- return h
end

x,a=0,{}                          -- initialise a counter, and the array of holy numbers
while(#a<arg[1]+0)                -- iterate until we have n holy numbers
do
  a[#a+1]=(x..'')                 
      :find("^[04689]*$")         -- if we can't find an unholy digit
             and x or nil         -- insert x into a
  x=x+1                           -- increment x anyway
end

for i=1,#a                        -- iterate n times(current size of a)
do
  m=1                             -- m is the index of the lowest value
  for j=1,#a                      -- iterate over a
  do
    x=a[m]                        -- x is shorter to write than a[m]
    m=(f(x)~=f(a[j])              -- nested ternaries, translated in
        and f(x)>f(a[j])          -- nested if below
        or x>a[j])and j or m      
  end
  print(a[m])                     -- output a[m]
  table.remove(a,m)               -- remove it from the table a
end

The nested ternaries used for the new value of m can be translated in nested ifs as:

if(f(a[m])~=f(a[j])) then         -- if a[m] and a[j] don't have the same holyness
  if(f(a[m])>f(a[j])) then m=j end-- compare by holyness
else
  if(a[m]>a[j]) then m=j end      -- else, compare by numeric value

Also, I would have loved to replace the nested for by using table.sort, but, for a reason I don't know, the following doesn't work despite not producing an infinite loop or crushing the sort function.

table.sort(a,function(i,j)
    return f(i)~=f(j)              
         and f(i)>f(j)          
         or i>j
end)
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 166 165 bytes

f=n=>[...Array(n)].map((_,i)=>i.toString(5)).sort((a,b)=>e(a)-e(b),e=n=>'0b'+[...n.replace(/./g,c=>'10010'[c])].reverse().join``).map(n=>+n.replace(/./g,c=>"04689"[c]))

Edit: Saved 1 byte by returning an array of strings.

Ungolfed:

function base5_to_extended_holiness_binary(c) {
    return "10010"[c];
}
function extended_holiness(n) {
    var binary = n.toString(5).replace(/./g, base5_to_extended_holiness_binary);
    binary = s.split("").reverse().join("");
    return parseInt(s, 2);
}
function extended_holiness_sort(a, b) {
    return extended_holiness(a) - extended_holiness(b);
}
function base5_to_holy_number(c) {
    return "04689"[c];
}
function list_by_extended_holiness(n) {
    var array = new Array(n);
    for (var i = 0; i < n; i++)
         array[i] = i;
    array = array.sort(extended_holiness_sort);
    for (var i = 0; i < n; i++)
        array[i] = parseInt(array[i].toString(5).replace(/./g, base5_to_holy_number);
    return array;
}
\$\endgroup\$

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.