欧拉项目#18 - Pythonic方法

时间:2017-09-15 21:51:22

标签: python

我试图解决18th problem from Project Euler,但我陷入了解决方案。在论文中做到这一点我得到了相同的结果,但我知道答案与我之间的差异为10。

  

从下方三角形的顶部开始,移动到下面一行的相邻数字,从上到下的最大总数为23。

   3
  7 4
 2 4 6
8 5 9 3
     

即3 + 7 + 4 + 9 = 23。

     

查找下方三角形从上到下的最大总数:

                            75
                          95  64
                        17  47  82
                      18  35  87  10
                    20  04  82  47  65
                  19  01  23  75  03  34
                88  02  77  73  07  63  67
              99  65  04  28  06  16  70  92
            41  41  26  56  83  40  80  70  33
          41  48  72  33  47  32  37  16  94  29
        53  71  44  65  25  43  91  52  97  51  14
      70  11  33  28  77  73  17  78  39  68  17  57
    91  71  52  38  17  14  91  43  58  50  27  29  48
  63  66  04  68  89  53  67  30  73  16  69  87  40  31
04  62  98  27  23  09  70  98  73  93  38  53  60  04  23
     

注意:由于只有16384条路线,因此可以通过尝试每条路线来解决此问题。然而,问题67,对于包含一百行的三角形来说是同样的挑战;它无法通过蛮力解决,需要一种聪明的方法! ; O)

这是我的代码

filename = "triangle.txt"

f = open(filename,"r+")

total = 0

#will store the position of the maximum value in the line
index = 0

#get the first pyramid value
total = [int(x) for x in f.readline().split()][0]

#since it's only one value, the position will start with 0
current_index = 0 

# loop through the lines
for line in f:

    # transform the line into a list of integers
    cleaned_list = [int(x) for x in line.split()]

    # get the maxium value between index and index + 1 (adjacent positions)
    maximum_value_now = max(cleaned_list[current_index],cleaned_list[current_index + 1])

    #print maximum_value_now

    # stores the index to the next iteration        
    future_indexes = [ind for (ind,value) in enumerate(cleaned_list) if value == maximum_value_now]

    # we have more that 2 values in our list with this maximum value
    # must return only that which is greater than our previous index                    
    if (len(future_indexes) > 1):        
        current_index = [i for i in future_indexes if (i >= current_index and i <= current_index + 1)][0]

    else:
        #only one occurence of the maximum value        
        current_index = future_indexes[0]

    # add the value found to the total sum
    total = total + maximum_value_now

print total

谢谢!

2 个答案:

答案 0 :(得分:0)

首先,将整个三角形读入2d结构。值得注意的是,我们可以对三角形进行仿射变换,因此使用更简单的坐标系:

   3        \    3
  7 4    ====\   7 4
 2 4 6   ====/   2 4 6
8 5 9 3     /    8 5 9 3

很容易将其读入Python中的jagged array

with open(filename, 'r') as file:
    rows = [[int(i) for i in line.split()] for line in file]

现在将x作为水平坐标,y作为垂直坐标,并且它们向左和向下增加,从(x,y):(x + 1,y + 1)和(x,y)有2个有效移动+ 1)。就这么简单。

现在的技巧是计算每行中单元格的所有最大总和。这称为dynamic programming。最大总和是最后一行的最大总和。

实际上,除了前一行的总和以及当前行的总和之外,不需要记住任何内容。要计算最大行总和current_sums', we notice that to arrive to position x in the latest row, the position must have been x - 1 or x . We choose the maximal value of these, then sum with the current cell_value`。为简单起见,我们可以将三角形外的任何数字视为0,因为它们不会影响此处的最大解。因此我们得到

with open('triangle.txt', 'r') as file:
    triangle = [[int(i) for i in line.split()] for line in file]

previous_sums = []
for row in triangle:
    current_sums = []
    for position, cell_value in enumerate(row):
        sum_from_right = 0 if position >= len(previous_sums) else previous_sums[position]
        sum_from_left = (previous_sums[position - 1]
                         if 0 < position <= len(previous_sums)
                         else 0)
        current_sums.append(max(sum_from_right, sum_from_left) + cell_value)

    previous_sums = current_sums

print('The maximum sum is', max(previous_sums))

如果你喜欢列表推导,内部循环可以写成一个:

current_sums = []
for row in triangle:
    len_previous = len(current_sums)
    current_sums = [
        max(0 if pos >= len_previous else current_sums[pos],
            current_sums[pos - 1] if 0 < pos <= len_previous else 0)
        + cell_value
        for pos, cell_value in enumerate(row)
    ]

print('The maximum sum is', max(current_sums))

答案 1 :(得分:-1)

这是一个使用memoization

的简单递归解决方案
L1 = [
    "   3   ",
    "  7 4  ",
    " 2 4 6 ",
    "8 5 9 3",
]

L2 = [
    "                            75                             ",
    "                          95  64                           ",
    "                        17  47  82                         ",
    "                      18  35  87  10                       ",
    "                    20  04  82  47  65                     ",
    "                  19  01  23  75  03  34                   ",
    "                88  02  77  73  07  63  67                 ",
    "              99  65  04  28  06  16  70  92               ",
    "            41  41  26  56  83  40  80  70  33             ",
    "          41  48  72  33  47  32  37  16  94  29           ",
    "        53  71  44  65  25  43  91  52  97  51  14         ",
    "      70  11  33  28  77  73  17  78  39  68  17  57       ",
    "    91  71  52  38  17  14  91  43  58  50  27  29  48     ",
    "  63  66  04  68  89  53  67  30  73  16  69  87  40  31   ",
    "04  62  98  27  23  09  70  98  73  93  38  53  60  04  23 ",
]

class Max(object):
    def __init__(self, l):
        "parse triangle, initialize cache"
        self.l = l
        self.t = [
            map(int,filter(lambda x:len(x)>0, x.split(" ")))
            for x in l
        ]
        self.cache = {}

    def maxsub(self, r=0, c=0):
        "compute max path starting at (r,c)"
        saved = self.cache.get((r,c))
        if saved:
            return saved

        if r >= len(self.t):
            answer = (0, [], [])
        else:
            v = self.t[r][c]
            s1, l1, c1 = self.maxsub(r+1, c)
            s2, l2, c2 = self.maxsub(r+1, c+1)
            if s1 > s2:
                answer = (v+s1, [v]+l1, [c]+c1)
            else:
                answer = (v+s2, [v]+l2, [c]+c2)
        self.cache[(r,c)] = answer
        return answer

    def report(self):
        "find and report max path"
        m = self.maxsub()
        print
        print "\n".join(self.l)
        print "maxsum:%s\nvalues:%s\ncolumns:%s" % m

if __name__ == '__main__':
    Max(L1).report()
    Max(L2).report()

示例输出

   3
  7 4
 2 4 6
8 5 9 3
maxsum:23
values:[3, 7, 4, 9]
columns:[0, 0, 1, 2]

                            75
                          95  64
                        17  47  82
                      18  35  87  10
                    20  04  82  47  65
                  19  01  23  75  03  34
                88  02  77  73  07  63  67
              99  65  04  28  06  16  70  92
            41  41  26  56  83  40  80  70  33
          41  48  72  33  47  32  37  16  94  29
        53  71  44  65  25  43  91  52  97  51  14
      70  11  33  28  77  73  17  78  39  68  17  57
    91  71  52  38  17  14  91  43  58  50  27  29  48
  63  66  04  68  89  53  67  30  73  16  69  87  40  31
04  62  98  27  23  09  70  98  73  93  38  53  60  04  23
maxsum:1074
values:[75, 64, 82, 87, 82, 75, 73, 28, 83, 32, 91, 78, 58, 73, 93]
columns:[0, 1, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 8, 9]

要解决100行Project Euler problem 67,我们会对__main__进行一些小改动

def main():
    with file('triangle.txt') as f:
        L = f.readlines()
    Max(L).report()

if __name__ == '__main__':
    main()

最后一行输出:

maxsum:7273
values:[59, 73, 52, 53, 87, 57, 92, 81, 81, 79, 81, 32, 86, 82, 97, 55, 97, 36, 62, 65, 90, 93, 95, 54, 71, 77, 68, 71, 94, 8, 89, 54, 42, 90, 84, 91, 31, 71, 93, 94, 53, 69, 73, 99, 89, 47, 80, 96, 81, 52, 98, 38, 91, 78, 90, 70, 61, 17, 11, 75, 74, 55, 81, 87, 89, 99, 73, 88, 95, 68, 37, 87, 73, 77, 60, 82, 87, 64, 96, 65, 47, 94, 85, 51, 87, 65, 65, 66, 91, 83, 72, 24, 98, 89, 53, 82, 57, 99, 98, 95]
columns:[0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 17, 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 26, 27, 27, 28, 29, 30, 31, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, 37, 38, 39, 40, 41, 41, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 48, 49, 49, 50, 51, 52, 52, 53]

在我的Mac上,它立即返回答案。这是一个timeit衡量标准:

$ python -m timeit -s 'from p067 import main' main
100000000 loops, best of 3: 0.0181 usec per loop