local start_index, end_index, counter local current_self local output local function done_after_adding(interval, n) -- Advance to the next self number in the sequence, append it to the output if required, indicate if finished. for _ = 1, n do current_self += interval counter += 1 if counter >= start_index then output:insert(current_self) end if counter == end_index then return true end end return false end local function self_numbers(index_range) start_index = index_range[1] end_index = index_range:back() counter = 0 current_self = -1 output = {} -- Main process. Start with the single-digit odd numbers and first run. if done_after_adding(2, 5) then return output end if done_after_adding(11, 9) then return output end -- If necessary, fast forward to last self number before the lowest-order block containing first number required. if counter < start_index then -- The highest-order blocks whose ends this handles correctly contain 9,777,777,778 self numbers. -- The difference between equivalently positioned numbers in these blocks is 100,000,000,001. -- The figures for successively lower-order blocks have successively fewer 7s and 0s! local index_diff = 9777777778 local numeric_diff = 100000000001 while index_diff >= 98 and counter != start_index do if counter + index_diff < start_index then counter += index_diff current_self += numeric_diff else index_diff = (index_diff + 2) / 10 -- (..78->80->8) numeric_diff = (numeric_diff + 9) /10 -- (..01->10->1) end end end -- Sequencing loop, per lowest-order block. while true do -- Eight ten-number runs, each at a numeric interval of 2 from the end of the previous one. for _ = 1, 8 do if done_after_adding(2, 1) then return output end if done_after_adding(11, 9) then return output end end -- Two shorter runs, the second at an interval inversely related to their length. local shorter_run_length = 8 local temp = current_self // 1000 -- Work out a shorter run length based on the most significant digit change about to happen. while temp % 10 == 9 do shorter_run_length -= 1 temp //= 10 end local interval = 2 for _ = 1, 2 do if done_after_adding(interval, 1) then return output end if done_after_adding(11, shorter_run_length) then return output end interval += (9 - shorter_run_length) * 13 end end end print("The first 50 self numbers are:\n") for i, sn in self_numbers({1, 50}) do io.write(string.format("%3d ", sn)) if i % 10 == 0 then print() end end print() for p = 8, 9 do local n = math.round(10 ^ p) local ord = string.formatint(n) local sn = string.formatint(math.round(self_numbers({n})[1])) print($"The {ord}th self number is {sn}") end