我的目标是找到给定数字量的所有armstrong/narcisstic十六进制数
基本思想是,对于一组数字,例如[a,3,F,5],幂和总是相同的,无论它们出现的顺序如何。这意味着我们不必考虑每一个可能的最大值,这将大大减少运行时间
# Armstrong numbers base 16 for n digits
import time
import itertools
from typing import Counter
pows = [[]]
def genPow(max, base):
global pows
pows = [[0]*1 for i in range(base)]
for i in range(base):
pows[i][0] = i ** max
def check(a, b):
c1 = Counter(a)
c2 = Counter(b)
diff1 = c1-c2
diff2 = c2-c1
# Check if elements in both 'sets' are equal in occurence
return (diff1 == diff2)
def armstrong(digits):
results = []
genPow(digits, 16)
# Generate all combinations without consideration of order
for set in itertools.combinations_with_replacement('0123456789abcdef', digits):
sum = 0
# Genereate sum for every 'digit' in the set
for digit in set:
sum = sum + pows[int(digit, 16)][0]
# Convert to hex
hexsum = format(sum, 'x')
# No point in comparing if the length isn't the same
if len(hexsum) == len(set):
if check(hexsum, set):
results.append(hexsum)
return sorted(results)
start_time = time.time()
print(armstrong(10))
print("--- %s seconds ---" % (time.time() - start_time))
我的问题是,这仍然相当缓慢。10位数字最多需要60秒。我相信有很多方法可以更有效地做到这一点。有些事情我能想到,但不知道怎么做:生成组合的更快方法,停止求和计算的条件,比较求和和和集合的更好方法,比较后转换为十六进制
有没有办法优化这个
Edit:我尝试以不同的方式进行比较/检查,这样已经快了一点https://gist.github.com/Claypaenguin/d657c4413b510be580c1bbe3e7872624与此同时,我试图理解递归方法,因为它看起来会快得多
您的问题是
combinations_with_replacement
对于baseb
和lengthl
返回(b+l choose b)
不同的东西。在你的例子中(基数16,长度10)意味着你有5311735个组合然后对每一个进行重量级计算
您需要做的是在创建组合时过滤所创建的组合。一旦你意识到你不在通往阿姆斯特朗号码的路上,就放弃这条路。计算看起来会更复杂,但当它允许您跳过整个组合块而不必单独生成它们时,这是值得的
以下是该技术核心的伪代码:
我有一个小的逻辑错误,但下面是工作代码:
举个简单的例子:
需要2秒钟多一点的时间才能找到唯一的答案是
bcc6926afe
由于itertools的组合将以升序返回数字,因此使用其数字的排序列表比较幂和将更有效:
这是一个通用的自恋数字生成器,它使用这种比较模式:
输出:
使用timeit来比较性能,我们的速度提高了3.5倍:
请注意,处理时间随着每个新大小呈指数增长,因此仅3.5倍的速度提升将没有意义,因为它只会将问题推到下一个大小
相关问题 更多 >
编程相关推荐