【问题标题】:Largest product in a series in pythonpython系列中最大的产品
【发布时间】:2016-02-19 09:39:18
【问题描述】:

1000位数字中乘积最大的相邻四位是:

9 × 9 × 8 × 9 = 5832

73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450

找出 1000 位数字中乘积最大的 13 个相邻数字。这个产品有什么价值?

我对这个问题的解决方法是:

def no(x):
    previous=0
    i=0
    t=1
    while i !=987:
        for num in x[i:i+13]:
            no=int(num)
            t=no*t

        if  t>previous:
            previous = t
        i=i+1
        t=1
    return previous  

有没有其他好的和有效的方法来解决这个问题?因为我觉得我的效率不是很高

【问题讨论】:

  • 您的解决方案仍然在线性时间内有效,因此除了添加一些短路启发式方法之外,很难提高效率。
  • 您的代码在 O(n) 中,在复杂度类中讲,它不会变得更快。但是,您可以做些什么来减少该因子,而不是每次都进行整个乘法运算,您可以保留数字并在每一步中除以被删除的数字并乘以新数字。您需要测试这是否更快,不确定除法与乘法相比有多快。
  • 您可以事先执行此操作:x = map(int, x) 并立即将所有数字转换为 int。您所做的是您对每个数字执行多次此转换。这应该可以为您节省一些时间。

标签: python python-2.7


【解决方案1】:

您可以在max 函数和适当的key 函数中使用生成器表达式来计算子数字的乘积。为此,您可以使用map 函数将数字转换为整数,并使用reduce(在python 3.X 中functools.reduce)来计算整数的乘积。

>>> max((digits[i:i+13] for i in xrange(0, len(digits) - 12)), key=lambda x: reduce(mul, map(int, x)))
'5576689664895'

请注意,如果您的数字之间有换行符,您需要使用str.replace() 方法将其删除。

digits = digits.replace('\n', '')

更优化的方法:

由于您每次都在处理 13 位数字,因此您可以使用容器来在每次迭代中保留您的数字,这里的最佳选择是 deque() 形式 collections 模块和 maxlen=13 它的流行和push 操作的顺序是 O(1)。然后您可以计算前 13 位数字的乘积,并且在每次推送和弹出时,您的初始乘积应该除以弹出的项目和乘以推送的项目。并且在每次迭代中,您可以只保留具有最大产品的序列。

from operator import mul
from collections import deque
from copy import copy

def cal_max_prod(container, current_product):
    max_container = {'seq': copy(container), 'prod': current_product}
    for i in digits[13:]:
        popped_item = int(container.popleft())
        container.append(i)
        try:
            push_item = int(i)
            current_product = (current_product / popped_item) * push_item
        except ZeroDivisionError:
            if '0' not in container:
                current_product = reduce(mul, map(int, container))
        else:
            if current_product > max_container['prod']:
                max_container['prod'] = current_product
                max_container['seq'] = copy(container)

    return ''.join(max_container['seq'])

演示:

container = deque(digits[:13], maxlen=13)
current_product = reduce(mul, map(int, container))
print cal_max_prod(container, current_product)
5576689664895

【讨论】:

  • 只是询问好奇心是不是这个问题的最佳代码,我的意思是在速度和内存占用方面(假设更大的序列说〜100万)
  • 速度方面,基本和OP的代码一样。
  • @jamessmith 由​​于此代码使用生成器表达式,我认为它在内存使用方面是最佳的,但由于多个索引和循环以及使用reduce,我认为它在运行时方面不是最佳的。跨度>
  • @falsetru 你认为就速度而言最好的代码
  • OP 的代码不使用列表,所以内存使用不会有明显差异。
【解决方案2】:

为简单起见,请考虑 3 位而不是 13 位。前三位是:

731

他们的乘积是 21。接下来的三位数字是:

316

乘积是18。我们想要一个高效的算法,所以我们必须回答的一个问题是:我们能否在常数时间内从316 的乘积中计算出731 的乘积?

答案是肯定的:如果我们看数字,从731316,我们删除了 7 并添加了 6。但是如果我们看产品,我们已经划分了 乘以 7 和 乘以乘以 6。而不是计算 7×3×1,然后是 3×1×6,然后是 1×6×7,依此类推(执行 n 每次乘法)我​​们可以从前一个乘积计算下一个乘积(仅执行 1 次乘法和 1 次除法)。

这是一个在线性时间内运行的高效算法的草图:

def maxproduct(number, digits):
    """Calculate the maximum product of the n-adjacent digits of number."""
    zeros = 0
    product = 1
    result = 0

    # Calculate the first, initial product.
    for x in number[:digits]:
        x = int(x)
        if x:
            product *= int(x)
        else:
            # This digit is 0. This will make our product zero
            # too (losing information about other digits) and will
            # also cause trouble with division later. Instead of
            # storing the zero in the product, we increment a counter.
            zeros += 1

    if not zeros:
        # This product is the highest we have seen so far.
        result = product

    # Calculate the other products with the remaining digits.
    for i in range(digits, len(number)):
        # Digit to remove.
        x = int(number[i - digits])
        # Digit to add.
        y = int(number[i])

        if x:
            product //= x
        else:
            # The digit to remove is 0.
            zeros -= 1

        if y:
            product *= y
        else:
            zeros += 1

        if not zeros and product > result:
            result = product

    return result

【讨论】:

  • 你测试过速度吗?而且它是 13 位而不是 3 位这一事实会对时间产生影响。
  • @LtWorf:我的回答与代码速度无关,而是与算法效率有关
  • 因为我们乘以一个常数位数,所以最终的解无论如何都是 O(n)。所以你要做的是减少常数因子。并且您假设除法与乘法具有相同的权重。但情况可能并非如此。这只是您做出的任意假设,没有任何支持。
  • @LtWorf:实际上我假设 13 不是常数
【解决方案3】:

如果你想提高效率,你会注意到在数字 n 到 n+12 和 n+1 到 n+13 的乘积之间,你有 12 个公因数。

所以让我们注意 di 是 [ 0, 999] 中 i 的 n 位数字,而 pi 是从 di 开始的 m 个连续数字的乘积(您的要求中 m=13):

  • pi 为 [0, 1000-m] 中的 i 定义
  • pi+1 = pi / di * di+m 当 di != 0

在每次迭代中,当 di != 0 时,您只做一个产品和一个除法,而不是 12 个产品,对于随机系列,这应该会出现 9 次(10 次)

与往常一样,优化算法,然后再考虑低级代码优化。但是...代码会更大或更复杂...

【讨论】:

    【解决方案4】:

    如果内存不是问题,则使用 numpy。从他的数字基准测试来看,速度似乎比 OP 代码快 10 倍:

    import numpy as np
    def no2(x,d=13):
        k = len(x)-d
        t = np.ones(k,dtype=int)
        xa = np.fromiter(map(int,tuple(x)),dtype=int)
        for i in range(d): #xrange on python2.x
            t *= xa[i:k+i]
        return np.max(t)
    

    要获得返回最大值的数字,请将np.max(t) 替换为x[np.argmax(t):np.argmax(t)+d]。 示例:

    no2('123456789101234123412351235324234324234')
    1244160
    

    基准测试

    对您提供的字符串进行基准测试时,我发现:

    #OP approach:
    %timeit -n 100  no(x)
    100 loops, best of 3: 3.49 ms per loop
    
    #Kasramvd approach:
    %timeit -n 100  no3=max((s[i:i+13] for i in range(0, len(s) - 12)), key=lambda x: reduce(mul, map(int, x)))
    100 loops, best of 3: 4.22 ms per loop
    
    #Andrea approach:
    %timeit -n 100 no4(x,13) 
    100 loops, best of 3: 777 µs per loop
    
    #numpy approach:
    %timeit -n 100  no2(x)
    100 loops, best of 3: 315 µs per loop
    

    当内存和速度都成为问题时:

    当使用 10^4 倍长的字符串(即 10^7 位)进行基准测试时:

    #numpy
    %timeit -n 10 no2(x5)
    10 loops, best of 3: 2.2 s per loop
    
    #OP code
    %timeit -n 1 no(x5)
    1 loop, best of 3: 37 s per loop
    
    #Andrea code
    %timeit -n 1 no4(x5,13)
    1 loop, best of 3: 8.01 s per loop
    

    这也是 OP 代码的大约 10 倍改进。因此,除非数组无法放入 RAM(但字符串可以),否则 numpy 的性能会更好。

    【讨论】:

      【解决方案5】:
       # I found this solution in this video  (https://www.youtube.com/watch?v=oSZZn7krnX0&list=PLO1D3YWS7ep3Zrh8B4SrhIsyxneg23x29&index=10)
       l='''
                      73167176531330624919225119674426574742355349194934
                      96983520312774506326239578318016984801869478851843
                      85861560789112949495459501737958331952853208805511
                      12540698747158523863050715693290963295227443043557
                      66896648950445244523161731856403098711121722383113
                      62229893423380308135336276614282806444486645238749
                      30358907296290491560440772390713810515859307960866
                      70172427121883998797908792274921901699720888093776
                      65727333001053367881220235421809751254540594752243
                      52584907711670556013604839586446706324415722155397
                      53697817977846174064955149290862569321978468622482
                      83972241375657056057490261407972968652414535100474
                      82166370484403199890008895243450658541227588666881
                      16427171479924442928230863465674813919123162824586
                      17866458359124566529476545682848912883142607690042
                      24219022671055626321111109370544217506941658960408
                      07198403850962455444362981230987879927244284909188
                      84580156166097919133875499200524063689912560717606
                      05886116467109405077541002256983155200055935729725
                      71636269561882670428252483600823257530420752963450'''
          number = l.strip()
          number = number.replace('\n','')
          largest_mul = 0
          lenght = 13
          for i in range(len(number)):
             mul = 1
             for n in number[i:i+lenght]:
                  mul *= int(n)
                  if mul > largest_mul:
                      largest_mul = mul
              
          print(largest_mul)
              # 23514624000
      

      【讨论】:

        【解决方案6】:
        data=list("
        73167176531330624919225119674426574742355349194934\
        96983520312774506326239578318016984801869478851843\
        85861560789112949495459501737958331952853208805511\
        12540698747158523863050715693290963295227443043557\
        66896648950445244523161731856403098711121722383113\
        62229893423380308135336276614282806444486645238749\
        30358907296290491560440772390713810515859307960866\
        70172427121883998797908792274921901699720888093776\
        65727333001053367881220235421809751254540594752243\
        52584907711670556013604839586446706324415722155397\
        53697817977846174064955149290862569321978468622482\
        83972241375657056057490261407972968652414535100474\
        82166370484403199890008895243450658541227588666881\
        16427171479924442928230863465674813919123162824586\
        17866458359124566529476545682848912883142607690042\
        24219022671055626321111109370544217506941658960408\
        07198403850962455444362981230987879927244284909188\
        84580156166097919133875499200524063689912560717606\
        05886116467109405077541002256983155200055935729725\
        71636269561882670428252483600823257530420752963450")
        
        print(len(data))
        
        lst=list()
        
        while True:
        
                x=list(data[0:13])
        
                pro=1
        
                for i in x:
        
                    pro*=int(i)
        
                lst.append(pro)
        
        
                data.pop(0)
        
                if len(data)==12:
        
                    break
        
        print(max(lst))
        
        print(len(data))
        
        print(lst[0])
        

        【讨论】:

          猜你喜欢
          • 2018-10-21
          • 1970-01-01
          • 1970-01-01
          • 2016-02-01
          • 2016-06-08
          • 1970-01-01
          • 1970-01-01
          • 2017-09-14
          • 2015-10-07
          相关资源
          最近更新 更多