(phixonline)-->
--
-- Base-10 self numbers by index (single or range).
-- Follows an observed sequence pattern whereby, after the initial single-digit odd numbers, self numbers are
-- grouped in runs whose members occur at numeric intervals of 11. Runs after the first one come in blocks of
-- ten: eight runs of ten numbers followed by two shorter runs. The numeric interval between runs is usually 2,
-- but that between shorter runs, and their length, depend on the highest-order digit change occurring in them.
-- This connection with significant digit change means every ten blocks form a higher-order block, every ten
-- of these a higher-order-still block, and so on.
--
-- The code below appears to be good up to the last self number before 10^12 — ie. 999,999,999,997, which is
-- returned as the 97,777,777,792nd such number. After this, instead of zero-length shorter runs, the actual
-- pattern apparently starts again with a single run of 10, like the one at the beginning.
--
integer startIndex, endIndex, counter
atom currentSelf
sequence output
function doneAfterAdding(integer interval, n)
-- Advance to the next self number in the sequence, append it to the output if required, indicate if finished.
for i=1 to n do
currentSelf += interval
counter += 1
if counter >= startIndex then
output &= currentSelf
end if
if counter = endIndex then return true end if
end for
return false
end function
function selfNumbers(sequence indexRange)
startIndex = indexRange[1]
endIndex = indexRange[$]
counter = 0
currentSelf = -1
output = {}
-- Main process. Start with the single-digit odd numbers and first run.
if doneAfterAdding(2,5) then return output end if
if doneAfterAdding(11,9) then return output end if
-- If necessary, fast forward to last self number before the lowest-order block containing first number rqd.
if counter<startIndex 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!
atom indexDiff = 9777777778,
numericDiff = 100000000001
while indexDiff>=98 and counter!=startIndex do
if counter+indexDiff < startIndex then
counter += indexDiff
currentSelf += numericDiff
else
indexDiff = (indexDiff+2)/10 -- (..78->80->8)
numericDiff = (numericDiff+9)/10 -- (..01->10->1)
end if
end while
end if
-- 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 i=1 to 8 do
if doneAfterAdding(2,1) then return output end if
if doneAfterAdding(11,9) then return output end if
end for
-- Two shorter runs, the second at an interval inversely related to their length.
integer shorterRunLength = 8,
temp = floor(currentSelf/1000)
-- Work out a shorter run length based on the most significant digit change about to happen.
while remainder(temp,10)=9 do
shorterRunLength -= 1
temp = floor(temp/10)
end while
integer interval = 2
for i=1 to 2 do
if doneAfterAdding(interval,1) then return output end if
if doneAfterAdding(11,shorterRunLength) then return output end if
interval += (9-shorterRunLength)*13
end for
end while
end function
atom t0 = time()
printf(1,"The first 50 self numbers are:\n")
pp(selfNumbers({1, 50}),{pp_IntFmt,"%3d",pp_IntCh,false})
for p=8 to 9 do
integer n = power(10,p)
printf(1,"The %,dth safe number is %,d\n",{n,selfNumbers({n})[1]})
end for
printf(1,"completed in %s\n",elapsed(time()-t0))