(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))