加速Python循环

时间:2013-10-23 08:22:29

标签: python performance algorithm list loops

我有以下代码:

import math

print "Hey, lets solve Task 4 :)"

number1 = input ("How many digits do you want to look at? ")
number2 = input ("What would you like the digits to add up to? ")
final = []

if number1 == 1:
    cow = range(1,10)
elif number1 == 2:
    cow = range(10,100)
elif number1 == 3:
    cow = range(001,1000)
elif number1 == 4:
    cow = range(1000,10000)
elif number1 == 5:
    cow = range(10000,100000)
elif number1 == 6:
    cow = range(100000,1000000)
elif number1 == 7:
    cow = range(1000000,10000000)
elif number1 == 8:
    cow = range(10000000,100000000)
elif number1 == 9:
    cow = range(100000000,1000000000)
elif number1 == 10:
    cow = range(1000000000,10000000000)

number3 = cow[-1] + 1
number10 = number3
number8 = number3 - 1

if number1 == 1:
    test = range(1,number10)
elif number1 == 2:
    test = range(00,number10)
elif number1 == 3:
    test = range(000,number10)
elif number1 == 4:
    test = range(0000,number10)
elif number1 == 5:
    test = range(00000,number10)
elif number1 == 6:
    test = range(000000,number10)
elif number1 == 7:
    test = range(0000000,number10)
elif number1 == 8:
    test = range(00000000,number10)
elif number1 == 9:
    test = range(000000000,number10)
elif number1 == 10:
    test = range(0000000000,number10)

if number1 == 1:
    number7 = number8
elif number1 == 2:
    number7 = number8 + 1
elif number1 == 3:
    number7 = number8 + 1
elif number1 == 4:
    number7 = number8 + 1
elif number1 == 5:
    number7 = number8 + 1
elif number1 == 6:
    number7 = number8 + 1
elif number1 == 7:
    number7 = number8 + 1
elif number1 == 8:
    number7 = number8 + 1
elif number1 == 9:
    number7 = number8 + 1
elif number1 == 10:
    number7 = number8 + 1



n = 0
while n < number7:

    a = test[n]
    a = str(a)
    print a

    number4 = sum(int(x) for x in a)

    if number4 == number2:
        final.append(number4)

    n = n + 1



print len(final)

基本上,此代码计算出有多少位数字包含加起来为某些数字的整数。当它运行时,它会询问您想要多少位数(例如4)以及您希望它们加起来的数字。例如,您可以选择4和18,它将计算1000 - 9999中有多少数字的整数加起来为18(例如4545)。

问题是,最后一个问题询问有多少10位数加到39位。使用这段代码,我的计算机需要很长时间,因为它必须从1一直到最大的10位数。当我试过它时,它撞毁了我的电脑!

有没有办法加快循环?

谢谢!

6 个答案:

答案 0 :(得分:2)

你推动计算机太难了,老兄:)计算数万亿这样的数字。

看起来xrange更适合你的情况。

cow = xrange(1000, 10000)

答案 1 :(得分:2)

import math

print "Hey, lets solve Task 4 :)"

number1, number2, answer = 4, 18, 0

for num in xrange(10 ** (number1 - 1), 10 ** number1):
    total = 0
    while num:
        total += num % 10
        num /= 10
    if total == number2: answer += 1

print answer

答案 2 :(得分:1)

你似乎是用一些强力方法做的,你应该找到一种方法来跳过一些迭代。

例如,您选择4和18,而您的号码是1809:

1809 = 18  # match
1810 = 10
1811 = 11
1812 = 12

有一种模式,你需要优化它以跳过一些迭代。

答案 3 :(得分:1)

你的暴力算法是指数级的,所以无论你如何实现它都会很慢。 aIKid的建议将帮助你避免充斥内存,但它仍然需要很长时间才能运行。

我建议使用另一种增量算法:

number1 = 4
number2 = 18

options={}
for i in range(1,10):
    options[i]=1 # for single digit numbers there is one option for each target total

for _ in range(1,number1):
    new_options={}
    for i in range(0,10): # for each new digit we can add
        for option in options: # go through all the options
            cur_total=i+option
            if cur_total not in new_options:
                new_options[cur_total]=0
            new_options[cur_total]+=options[option] # and calc how many options this digit add for each target total
    options=new_options

print(options[number2])

答案 4 :(得分:1)

当您拥有大数字时,最快的解决方案是根本不会查看单个数字。如果你想知道有多少10位数字总和为39,首先找到所有10位数的独特组合,总和为39(其中2630)。

您可以这样做的方法是为每个数字d找到所有9位数字的唯一组合,总计39-d,其中没有数字小于d

def unique_combos(digits, total, smallest=0):
    if digits*9 < total or digits*smallest > total:
        return
    if digits==1:
        yield [total]
        return

    for i in range(smallest, 10):
        for combo in unique_combos(digits-1, total-i, i):
            yield [i]+combo

然后,对于每个唯一的数字组合,找出可以安排的方式。这只是简单的数学运算,只有你必须避免计算以前导零开头的任何数字。

import operator
from collections import Counter
from math import factorial
def npermutations(l):
    """From stackoverflow 16453188"""
    num = factorial(len(l))
    mults = Counter(l).values()
    den = reduce(operator.mul, (factorial(v) for v in mults), 1)
    return num / den

现在只需将每个组合的数字相加即可得到答案。鉴于我们不再过分担心速度,我只是用较少的数字减去数字。

def answer(digits, total):
    n_or_fewer = sum(npermutations(l) for l in unique_combos(digits, total))
    fewer = sum(npermutations(l) for l in unique_combos(digits-1, total))
    print("There are {} {}-digit numbers with digits summing to {}".format(
        n_or_fewer - fewer,
        digits, total))

if __name__=='__main__':
    answer(4,18)
    answer(10,39)

输出时间不到1秒:

C:\Temp>digitsum2.py
There are 615 4-digit numbers with digits summing to 18
There are 307100365 10-digit numbers with digits summing to 39

为了完整起见,Sudipta建议优化您如何逐步完成数字。这里有一些代码可以做到这一点:它直接从一个数字到下一个数字,并获得4位数字的相同答案,但我仍然厌倦了为3.07亿个10位数值运行它。它可能会被优化一点,但蛮力是错误的方式去这里。

def smallest_sum(n, digits, first=1):
    """Find the smallest number with 'digits' digits that sum to 'n'
    Returns the value as a list of digits because that's what we need next anyway"""
    k = max(first,n + 9 - 9*digits)
    assert k <= 9, "Impossible values"
    if digits > 1:
            return [k]+smallest_sum(n-k, digits-1, 0)
    return [k]

def all_sums(digits):
    """Find the next string of digits with the same sum.
    We can do this by finding the last non-zero digit and decrementing it
    then incrementing the previous non-nine, and sorting any digits that
    follow it."""
    yield digits
    while True:
        was = list(digits)
        for i in range(len(digits)-1,-1,-1):
            if digits[i] != 0:
                decrement = i
                break
        else:
            return
        for i in range(decrement-1,-1,-1):
            if digits[i] != 9:
                increment = i
                break
        else:
            return
        digits[decrement] -= 1
        digits[increment] += 1
        if increment < len(digits)-2:
            digits[increment+1:] = digits[increment+1:][::-1]
        assert digits > was
        yield digits

def count(num_digits, total):
    try:
        digits = smallest_sum(total, num_digits)
    except AssertionError:
        return 0
    return sum(1 for _ in all_sums(digits))

if __name__=='__main__':
    print(count(4,18))
    print(count(10,39))

答案 5 :(得分:0)

首先,正如许多其他答案所表明的那样:你需要使用xrange,它就像一个生成器,逐个给你每个数字并节省大量内存。

您还可以通过合并一些优化来改进算法:

  1. 检查给定范围内的 min和max 数字,其整数加起来为给定总和。

    示例 - 您的号码是4和18.您可以运行1089到9900的循环,而不是检查所有号码。

  2. 如果找到匹配项,则紧接着的下一个数字永远不会匹配(因为它的数字总和将比当前数字多1或8,17 ...比现在少没有。)。同样,即使是下一个也不能使用......依此类推,你可以找到一个模式来安全地跳过这些数字 (另一个答案中提出了类似的策略)

  3. 一开始,您可以检查一些边缘情况,例如,如果sum的输入小于9。

    示例 - 您的数字是4(数字编号)和5(数字之和)。然后我们可以安全地排除其中包含数字6,7,8,9的所有数字。