繁体   English   中英

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

[英]Why isn't my solution to Project Euler 9 working?

问题9:

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

a 2 + b 2 = c 2

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

精确存在一个毕达哥拉斯三联体,其中a + b + c = 1000 找到产品abc。

我的方法是生成三元组,然后检查它们是否满足a + b + c = d 为此,我采用Dickson的方法来生成勾股三胞胎(如Wikipedia所述的Dickson方法:要找到x 2 + y 2 = z 2整数解,请找到正整数rstr 2 = 2 * s * t是一个平方,则: x = r + s; y = r + tz = r + s + t从中我们可以看到r是任意偶数整数,并且str 2 / 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)后停止(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

有一种简单的方法可以做到这一点。

如果您不去研究编码,而是看数学,那么可以大大简化问题。

我们有两个方程;

a 2 + b 2 = c 2

a + b + c = 1000

因此,将一个替换为另一个:

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

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

我们还知道abc<1000 因此,我们可以获得满足该方程式的ab的所有组合的列表,然后简单地检查sqrt(a**2 + b**2)是什么。

您可以在单个(很丑陋,但不是很Python的)列表理解中完成此操作,并且只需要一秒钟。

[[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中创建列表的快速方法。 通过列表理解创建的任何列表始终可以使用循环以更清晰的方式创建。 您可以在此处阅读有关它们的更多信息

这是一个简单的例子:

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 = 1000 2 - 2000a - 2000b - 2ab

可以改写给予b来讲a

a = 1000 * 500-b / 1000-a

combinations(range(1,1000), 2)将给出一百万个条目的列表。 它按n 2缩放。 通过编写b来讲a ,也是唯一遍历range(1,1000)一次,你把它降低到O(N),从O( n 2 )。

相反,您可以执行以下操作:

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 ,但是如果它由于某种原因不起作用,则将其屏蔽。

  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 a,b,c in triplets: 或者,甚至更简单:

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

由于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

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

#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()

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

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")
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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM