“如何衡量效率?”
测量经验增长顺序,就是这样!:)
例如,使用来自已接受答案、log_10(50.41/0.9) = 1.75
和的数据log_10(2.31/0.21) = 1.04
,因此它是~ n^1.75
您的 TD(试验部门)代码(在 1,000 ... 10,000 范围内)与~ n^1.04
Eratosthenes 筛子的经验增长顺序。
其中,后者与 一致n log log n
,而前者与一致n^2 / log(n)^2
。
有了g(n) = n^2 / log(n)^2
,我们有了g(10000)/g(1000) = 56.25
,仅比经验值低 0.4% 50.41/0.9 = 56.01
。但是g2(n) = n^2 / log(n)
,我们有g2(10000)/g2(1000) = 75
,这与证据相去甚远。
关于时间复杂度:实际上,大多数复合都提前失败(是小素数的倍数)。在这里产生k=n/log(n)
素数需要时间,通过所有O(k^2)
前面的素数来测试每个素数,而不是那些不超过其平方根的素数。
复合材料不会增加复杂性(M. ONeill 的 JFP 文章(第 4 页)中的精确分析给出了复合材料与素数测试时相同的复杂性sqrt
- 每个复合材料都保证有一个不大于其的素数因子sqrt
- 所以复合材料的复杂性甚至低于这里素数的复杂性)。
所以总体来说O(k^2)
是 ,也就是说,O(n^2/log(n)^2)
。
通过仅添加两行代码,您可以显着提高代码的速度,包括O(n^2/log(n)^2)
时间O(n^1.5/log(n)^2)
复杂度:
for j in ans:
if j*j > i:
ans.append(i); break;
if i % j == 0:
break
# else:
# ans.append(i)
17.8x
时间复杂度的提高意味着10,000 比 1,000的运行时间比率,而不是56.25x
像以前那样。这转化~ n^1.25
为该范围内的经验增长顺序(而不是~ n^1.75
)。10,000 次调用的绝对运行时间将比旧的 50.41 秒更接近 2.31 秒。
顺便说一句,您的原始代码相当于大卫特纳的著名代码,
primes = sieve [2..]
sieve (x:xs) = x : sieve [y | y <- xs, rem y x /= 0]
和改进的代码,对此:
primes = 2 : sieve [3..] primes
sieve xs (p:ps) | (h,t) <- span (< p*p) xs =
h ++ sieve [y | y <- t, rem y p /= 0] ps
(代码在 Haskell 中,我认为它具有足够的可读性。x:xs
代表带有x
头部元素和xs
列表其余部分的列表)。