我想从Project Euler解决问题(BTW,问题25),我在Python中找到了一个解决方案:
fibonacci = 1
old1 = 0
old2 = 1
limit = 1000
i = 1
while len(str(fibonacci)) < limit:
fibonacci = old1 + old2
old1 = old2
old2 = fibonacci
i = i + 1
print(i)
计算需要1.5秒。
我在PHP中实现了相同的代码:
$fibonacci = 1;
$old1 = 0;
$old2 = 1;
$limit = 1000;
$i = 1;
while (strlen((string)$fibonacci) < $limit){
$fibonacci = $old1 + $old2;
$old1 = $old2;
$old2 = $fibonacci;
$i = $i + 1;
}
print($i);
花了30多分钟,还在计算......
我知道Python被认为比PHP更快,但它仍然不应该是那么大的差异。如果有办法,如何改进我的PHP代码以更快地获得结果?
修改
我根据下面的评论编辑这篇文章,所以首先我的解决方案不起作用。 一个解决方案可以是旧的而不是旧的:
while (strlen(number_format($fibonacci, 0, '', '')) < $limit){ ... }
但这又是一个很大的速度问题。
所以最终的解决方案是使用BCMath:
$fibonacci = '1';
$old1 = '0';
$old2 = '1';
$limit = 1000;
$i = 1;
while (strlen($fibonacci) < $limit){
$fibonacci = bcadd($old1, $old2);
$old1 = $old2;
$old2 = $fibonacci;
$i = $i + 1;
}
echo $fibonacci . "<br />";
print($i);
因此,您可以以与PHP中的Python相同的速度获得结果。
答案 0 :(得分:12)
当然,PHP正在进入无限循环。如果没有错误,那就不可能花那么长的时间......
我不认为用strlen
计算这些数字的数字将在PHP中起作用。 PHP处理科学记数法中的数字,精度低于Python。
我向PHP添加了调试echo
语句,为每一步打印出$ fibonacci和$ i。
典型的Python行似乎是
fib is 7540113804746346429
i is 92
在PHP中,那是
fib is 7.54011380475E+18
i is 92
要在PHP中完成此任务,您可能需要使用更高精度的数学库。
查看http://www.php.net/manual/en/book.bc.php - 您可以使用bcadd
函数完成添加,它将像在Python中一样工作。
答案 1 :(得分:6)
这不是速度问题,它是终止条件的逻辑问题。
可能不会完成。当您将$ fibonacci的当前值转换为while测试中的字符串时,当您将其转换为字符串时,它将转换为科学格式并截断为一组有限的小数位(取决于您的精度设置)。该位数将远远小于1000,因此永远不会满足while终止条件。
答案 2 :(得分:3)
问题是,你正在处理大数字。您应该使用BC数学函数(php.net/bc)。所以你的代码可以是:
$fibonacci = "1";
$old1 = "0";
$old2 = "1";
$limit = 1000;
$i = 1;
while (strlen($fibonacci) < $limit){
$fibonacci = bcadd($old1, $old2);
$old1 = $old2;
$old2 = $fibonacci;
$i = $i + 1;
}
print($i);
我已经尝试过,大概需要0.095秒。
答案 3 :(得分:2)
由于问题似乎是转换为字符串,因此这是一种更快的方法,不需要它。这基本上与你发布的算法相同(所以我觉得你没有感觉不好),但演示了如何使用除法来测试整数的长度,而不是将其转换为字符串。
def fibonacci_digits(limit):
limit = 10**limit
fib = 1
old1 = 0
old2 = 1
i = 1
size = 1
while size < limit:
fib = old1 + old2
if not size//fib: # // is pythons integer division operator, not a comment
size *= 10
old1 = old2
old2 = fib
i += 1
return i
print fibonacci_digits(1000)
转换为字符串很慢,几乎不是正确的事情。这是时间结果:
$ python -mtimeit -s'import fib' 'fib.fibonacci_digits(1000)'
10 loops, best of 3: 30.2 msec per loop
$ python -mtimeit -s'import fib' 'fib.fibonacci_digits2(1000)'
10 loops, best of 3: 1.41 sec per loop
答案 4 :(得分:2)
许多项目的Euler问题都必须处理大数字。
PHP将使您的大数字看起来像2.579234678963E+12
,这是数字的指数表示......显然很难使用。
因此,对于大多数问题,最好使用BCMath Functions。这将使您的号码保持原样,即使它是一个巨大的数字。
请注意,使用echo bcmul(500,500);
永远不会像echo 500*500
那么快。并且,BCMath函数返回值始终是字符串。
要解决您的问题,请使用相应的BCMath函数替换所有算术运算。
答案 5 :(得分:1)
我优化了Python代码。使用len(str())来检查位数非常慢。由math.log10替换,以更快的速度运行程序
Fibonacci序列中包含1000位数的第一项是:4782 以0.008573秒计算
import time
from math import log10
def digits(n): # Return the number of digits for n>=1
return int(log10(n))+1
fibonacci = 1L # Thanks to Python to handle very big numbers
old1 = 0
old2 = 1
limit = 1000
i = 1
start = time.time() #Start timer for bench
while digits(fibonacci) < limit:
fibonacci = old1 + old2
old1 = old2
old2 = fibonacci
i += 1
print "The first term in the Fibonacci sequence to contain %s digits is : %s" % (str(limit), str(i))
print "Calculated in %3.6f seconds" % (time.time() - start)