我正在尝试用PHP解决Project Euler并在while循环中遇到my循环条件的问题。有人能指出我走向正确的方向吗?我在这里走在正确的轨道上吗?
问题,顺便说一句,是找到低于2,000,000的所有素数的总和
其他说明:我遇到的问题是,它似乎是一个记忆猪,除了实施筛子,我不知道如何处理这个问题。所以,我想知道我是否在实施中做错了。
<?php
// The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
// Additional information:
// Sum below 100: 1060
// 1000: 76127
// (for testing)
// Find the sum of all the primes below 2,000,000.
// First, let's set n = 2 mill or the number we wish to find
// the primes under.
$n = 2000000;
// Then, let's set p = 2, the first prime number.
$p = 2;
// Now, let's create a list of all numbers from p to n.
$list = range($p, $n);
// Now the loop for Sieve of Eratosthenes.
// Also, let $i = 0 for a counter.
$i = 0;
while($p*$p < $n)
{
// Strike off all multiples of p less than or equal to n
for($k=0; $k < $n; $k++)
{
if($list[$k] % $p == 0)
{
unset($list[$k]);
}
}
// Re-initialize array
sort ($list);
// Find first number on list after p. Let that equal p.
$i = $i + 1;
$p = $list[$i];
}
echo array_sum($list);
?>
答案 0 :(得分:2)
您可以对中间循环进行重大优化。
for($k=0; $k < $n; $k++)
{
if($list[$k] % $p == 0)
{
unset($list[$k]);
}
}
以2 * p开头并以$ p而不是1递增。这消除了对可分性检查的需要以及减少总迭代次数。
for($k=2*$p; $k < $n; $k += $p)
{
if (isset($list[k])) unset($list[$k]); //thanks matchu!
}
上面的建议只检查开始时的赔率(2除外)也是一个好主意,虽然因为内循环从来没有为这些情况起作用,我认为它不重要。我也忍不住认为这些不合理是低效的,所以我不是百分之百确定。
这是我的解决方案,使用'boolean'数组作为素数,而不是实际删除元素。我喜欢使用地图,过滤器,缩小和填充,但我认为id很接近你所做的事情,这可能会更有效(尽管更长)。
$top = 20000000;
$plist = array_fill(2,$top,1);
for ($a = 2 ; $a <= sqrt($top)+1; $a++)
{
if ($plist[$a] == 1)
for ($b = ($a+$a) ; $b <= $top; $b+=$a)
{
$plist[$b] = 0;
}
}
$sum = 0;
foreach ($plist as $k=>$v)
{
$sum += $k*$v;
}
echo $sum;
当我为项目euler做这个时,我使用了python,就像我最常做的那样。但是那个使用PHP的人和我声称的那个人在7秒钟内运行它(第2页的SekaiAi,对于那些可以看的人)。我并不真正关心他的形式(将for循环的主体放入其增量子句!),或者使用全局变量和他所拥有的函数,但主要观点都在那里。我测试PHP的便捷方法是通过VMWareFusion本地计算机上的服务器运行,因此它速度慢,无法真实地评论经验。
答案 1 :(得分:1)
我已经将代码运行到了运行的位置,并传递了一些小例子(例如17)。但是,它已经过了8分钟左右,它仍然在我的机器上运行。我怀疑这个算法虽然简单,但可能不是最有效的,因为它必须经常运行很多次数。 (第一次运行200万次测试,下次运行100万次,随着时间的推移他们开始越来越少。)它还使用了大量内存,因为你知道存储数百万的列表整数。
无论如何,这是我的代码的最终副本,其中列出了我所做的更改以及原因。我不确定它是否适用于2,000,000,但我们会看到。
编辑:它找到了正确答案!耶!
memory_limit
设置为-1
以允许PHP为这个非常特殊的情况获取尽可能多的内存(在生产脚本中非常非常糟糕!)%
代替mod
$j
作为内循环。$j
$i + 1
$arr
代替$list
;)$
,因此PHP会将$list[j]
解释为$list['j']
。只是一个错字。我认为这就是我所做的一切。我用一些进度输出运行它,到现在达到的最高素数是599,所以我会告诉你它是怎么回事:)
我在Ruby中针对此问题的策略只是检查n
下的每个数字是否为素数,循环遍历2
和floor(sqrt(n))
。它也可能不是最佳解决方案,需要一段时间才能执行,但只需要一两分钟左右。这可能是算法,或者可能只是Ruby在这种工作上比PHP更好:/
最终代码:
<?php
ini_set('memory_limit', -1);
// The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
// Additional information:
// Sum below 100: 1060
// 1000: 76127
// (for testing)
// Find the sum of all the primes below 2,000,000.
// First, let's set n = 2 mill or the number we wish to find
// the primes under.
$n = 2000000;
// Then, let's set p = 2, the first prime number.
$p = 2;
// Now, let's create a list of all numbers from p to n.
$list = range($p, $n);
// Now the loop for Sieve of Eratosthenes.
// Also, let $i = 0 for a counter.
$i = 0;
while($p*$p < $n)
{
// Strike off all multiples of p less than or equal to n
for($j=$i+1; $j < $n; $j++)
{
if($list[$j] % $p == 0)
{
unset($list[$j]);
}
}
// Re-initialize array
sort ($list);
// Find first number on list after p. Let that equal p.
$i = $i + 1;
$p = $list[$i];
echo "$i: $p\n";
}
echo array_sum($list);
?>