Python 3:优化项目Euler问题#14

Python 3:优化项目Euler问题#14,python,python-3.x,memoization,collatz,Python,Python 3.x,Memoization,Collatz,我正在尝试使用Python3解决(最长的Collatz序列)。下面是我的实现 cache_limit = 5000001 lookup = [0] * cache_limit lookup[1] = 1 def collatz(num): if num == 1: return 1 elif num % 2 == 0: return num >> 1 else: return (3 * num) + 1

我正在尝试使用Python3解决(最长的Collatz序列)。下面是我的实现

cache_limit = 5000001
lookup = [0] * cache_limit
lookup[1] = 1


def collatz(num):
    if num == 1:
        return 1
    elif num % 2 == 0:
        return num >> 1
    else:
        return (3 * num) + 1


def compute(start):
    global cache_limit
    global lookup
    cur = start
    count = 1

    while cur > 1:
        count += 1
        if cur < cache_limit:
            retrieved_count = lookup[cur]
            if retrieved_count > 0:
                count = count + retrieved_count - 2
                break
            else:
                cur = collatz(cur)
        else:
            cur = collatz(cur)

    if start < cache_limit:
        lookup[start] = count

    return count


def main(tc):
    test_cases = [int(input()) for _ in range(tc)]
    bound = max(test_cases)
    results = [0] * (bound + 1)

    start = 1
    maxCount = 1
    for i in range(1, bound + 1):
        count = compute(i)
        if count >= maxCount:
            maxCount = count
            start = i
        results[i] = start

    for tc in test_cases:
        print(results[tc])


if __name__ == "__main__":
    tc = int(input())
    main(tc)
我现在被这件事困扰了一段时间。不知道这里还能做些什么

这里还有什么可以优化的,这样我就不会超时了

任何帮助都将不胜感激:)

注意:使用上述实现,我能够解决实际的Project Euler问题#14。它只为hackerrank中的4个测试用例提供超时时间。

以下是我的实现(专门针对Project Euler网站上的问题):

num=1
limit=int(输入())
seq_list=[]
而num<限制:
序列号=0
n=num
如果n==1:
顺序_num=1
其他:
而n!=1:
如果n%2==0:
n=n/2
序列号_num+=1
其他:
n=3*n+1
序列号_num+=1
序列号_num+=1
序号列表追加(序号)
num+=1
k=序列列表索引(最大值(序列列表))
打印(k+1)

是的,您可以对代码做一些事情来优化它。但我认为,更重要的是,有一个数学观察需要考虑,这是问题的核心:

whenever n is odd, then 3 * n + 1 is always even. 
有鉴于此,我们总是可以将(3*n+1)除以2。这为我们节省了不少时间……

这里有一个改进(需要1.6秒):不需要计算每个数字的顺序。您可以创建字典并存储序列中元素的数量。如果出现的数字已经出现,则序列计算为dic[原始编号]=dic[n]+计数-1。这节省了很多时间

import time

start = time.time()

def main(n,dic):
    '''Counts the elements of the sequence starting at n and finishing at 1''' 
    count = 1
    original_number = n
    while True:
        if n < original_number:
            dic[original_number] = dic[n] + count - 1 #-1 because when n < original_number, n is counted twice otherwise
            break
        if n == 1:
            dic[original_number] = count
            break
        if (n % 2 == 0):
            n = n/2
        else:
            n = 3*n + 1
        count += 1
    return dic

limit = 10**6
dic = {n:0 for n in range(1,limit+1)}

if __name__ == '__main__':
    n = 1
    while n < limit:
        dic=main(n,dic)

        n += 1        
    print('Longest chain: ', max(dic.values()))
    print('Number that gives the longest chain: ', max(dic, key=dic.get))
    end = time.time()

    print('Time taken:', end-start)
导入时间
开始=时间。时间()
def总管(n,驾驶员信息中心):
''计算从n开始到1结束的序列元素''
计数=1
原始编号=n
尽管如此:
如果n<原始编号:
dic[原始编号]=dic[n]+计数-1#-1,因为当n<原始编号时,n会计数两次,否则
打破
如果n==1:
dic[原始编号]=计数
打破
如果(n%2==0):
n=n/2
其他:
n=3*n+1
计数+=1
返回dic
限值=10**6
dic={n:0表示范围(1,极限+1)}
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
n=1
当n<极限时:
dic=主(n,dic)
n+=1
打印('最长链:',最大值(dic.values()))
打印('给出最长链的编号:',max(dic,key=dic.get))
end=time.time()
打印('所用时间:',结束-开始)

解决这个问题的诀窍是只计算最大输入的答案,并将结果保存为查找所有较小输入,而不是计算极值上限

这是我的实现,它通过了所有测试用例

MAX=int(5*1e6)
ans=[0]
步骤=[0]*(最大值+1)
def解算(N):
如果N>1)#这是递归
如果N=mx:
mx=当前计数
collatz=i
附加说明(collatz)
对于uu输入:
打印(ans[3;])

感谢您的回答,但我正在寻找关于我的实现不起作用的原因的解释。此外,就实际的project Euler问题的解决方案而言,上述实现已经起到了作用。在许多hackerrank的问题中,如果您使用python,就会出现该错误。与其他语言中的代码完全相同,它将通过所有测试。@rpanai-Hmm。。。看起来像是。。。
import time

start = time.time()

def main(n,dic):
    '''Counts the elements of the sequence starting at n and finishing at 1''' 
    count = 1
    original_number = n
    while True:
        if n < original_number:
            dic[original_number] = dic[n] + count - 1 #-1 because when n < original_number, n is counted twice otherwise
            break
        if n == 1:
            dic[original_number] = count
            break
        if (n % 2 == 0):
            n = n/2
        else:
            n = 3*n + 1
        count += 1
    return dic

limit = 10**6
dic = {n:0 for n in range(1,limit+1)}

if __name__ == '__main__':
    n = 1
    while n < limit:
        dic=main(n,dic)

        n += 1        
    print('Longest chain: ', max(dic.values()))
    print('Number that gives the longest chain: ', max(dic, key=dic.get))
    end = time.time()

    print('Time taken:', end-start)
MAX = int(5 * 1e6)
ans = [0]
steps = [0]*(MAX+1)
 
def solve(N):
    if N < MAX+1:
        if steps[N] != 0:
            return steps[N]
    if N == 1:
        return 0
    else:
        if N % 2 != 0:
            result = 1+ solve(3*N + 1) # This is recursion
        else:
            result = 1 + solve(N>>1) # This is recursion
        if N < MAX+1:    
            steps[N]=result # This is memoization
        return result
    
inputs = [int(input()) for _ in range(int(input()))]
largest = max(inputs)

mx = 0
collatz=1
for i in range(1,largest+1):
    curr_count=solve(i)
    if curr_count >= mx:
        mx = curr_count
        collatz = i
    ans.append(collatz)
    
for _ in inputs:
    print(ans[_])