项目欧拉||问题10

时间:2010-12-13 01:41:04

标签: php

我正在尝试用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);

?>

2 个答案:

答案 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为这个非常特殊的情况获取尽可能多的内存(在生产脚本中非常非常糟糕!)
  • 在PHP中,使用%代替mod
  • 内圈和外圈不能使用相同的变量; PHP认为它们具有相同的范围。也许,使用$j作为内循环。
  • 为避免在内循环中关闭素数触发,请在$j
  • 处开始$i + 1
  • 在未设置的情况下,您使用$arr代替$list;)
  • 您在未设置时错过了$,因此PHP会将$list[j]解释为$list['j']。只是一个错字。

我认为这就是我所做的一切。我用一些进度输出运行它,到现在达到的最高素数是599,所以我会告诉你它是怎么回事:)

我在Ruby中针对此问题的策略只是检查n下的每个数字是否为素数,循环遍历2floor(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);

    ?>