为什么我的Project Euler 9的解决方案不起作用?

时间:2014-11-12 23:55:09

标签: python

问题9:

  毕达哥拉斯三元组是一组三个自然数,a< b< c,为此,   

a2 + b2 = c2

     例如,3 2 + 4 2 = 9 + 16 = 25 = 5 2

     

恰好有一个毕达哥拉斯三重态a + b + c = 1000。找到产品abc。

我的方法是生成三元组,然后检查它们是否符合总和a + b + c = d。为此,我应用了Dickson生成Pythagorean三元组的方法(Dickson方法,如维基百科所述:要找到x2 + y2 = z2的整数解,找到正整数rstr2 = 2 * s * t是一个正方形。然后:x = r + s; y = r + tz = r + s + t。从此我们看到r是任何偶数整数且s tr2 / 2的因素。

def problem9(d=12):
  def dickson(r=6):
    factors, triplets, st = [], [], (r**2)/2
    for i in range(1, int( sqrt(st)+1 )): # Sqrt optimization
      if st % i == 0:
        factors += [[r, i, st//i]]
    for i in range(len(factors)):
      r,s,t = factors[i][0], factors[i][1], factors[i][2]
      triplets += [[r+s,r+t,r+s+t]]
    return triplets

  def tripletSumsMyDValue(triplets):
    for tri in triplets:
      a,b,c = tri[0],tri[1],tri[2]
      if d == int(a + b + c):
        return tri
      else:
        return None

  inc = 2
  while True:
    found = tripletSumsMyDValue(dickson(inc))
    if found: return found
    else: print(inc,found,'is not!')
    inc += 1

令人沮丧的是,你认为自己是个天才,在执行你的代码后几秒钟就会发现你陷入了无限循环:P

while True:循环,它应该在点击正确的答案(200, 375, 425)后停止,但结果是无限的。

最令人沮丧的是下一个代码工作正常,所以请不要指出这一点。我只是想知道我的错误。

def problem9b(d = 12):
  inc = 1
  while True:
    for a in range(1, 100 * inc):
      for b in range(1, 100 * inc):
          c = (a ** 2 + b ** 2) ** .5
          if a + b + c == 1000:
              return a, b, c
    inc += 1

6 个答案:

答案 0 :(得分:3)

有一个很多更简单的方法来做到这一点;

如果不是直接进入编码,你可以看一下数学,你可以大大简化问题。

我们有两个方程式;

a2 + b2 = c2

a + b + c = 1000

所以将一个替换为另一个:

a2 + b2 = (1000 - a - b)2

0 = 10002 - 2000a - 2000b - 2ab

我们也知道abc都是<1000。因此,我们可以获得满足该等式的ab的所有组合的列表,然后只需检查sqrt(a**2 + b**2)是什么。

你可以用一个单独的(不可思议的丑陋而不是非常pythonic)列表理解来完成它,它只需要一秒钟。

[[a,b, sqrt(a**2 + b**2)] for a,b in combinations(range(1,1000),2) if 1000000-2000*a-2000*b+2*a*b==0]

[[200,375,425.0]]

list comprehension是在python中创建列表的快捷方式。通过列表推导创建的任何列表总是可以使用循环以更清晰的方式创建。您可以更多地了解它们here

这是一个简单的问题:

a = [i for i in range(10)]

与...完全相同:

a = []
for i in range(10):
  a.append(i)

这是我给出的例子;

[[a,b, sqrt(a**2 + b**2)] for a,b in combinations(range(1,1000),2) if 1000000-2000*a-2000*b+2*a*b==0]

可写:

triplets = []
for a,b in combinations(range(1,1000),2):
  if 1000000-2000*a-2000*b+2*a*b==0:
    triplets.append([a, b, sqrt(a**2 + b**2)])

combinations只是itertools模块的一个功能,您可以在那里阅读。

正如下面的评论所指出的那样,有一种方法可以进一步简化;

0 = 10002 - 2000a - 2000b - 2ab

可以重写

,以b为基础提供a

a = 1000 * 500-b / 1000-a

combinations(range(1,1000), 2)会列出一百万个条目。它与n2成比例。用b代表a,只迭代range(1,1000)一次,将其从O(n2)切割为O(n)。

相反,您可以这样做:

from math import sqrt, floor

for a in range(1, 500):
  b = 1000 * (500-a) / (1000-a)
  c = sqrt(a**2 + b**2)
  if int(floor(c)) == c and 0 < a < b < c:
    print a, b, c

如果您愿意,可以将c投射到int,但如果它以某种方式不起作用,则会掩盖它。

答案 1 :(得分:2)

  def tripletSumsMyDValue(triplets):
    for tri in triplets:
      a,b,c = tri[0],tri[1],tri[2]
      if d == int(a + b + c):
        return tri
      else:
        return None

对于小组中的每个三元组,如果它是答案,则将其返回;如果不是,则返回None。这意味着你不会在每组中的第一个之后查看任何三元组。答案显示在第150组中,其中包含加起来为22952, 11704, 7956, 4960, 4212, 2968, 2720, 1980, 1736, 1400, 1260, 1040, 1000, 900, 880的三元组;由于22952不是1000,因此您失败了。

解决这个问题的最简单方法是删除整个else子句。如果你在循环过程中从不return tri,那么你将失去函数的末尾并默认返回None

然而,值得注意的是,即使使用此修复程序,您的代码也永远不会终止没有答案的数字,即使您我很确定您很快就会达到无法继续使用其他值的程度。 (没有仔细阅读,我不确定那个点在哪里;也许inc > d?)


作为旁注,您可以大量简化此代码。首先,如果tri总是有三个成员(它确实如此),则以下内容是等效的:

a,b,c = tri[0],tri[1], tri[2]
a,b,c = tri

或者您甚至可以for a,b,c in triplets:。或者,甚至更简单:

for tri in triplets:
    if d == int(sum(tri)):
        return tri

答案 2 :(得分:1)

由于c = 1000 - a - b,也可以遵循这个更短更快的Python代码:

# i = a, k = b, j = c
for i in range (1 , 1000):
for k in range (i + 1 , 1000):
    j = 1000 - i - k
    if (i ** 2 + k ** 2 == j ** 2) & (i + j + k == 1000):
        print (i * k * j)
        break

答案 3 :(得分:0)

您可以按照以下简单方式编写程序:

#function to find the pythagorean triplet
#Using Dickson formula
def pythagorean_triplet_dickson():
 for r in range(1,1000):
  for s in range(1,r):
   if ((r**2)/2)%s == 0:
    t = (r**2/2)/s
    if r+s+r+t+r+t+s == 1000:
     return (r+s)*(r+t)*(r+t+s)

#Printing the result
print pythagorean_triplet_dickson()

与Dickson方法相比,您还可以使用具有更好执行时间的m, n formula。查看源代码以查看该程序:)

答案 4 :(得分:0)

import time
start = time.time()
def isok():
    for a in range(100,1000):
        for b in range(100,1000):
            for c in range(100,1000):
                if a + b + c == 1000 and a ** 2 + b ** 2 == c **2:
                    print("a is {} b is {} c is {}".format(a,b,c))
                    print("anwser is",a * b * c)
                    return True
print(isok())
elapsed = (time.time() - start)
print("This code took: " + str(elapsed) + " seconds")

答案 5 :(得分:0)

for i in range(1,500):
    for j in range(1,500):
        c = (i ** 2 + j ** 2) ** 0.5
        if i+j+c ==1000:
            print(i*j*c)`

我猜这是最短的代码。我使用500是因为斜边必须大于其他边,并且简单的数学运算:D