Python:涉及幂级数的问题中的效率

时间:2018-10-18 07:14:25

标签: python algorithm python-2.7 performance generator

问题定义:

这是codewars中的问题。

自我反转的电源序列定义为:

S(0) = 0
S(1) = 1
S(2) = 1^2 + 2 = 3
S(3) = 1^3 + 2^2 + 3 = 8
...
S(n) = 1^n + 2^(n-1) + ... + (n-1)^2 + n

实现一个函数,该函数接受2个参数(num_dig和ord_max),并找到小于num dig的序列的最小数目,该序列也具有ord_max位数。

如果有正确位数的数字,则结果应为以下形式的数组:

[True, smallest found term] 

[False, -1]

这些是一些示例:

n-th Term    Term Value
1              0
2              1
3              3
4              8
5              22
6              65
7              209
8              732
9              2780
10             11377
11             49863
12             232768
13             1151914
14             6018785

因此示例测试包括:

min_length_num(5, 10) == [True, 10]   # 10th term has 5 digits
min_length_num(7, 11) == [False, -1]  # no terms before 13th has 7 digits
min_length_num(7, 14) == [True, 13]   # 13th term already has 7 digits

我的方法

我创建了一个生成器,其生成幂级数S的所有值,直到值n:

def seq(n):
    for i in range(n):
        s = range(i+1)
        j = i+1
        tot = 0
        while j > 0:
            for k in s:
                tot += k**j
                j -=1
            break
        yield tot

然后,我检查生成器的值,然后:或者,当遇到第一个具有所需位数的值时,立即返回True,否则返回False:

def min_length_num(num_dig, ord_max): 
    i = 1
    for n in seq(ord_max):
        if len(str(n)) == num_dig:
            return [True, i]
        elif len(str(n)) > num_dig:
            break
        i +=1
    return [False, -1]

这将通过所有测试,但由于超时而无法完成测试过程。输入范围假定ord_max <=1000。

我对生成器不是很精通,所以也许我在这里做错了什么,或者做得不合适。我将不胜感激。谢谢。

编辑:另一种解决方案。

因为我知道ord_max <= 1000,所以我可以预先计算所有值并按如下所示修改代码:

p = [n for n in seq(1000)]

def min_length_num(num_dig, ord_max): 
    i = 1
    for k in p[:ord_max]:
        if len(str(k)) == num_dig:
            return [True, i]
        elif len(str(k)) > num_dig:
            break
        i +=1
    return [False, -1]

这更快并且可以解决我的问题,但是我发现它是一个非常丑陋的解决方案,更多是肮脏的hack。我想要更好的东西。

2 个答案:

答案 0 :(得分:1)

不是完整的解决方案,但有一些优化技巧:

您不需要一次又一次地计算自然数的所有幂-我怀疑求幂运算需要很长时间。而是存储当前功率列表,并在每一步中将power[k]乘以k

 1  4  3       //3rd stage
 1  8  9  4    //4th stage

并使用新旧功率之间的差异更新总和值

 S(4) = S(3) + (8-4) + (9-3) + 4 = 22

还要用所需的位数预先计算目标值

  powten = 10**(num_dig-1)  //1 000 000 for num_dig=7

并与powten比较总和而不转换为字符串

答案 1 :(得分:0)

类似于@Mbo的建议,但是您不必简单地用旧幂和新幂之间的差异来更新序列,只需将现有序列乘以相应的增量,然后附加新的术语号(减去一个,因为第一个术语的值是0)的顺序:

def min_length_num(dig, max_term):
    seq = []
    target = pow(10, dig - 1)
    term = 1
    while term <= max_term:
        for i in range(term - 1):
            seq[i] *= i
        seq.append(term - 1)
        if sum(seq) >= target:
            return [True, term]
        term += 1
    return [False, -1]

这样:

print(min_length_num(5, 10))
print(min_length_num(7, 11))
print(min_length_num(7, 14))

将输出:

[True, 10]
[False, -1]
[True, 13]