代码大战。整数:娱乐一。我有错误“执行超时”

时间:2019-05-21 09:05:10

标签: python python-3.x algorithm

我尝试通过代码战来完成任务。

42的除数为:1, 2, 3, 6, 7, 14, 21, 42。这些除数的平方为:1, 4, 9, 36, 49, 196, 441, 1764。平方和的平方和为2500,即50 * 50,平方!

给定两个整数m, n (1 <= m <= n),我们想找到mn之间的所有整数,它们的平方除数之和本身就是一个平方。 42就是这样的数字。

结果将是一个数组数组,每个子数组都有两个元素,首先是平方除数为平方的数字,然后是平方除数的总和。

示例:

list_squared(1, 250) --> [[1, 1], [42, 2500], [246, 84100]]

list_squared(42, 250) --> [[42, 2500], [246, 84100]]

上方是问题的讲师。

我的代码已经通过所有测试,但是有一个错误Execution Timed Out,也许我的代码没有经过优化。也许有人帮助我优化代码。

这是我的代码

import math
def list_squared(m, n):
    number = 0
    result = []
    if m==1:
        result.append([1,1])
    else:
        number = m
        squared_list(number)
        result += squared_list(number)

    for i in range(m + 1, n + 1):
        number = i
        squared_list(number)
        result += squared_list(number)

    return result

def squared_list(number):
    array, arrays = [], []
    x = sum([i*i for i in range(1, number//2+1)if not number%i]) + number*number

    if math.sqrt(x) % 1 == 0:
        array.append(number)
        array.append(x)
        arrays.append(array)

    return arrays

1 个答案:

答案 0 :(得分:3)

我们将任务分为两部分:计算除数平方和,并识别平方。

对于任务的前半部分,数论领域的除数函数sigma(k,n)返回 n <的除数的 k 次幂的和/ em>;如果 k = 0,则返回除数的计数:

def sigma(k, n):
  def add(s, p, m):
    if k == 0: return s*(m+1)
    s *= (p**(k*(m+1))-1)
    return s / (p**k-1)
  fs = factors(n)
  p,m,s = fs.pop(0),1,1
  while len(fs) > 0:
    f = fs.pop(0)
    if f <> p:
      s,p,m = add(s,p,m),f,1
    else: m += 1
  return add(s, p, m)

如果 n 很大,那比蛮力列表理解来计算除数,然后进行平方和求和要快得多。如果 n 不太大(最多约10 ** 12),则用2,3,5质数轮进行试验划分是查找 n 因素的简单算法。 em>:

def factors(n):
  wheel = [1,2,2,4,2,4,2,4,6,2,6]
  w, f, fs = 0, 2, []
  while f * f <= n:
    while n % f == 0:
      fs.append(f); n /= f
    f, w = f + wheel[w], w + 1
    if w == 11: w = 3
  if n == 1: return fs
  else: return fs + [n]

因此,factors(42)返回[2,3,7],而sigma(2,42)返回2500。如果您的 n 较大,则需要更好的分解算法。

问题的另一半是识别正方形。您可以使用内置的sqrt函数,但是该函数适用于浮点数,因此不精确,因此您可能需要更好的东西。艾萨克·牛顿(Isaac Newton)给出了一种通过求导的逐次逼近的方法来计算整数平方根的算法:

def isqrt(n):
    x = n; y = (x + 1) // 2
    while y < x:
        x=y; y=(x+n//x)//2
    return x

此函数返回 x ²≤ n 的最大 x 。为了识别平方,我们可以计算平方根,然后乘以检查数字是否为平方,但是计算平方根很昂贵,因此在执行平方根计算之前必须过滤掉明显的非平方:

def isSquare(n):
  if 33751571 >> (n % 32) & 1 == 0 \
  or 38348435 >> (n % 27) & 1 == 0 \
  or 19483219 >> (n % 25) & 1 == 0 \
  or   199411 >> (n % 19) & 1 == 0 \
  or   107287 >> (n % 17) & 1 == 0 \
  or     5659 >> (n % 13) & 1 == 0 \
  or      571 >> (n % 11) & 1 == 0 \
  or       23 >> (n %  7) & 1 == 0 :
    return False
  s = isqrt(n)
  if s * s == n: return s
  return False

魔术数是一组bloom过滤器,它们丢弃不是二次余数的数字。总体而言,他们在进行昂贵的平方根计算之前会确定99.82%的非平方。布隆过滤器由以下程序计算:

def q(n):
from sets import Set
s, sum = Set(), 0
for x in xrange(0,n):
  t = pow(x,2,n)
  if t not in s:
    s.add(t)
    sum += pow(2,t)
return sum

因此,例如q(32) = 33751571,二次残差(在函数内部计算的集合 s )为0、1、4、9、16、17和25。只有7/32 = 21.875%的非平方数可以通过测试。结合其他测试,只有0.18%的非平方数没有被其中一个过滤器捕获,因此对昂贵的平方根函数的大多数调用只是在确认该数是否为平方。

所有这些,很容易执行所需的任务:

def task(m,n):
  result = []
  for x in xrange(m,n):
    if x == 1:
      result.append([1,1])
    else:
      s = sigma(2,x)
      if isSquare(s):
        result.append([x,s])
  return result

请注意,1需要特殊处理,因为1没有素数因子(既不是素数也不是复合数)。对于 m = 1, n = 250,我们得到:

>>> task(1,250)
[[1, 1], [42, 2500], [246,84100]]

多么有趣的任务!它需要一点数字理论(sigma函数)和一些聪明的编码(bloom过滤器)来创建足够快的程序。它在OEIS中显示为序列A046655,在Euler项目中显示为Problem 211。您可以在Ideone上运行程序。

顺便说一句,如果 m n 之间的差异很大,则不必筛选每个数字,而是可以通过筛选找到这些因素。我给出了一个程序来筛选my blog处数字范围的最小素数;您可以修改该程序以收集所有因素,而不仅仅是最小因素。