在较大的迭代值时更高效的 python for 循环(3 个冗余循环)
More efficient python for loop (3 redundant loops) at large values of iterations
我正在尝试实现以下代码:
def foo(n, p):
for i in range(1,n):
for j in range(1,n):
for k in range(1,n):
if ((n-j)*i*k)==(j*(n-i)*(n-k)):
p=p-11
但是 n 将接近 10^10 的值,这使得这非常低效。事实上,即使当 n=1000 时,这也很慢。
有没有一种方法可以通过压缩 for 循环来加快速度,或者有没有一种方法可以完全不使用 for 循环?
操纵(n-j)*i*k=j*(n-i)*(n-k)
。我们有 j=n/(((n-i)*(n-k))/(i*k) + 1)
并且 j 应该是 1 到 n-1 之间的整数,所以:
def foo(n, p):
for i in range(1,n):
for k in range(1,n):
j=n/(((n-i)*(n-k))/(i*k) + 1)
if n%(((n-i)*(n-k))/(i*k) + 1) == 0 and j > 0 and j < n:
p=p-11
这将复杂度从 O(n³) 降低到 O(n²)
我将采用数学方法与计算机科学方法。减少那些 for 循环显然有一些有趣的问题,但数学方法可能会让你得到几乎相同的东西,但有一个小错误。
我想知道这个序列是否有一个封闭形式的公式,因为它总是比任何循环都快!在你提供的OEISlink中,FORMULA下有人提供了"empirical"生成函数
x*(1+5*x+11*x^2+x^3+6*x^4)/(1-x)^3/(1+x)^2
我稍后会谈到 "empirical" 部分。但是因为这是多项式的比率,如果您了解生成函数的工作原理,就很容易得到一个封闭形式的解决方案。如果这种方法最终成为您喜欢的东西,我可以将代数添加到我的答案中,但现在,让我们直接切入公式:
def empirical(n):
return ((-1)**n * (-1.5*n + 2.5)) + \
(3.0*n**2 - 4.5*n + 3.5)
非常简洁明了。这有多准确?好吧,我检查了前 500 个值。这两个函数通常完美地对齐,但有时 empirical
夸大了真实的顺序:
correct empirical pct_diff
1 1 1.0 0.000000
2 6 6.0 0.000000
3 19 19.0 0.000000
4 30 30.0 0.000000
5 61 61.0 0.000000
6 78 78.0 0.000000
7 127 127.0 0.000000
8 150 150.0 0.000000
9 217 217.0 0.000000
10 246 246.0 0.000000
11 331 331.0 0.000000
12 366 366.0 0.000000
13 469 469.0 0.000000
14 510 510.0 0.000000
15 625 631.0 0.009600*
16 678 678.0 0.000000
17 817 817.0 0.000000
18 870 870.0 0.000000
19 1027 1027.0 0.000000
20 1080 1086.0 0.005556*
21 1261 1261.0 0.000000
22 1326 1326.0 0.000000
偶尔的差异几乎总是小于 1%。现在,我不能保证这种模式会持续 n = 10**10
(即,经验几乎总是正确的,偶尔会有轻微的夸大),但请查看 OEIS 页面上的另一条评论:
Ceva's Theorem is used to deduct vanishing regions from the naive count. The first deduction is at n=15 for n odd and n=20 for n even.
15和20恰好是empirical
的第一个分歧!所以看起来经验生成函数在大多数时候都是正确的("naive count"?),但在某些地方当必须进行推论时它是一个上限。这进入了特定领域的领域,我对 Ceva 定理的了解还不够,无法确切地了解何时以及如何进行这些推论——所以我担心我无法改进这个封闭形式的上限,因为我有它以上。
你原来的问题是想测试10**10。所以现在立即int(empirical(10**10))
:
299999999939999956992
这要么是完全正确的,要么是一个非常非常接近真实答案的上限。
我知道这是一个 "alternative" 的解决方案,但希望它是一个信息转移。这就像有人要你找到第 (10**10) 个斐波那契数。您可以使用循环,但如果存在闭式公式,请使用它!
我正在尝试实现以下代码:
def foo(n, p):
for i in range(1,n):
for j in range(1,n):
for k in range(1,n):
if ((n-j)*i*k)==(j*(n-i)*(n-k)):
p=p-11
但是 n 将接近 10^10 的值,这使得这非常低效。事实上,即使当 n=1000 时,这也很慢。
有没有一种方法可以通过压缩 for 循环来加快速度,或者有没有一种方法可以完全不使用 for 循环?
操纵(n-j)*i*k=j*(n-i)*(n-k)
。我们有 j=n/(((n-i)*(n-k))/(i*k) + 1)
并且 j 应该是 1 到 n-1 之间的整数,所以:
def foo(n, p):
for i in range(1,n):
for k in range(1,n):
j=n/(((n-i)*(n-k))/(i*k) + 1)
if n%(((n-i)*(n-k))/(i*k) + 1) == 0 and j > 0 and j < n:
p=p-11
这将复杂度从 O(n³) 降低到 O(n²)
我将采用数学方法与计算机科学方法。减少那些 for 循环显然有一些有趣的问题,但数学方法可能会让你得到几乎相同的东西,但有一个小错误。
我想知道这个序列是否有一个封闭形式的公式,因为它总是比任何循环都快!在你提供的OEISlink中,FORMULA下有人提供了"empirical"生成函数
x*(1+5*x+11*x^2+x^3+6*x^4)/(1-x)^3/(1+x)^2
我稍后会谈到 "empirical" 部分。但是因为这是多项式的比率,如果您了解生成函数的工作原理,就很容易得到一个封闭形式的解决方案。如果这种方法最终成为您喜欢的东西,我可以将代数添加到我的答案中,但现在,让我们直接切入公式:
def empirical(n):
return ((-1)**n * (-1.5*n + 2.5)) + \
(3.0*n**2 - 4.5*n + 3.5)
非常简洁明了。这有多准确?好吧,我检查了前 500 个值。这两个函数通常完美地对齐,但有时 empirical
夸大了真实的顺序:
correct empirical pct_diff
1 1 1.0 0.000000
2 6 6.0 0.000000
3 19 19.0 0.000000
4 30 30.0 0.000000
5 61 61.0 0.000000
6 78 78.0 0.000000
7 127 127.0 0.000000
8 150 150.0 0.000000
9 217 217.0 0.000000
10 246 246.0 0.000000
11 331 331.0 0.000000
12 366 366.0 0.000000
13 469 469.0 0.000000
14 510 510.0 0.000000
15 625 631.0 0.009600*
16 678 678.0 0.000000
17 817 817.0 0.000000
18 870 870.0 0.000000
19 1027 1027.0 0.000000
20 1080 1086.0 0.005556*
21 1261 1261.0 0.000000
22 1326 1326.0 0.000000
偶尔的差异几乎总是小于 1%。现在,我不能保证这种模式会持续 n = 10**10
(即,经验几乎总是正确的,偶尔会有轻微的夸大),但请查看 OEIS 页面上的另一条评论:
Ceva's Theorem is used to deduct vanishing regions from the naive count. The first deduction is at n=15 for n odd and n=20 for n even.
15和20恰好是empirical
的第一个分歧!所以看起来经验生成函数在大多数时候都是正确的("naive count"?),但在某些地方当必须进行推论时它是一个上限。这进入了特定领域的领域,我对 Ceva 定理的了解还不够,无法确切地了解何时以及如何进行这些推论——所以我担心我无法改进这个封闭形式的上限,因为我有它以上。
你原来的问题是想测试10**10。所以现在立即int(empirical(10**10))
:
299999999939999956992
这要么是完全正确的,要么是一个非常非常接近真实答案的上限。
我知道这是一个 "alternative" 的解决方案,但希望它是一个信息转移。这就像有人要你找到第 (10**10) 个斐波那契数。您可以使用循环,但如果存在闭式公式,请使用它!