思路

提交已通过。感谢大佬们的思路,直接借鉴范围缩减至sqrt(n)+1,降低时间复杂度。
另加了两个条件,进一步减少低效计算:

一 数学家发现完全数末位数字都是6或28,虽然不知道原因,但是管用。
二 完全数另一个特征:
在因子数列末尾加上完全数自身、再从中间对半劈开,则左右都是等比数列,公比为2。
文字描述比较抽象,列几行一看就明白了:

1 2 pikai 3 6
1 2 4 pikai 7 14 28
1 2 4 8 16 pikai 31 62 124 248 496
1 2 4 8 16 32 64 pikai 127 254 508 1016 2032 4064 8128

以上,1 2 3 6 分别是 2**0, 2**1, 2**2-1, (2**2-1)*2
也就是说,以2**k找出因子数列再求和,就可以得到一个潜在的完全数,而不满足这个特点的一定不是完全数,即可进一步缩减查找范围。

结构概要:

addOnePerfectNum 负责每次为 arr 列表增加一个完全数,列表复用

Step1 计算出以 2**k 为中心的对称序列,从而确定一个潜在完全数 mul
Step2 检查这个潜在完全数是否符合特点一(以‘6’或者‘28’结尾)
Step3 用完全数定义检查(找出所有真因子并求和,再与自身比较)
Step4 通过筛选确认为完全数,加入列表arr

helper 负责检查整数n是否大于目前 arr 列表中最大的完全数

如果小于则遍历arr,数出小于n的完全数个数
如果大于,调用addOnePerfectNum补充完全数,每算一个就与n比较,直到最新的完全数大于n

代码:

class Solution:
    #零 arr承载已经计算出来的完全数,避免重复计算。vark承载特点二中的k值。  
    arr,vark = [],0
    
    #一 由 helper 负责检查整数n是否大于 目前计算出的最大完全数
    def helper(self,n:int)->int:
        _arr = self.arr
        #1 如果小于,则遍历现有的完全数数组,找出小于n的完全数个数
        if n<_arr[-1]:
            for i in range(0,len(_arr)):
                if _arr[i]>n:
                    break
            return i
        #2 如果大于,调用addOnePerfectNum补充完全数,每算一个就与n比较,直到最新的完全数大于n为止
        else:
            while(_arr[-1]<n):
                self.addOnePerfectNum()
            return len(_arr) if _arr[-1]==n else len(_arr)-1
    
    #二 由 addOnePerfectNum 负责每次为 arr 列表增加一个完全数    
    def addOnePerfectNum(self):       
        k = self.vark
        while True:
            #1 首先计算出以 2**k 为中心的对称序列,从而确定一个潜在完全数 mul
            a,b = 2**k, 2**(k+1)-1
            mul,sm = a*b, a+b
            for i in range(k-1,-1,-1):
                a /= 2
                b *= 2
                sm += a+b
            #2 然后检查这个潜在完全数是否符合特点一(以‘6’或者‘28’结尾)
            sm2 = 0
            if sm==2*mul and (str(mul).endswith('6') or str(mul).endswith('28')):
                
                #2.1 用完全数的定义进行检查,找出所有真因子并求和,再与自身比较。
                for i in range(1,int(mul**0.5)+1):
                    if mul%i==0:
                        if i**2==mul:
                            sm2 += i
                        else:
                            sm2 += int(i+mul/i)
                #2.2 通过本轮筛选即确认为完全数数,加入列表arr
                if mul*2==sm2:
                    self.arr.append(mul)
                    self.vark = k+1
                    break
                else:
                    k = k+1
            else:
                k = k+1

# 在输入循环以外创建实例,不然还是每次从第一个完全数开始算。
s1 = Solution()
s1.addOnePerfectNum()
while True:
    try:
        n = int(input())
        print(s1.helper(n))
    except:
        break

效果还行

alt