84 lines
3.0 KiB
Plaintext
84 lines
3.0 KiB
Plaintext
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
|
|
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
|