简体   繁体   English

我怎样才能使这个完美平方和函数更有效?

[英]How can I make this sum of perfect squares function more efficient?

Part of my homework involves making a function which checks if a number is a sum of two squares.我的部分作业涉及制作一个函数来检查一个数字是否是两个平方之和。 Problem is that it takes a very long time for larger numbers to run through it.问题是较大的数字需要很长时间才能通过它。 Any suggestions on how to make this more efficient?关于如何提高效率的任何建议? for example, given the number 50 it would return (7,1) since 7^2 is 49 and 1^2 is 1 so the total would be 50 Here's the code:例如,给定数字50它会返回(7,1)因为 7^2 是 49 并且 1^2 是 1 所以总数是 50 这是代码:

def sum_of_squares(n) : 
  i = 1 

  while i * i <= n : 
      j = 1

      while(j * j <= n) : 

          while (i * i + j * j == n) : 

              return (j,i)

          j = j + 1
      i = i + 1
def sum_of_squares(n) :
  range = round(math.sqrt(n)) 
  i = 1 
  while i <= round(range/2) : 
      j = range;

      while(j >= round(range/2)) : 

          while (i * i + j * j == n) : 

              return (j,i)

          j = j - 1
      i = i + 1

A modification of algorithm from https://www.geeksforgeeks.org/check-whether-number-can-represented-sum-two-squares/来自https://www.geeksforgeeks.org/check-whether-number-can-represented-sum-two-squares/的算法修改

  1. Method 1--saves squares in a dictionary for later lookup方法1--将正方形保存在字典中供以后查找
  2. Method 2--computes integer square root using binary search方法2--使用二分查找计算整数平方根
  3. Method 3--checks if the number is a perfect square by using math.sqrt function方法 3--使用 math.sqrt 函数检查数字是否为完全平方数

Method 1方法一

The technique is basically to loop through numbers from 0 to sqrt(n), storing the squares i * i as dictionary key, with i as value.该技术基本上是循环从 0 到 sqrt(n) 的数字,将平方 i * i 存储为字典键,将 i 作为值。

If number (n - i * i) already in the dictionary, then you have found a pair (i & d[n - i * i].如果数字 (n - i * i) 已经在字典中,那么你已经找到了一对 (i & d[n - i * i].

Returns the solution with the max tuple value.返回具有最大元组值的解决方案。

Algorithm complexity O(sqrt(n)).算法复杂度 O(sqrt(n))。

def sum_square(n): 
  d = {}
  maxi = None
  for i in range(n): 
    if i * i > n: 
      break
      # store squared value with value in dictionary 
    d[i*i] = i  # saving the square root of i*i as i

    try:
      k = d[n - i*i]      # check if n- i*i is already in a key
      if maxi:
        maxi = max(maxi, (i, k))    # update max solution
      else:
        maxi = (i, k)               # first solution
    except:
      continue

  return maxi

Method 2方法二

def sum_square_binary(n): 
  def binary_search(start_, end_, val): 
    # If lower limit exceeds   
    # upper limit.  
    if start_ > end_: 
        return None 

    # Calculating mid.  
    mid = start_ + (end_ - start_) // 2 

    if mid * mid == val:
        return mid 

    if mid * mid > val: 
        return binary_search(start_, mid - 1, val)

    return binary_search(mid + 1, end_, val)

  maxi = None

  for i in range(n):
      if i*i > n:
          break

      b = n - i * i 

      # Use binary search to see if b is a perfect square
      # only need to check range [0, b] in binary search
      k = binary_search(0, b, b)  # k is the root of b (if perfect square)
      if k:
          maxi = max(maxi, (i, k), key = max) if maxi else (i, k)

  return (max(maxi), min(maxi)) if maxi else maxi

Method 3方法三

import math

def sum_square_sqrt(n):
  def integer_sqrt(x): 
      """ Returns sqrt of integer if it is a perfect square.  
          Uses technique from Cook https://www.johndcook.com/blog/2008/11/17/fast-way-to-test-whether-a-number-is-a-square/ to reduce the number of times sqrt is called """
      # Find the floating point value of  
      # square root of x. 
      h = x & 0xF
      if h > 9:
          return None
      if ( h != 2 and h != 3 and h != 5 and h != 6 and h != 7 and h != 8 ):
          sr = math.sqrt(x) 

          # If square root is an integer 
          return int(sr) if ((sr - math.floor(sr)) == 0) else None
      else:
          return None

  maxi = None

  for i in range(n):
      if i*i > n:
          break

      b = n - i * i 

      # Use binary search to see if b is a perfect square
      # only need to check range [0, b] in binary search
      k = integer_sqrt(b)  # k is the root of b (if perfect square)
      if k:
          maxi = max(maxi, (i, k), key = max) if maxi else (i, k)

  return (max(maxi), min(maxi)) if maxi else maxi

Performance表现

Method 1 using a dictionary is much faster, with Method 3 comparable.使用字典的方法 1 要快得多,与方法 3 相当。 Method 3 should be okay until the sqrt becomes inaccurate as a test for larger n.方法 3 应该没问题,直到 sqrt 作为对较大 n 的测试变得不准确。

tst = [randint(1e6, 1e9) for _ in range(10)]

%timeit for k in tst: sum_square(k)

>>>Method 1: 180 ms ± 14.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) >>>方法 1:每个循环 180 ms ± 14.7 ms(7 次运行的平均值 ± 标准偏差,每个循环 1 次)

%timeit for k in tst: sum_square_binary(k)

>>>Method 2: 4.04 s ± 97.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) >>>方法 2:每个循环 4.04 s ± 97.1 ms(7 次运行的平均值 ± 标准偏差,每个循环 1 次)

%timeit for k in tst: sum_square_sqrt(k)

>>>Method 3: 192 ms ± 5.66 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) >>>方法 3:每个循环 192 ms ± 5.66 ms(平均值 ± 标准偏差,7 次运行,每次 10 次循环)

You could use dynamic programming, to prevent squares from being recalculated over and over.您可以使用动态编程,以防止反复重新计算平方。 By using a keeping track of a list of outcomes you can just look up the value.通过使用跟踪结果列表,您可以只查找值。 If the value is not defined yet, you just calculate it once, if it has been calculated before, you just return the corresponding entry in the list instead of recalculating:如果该值还没有定义,你只需计算一次,如果之前已经计算过,你只需返回列表中的相应条目,而不是重新计算:

squares = []

def get_square(x):
    global squares

    if(squares[x] == -1):
        squares[x] = x * x

    return squares[x]

def sum_of_squares(n):
    global squares
    squares = [-1 for i in range(n)]

    i = 1
    while get_square(i) <= n - 1: 
        j = 1
        while(get_square(j) <= n - 1) :
            if(get_square(i) + get_square(j) == n) :
                return (j,i)
            j = j + 1
        i = i + 1

print(sum_of_squares(25))

As some others suggested above, preventing the computation of squares in each iteration is a good start.正如上面其他人所建议的,在每次迭代中阻止计算平方是一个好的开始。 Here is a simple implementation using for loops :这是一个使用 for 循环的简单实现:

def sum_of_squares(n):
     lim = int(n**0.5)+1
     for i in range(1, lim):
             for j in range(1,i):
                     if(i**2 + j**2==n):
                             return (i,j)

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

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