我尝试通过代码战来完成任务。
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)
,我们想找到m
和n
之间的所有整数,它们的平方除数之和本身就是一个平方。 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
答案 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处数字范围的最小素数;您可以修改该程序以收集所有因素,而不仅仅是最小因素。