我一开始的思路是用递归解决:
class Solution:
def minSteps(self, n: int) -> int:
@functools.lru_cache()
def findprime(n):
if n==1:
return 0
for i in range(int(sqrt(n)),1,-1):
if n%i==0:
return findprime(i)+findprime(n//i)
return n
return findprime(n)
时间复杂度大概是根号n左右,这里用到了一个记忆化递归的手法。 先讲一下cache装饰器。具体的知识我看这篇文章很有收获:
缓存淘汰算法与 python 中 lru_cache 装饰器的实现
基本思想是,缓存是有限的,那么如何选择被淘汰的缓存呢?
缓存淘汰算法:
FIFO:先进先出,太简单,不行
LFU: 但是,这个算法仍然存在一定的问题,那就是一旦某个数据在短时间被大量访问,此后即便很长时间没有任何访问,该数据仍然凭借其巨大的访问次数数值而不被淘汰。
LRU:最常使用:这是目前实践中最为广泛使用的一种缓存淘汰算法,他的算法思想与 LFU 非常相似,但他去除了访问次数数值,在队列中采用访问则上升的策略,从而规避了上面提到的访问次数数值过大造成的影响。
讲到装饰器,就要先从装饰器的原理开始:具体看文章:
让你真正明白装饰器的工作原理和执行顺序
简单来说:python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
而lru_cache本质上是一种带参数的装饰器,他的参数就是max_size:
def make_key(args, kwds):
"""
通过方法参数获取缓存的 key
:param args: 方法原始参数
:param kwds: 方法键值对参数
:return: 计算后 key 的 hash 值
"""
key = args
if kwds:
key += object()
for item in kwds.items():
key += item
elif len(key) == 1 and type(key[0]) in {int, str, frozenset, type(None)}:
return key[0]
return hash(key)
def lru_cache(maxsize=128):
def decorating_function(user_function):
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3
cache = {} # 缓存结构,存储 key 与缓存数据的映射
hits = misses = 0 # 缓存命中、丢失统计
full = False # 缓冲区是否已满标记
cache_len = cache.__len__ # 缓冲区大小
root = []
root[:] = [root, root, None, None] # 队列头结点为空
def lru_cache_wrapper(*args, **kwds):
nonlocal root, hits, misses, full
key = make_key(args, kwds)
with RLock(): # 线程安全锁
link = cache.get(key)
""" 缓存命中,移动命中节点,直接返回预设结果 """
if link is not None:
""" 从链表中移除命中节点 """
link_prev, link_next, _key, result = link
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
""" 将命中节点移动到队尾 """
last = root[PREV]
last[NEXT] = root[PREV] = link
link[PREV] = last
link[NEXT] = root
hits += 1
return result
""" 缓存未命中,调用方法获取返回,创建节点,淘汰算法 """
result = user_function(*args, **kwds)
with RLock():
if key in cache:
pass
elif full:
""" 缓冲区已满,淘汰队首元素,删除缓冲区对应元素 """
oldroot = root
oldroot[KEY] = key
oldroot[RESULT] = result
root = oldroot[NEXT]
oldkey = root[KEY]
root[KEY] = root[RESULT] = None
del cache[oldkey]
cache[key] = oldroot
else:
""" 缓冲区未满,直接创建节点,插入数据 """
last = root[PREV]
link = [last, root, key, result]
last[NEXT] = root[PREV] = cache[key] = link
full = (cache_len() >= maxsize)
misses += 1
return result
return lru_cache_wrapper
return decorating_function
关于带parameter的decorator我看了看中文的博客没有讲的很好的,不过GF上有一篇文章讲的不错:
Decorators with parameters in Python
稍微聊聊一下装饰器,然后再扯回原来的方法,官方题解给的方法是质因数分解的方法,方法一般但具有普遍性,所以我自己又写了一遍。
class Solution:
def minSteps(self, n: int) -> int:
res,i=0,2
while i*i<=n:
while n%i==0:
n=n//i
res+=i
i+=1
if n!=1:
return res+n
return res