Code Freezes Half Way Through - PHP - php

I have this relatively complex combinations and permutations code that I have to execute in CLI. The code takes a number as command line parameter and then outputs a list of all permutations of all unique combinations. The arrays are strings of numbers separated by a space.
The code works nice if N is 6 or less. Even 7. However when I pass n=8 the code simply freezes,, it stops and doesn't move on.
How can I fix this so that N can be 8.
N will never be larger than 8, BUT the code must be able to execute with 8.
Here is the code
for ($i=0; count($list) < $nop; $i++) {
shuffle($array);
$tmp = implode(' ', $array);
if (!isset($list[$tmp])) {
$list[$tmp] = 1;
}}
Thanks for all advice in advance.

for ($i=0; count($list) < $nop; $i++) {
causes an infinite loop. Nowhere in the loop does the size of $list or $nop change so if count($list) < $nop is true, it'll be an infinite loop.

Related

Using number_format in a loop

I am running a for loop 10 times in order to populate data in a data table. In doing this, I wanted to use number_format in order to format the numbers. However, when I apply the number_format the For loop for some reason runs one additional time.
It works just fine when I exclude the number_format. Can anyone explain why this happens?
<?php
foreach($data['data'] as $result) {
For ($n = 0; $n <= 10; $n++){
echo "<td>";
echo number_format($result[$n], 0, ".", ",");
echo "</td>";
}
}
?>
TL;DR: Your loop will always run an additional time. Assuming that there are no errors in your number_format function call, all you have to do to get this to run 10 times is change your code to for($n = 0; $n < 10; n++). Note the use of < and not <=.
For loops are really just syntactical sugar for while loops. The statement for(initial_statement; bound_condition; loop_statement) { code; } is equivalent to
initial_statement;
while(bound_condition) {
code;
loop_statement;
}
Which, functionally, is equivalent to
initial_statement;
while(true) {
code;
loop_statement;
if(!bound_condition) break;
}
This means that if you want a loop to run, say, 2 times, and you write for($i = 0; $i <= 2; $i++) your code will loop as follows:
$i = 0
i++; (i now equals 1)
i <= 2 (condition is true, so continue)
$i = 1
i++; (i now equals 2)
i <= 2 (condition is true, so continue)
$i = 2
i++; (i now equals 3)
i <= 2 (condition is FALSE, so break)
Using the <= operator when your control variable starts at 0 causes an extra iteration to occur, since there are three integer values of i such that 0 <= i <= 2 (0, 1, and 2). To ensure that there are only two iterations, use the < operator, and now the loop will only be executed for values in the domain 0 <= i < 2 (0 and 1).
If you are still bent on using the <= operator and are fine with a non-zero-based iteration count, then you can simply change the initial value of i to 1 to offset the error.
By the way your code is written, I assume that you wish for your inner loop to run 10 times, not 11. This would explain why you are getting an extra iteration, and the issue is quite unrelated to the use of number_format. If you are only getting 10 iterations when you don't use that function, you might want to make sure that the statement 1 == 1 evaluates to true in your PHP interpreter.
Additionally, as a code styling issue, I would recommend using consistent case in your statements; you write foreach (lowercase) but also use For (uppercase). The convention is to use lowercase for both.
I have no clue why you would be only getting 10 iterations without number_format. You might be counting incorrectly? Try changing it to < and see if that resolves your issue.

Why is a sorted array slower than a non sorted array in PHP

I have the following script, and I know about the principle "Branch prediction" but it seems that's not the case here.
Why is it faster to process a sorted array than an unsorted array?
It seems to work the other way around.
When I run the following script without the sort($data) the script takes 193.23883700371 seconds to complete.
When I enable the sort($data) line the scripts takes 300.26129794121 seconds to complete.
Why is it so much slower in PHP? I used PHP 5.5 and 5.6.
In PHP 7 the script is faster when the sort() is not commented out.
<?php
$size = 32768;
$data = array_fill(0, $size, null);
for ($i = 0; $i < $size; $i++) {
$data[$i] = rand(0, 255);
}
// Improved performance when disabled
//sort($data);
$total = 0;
$start = microtime(true);
for ($i = 0; $i < 100000; $i++) {
for ($x = 0; $x < $size; $x++) {
if ($data[$x] >= 127) {
$total += $data[$x];
}
}
}
$end = microtime(true);
echo($end - $start);
Based on my comments above the solution is to either find or implement a sort function that moves the values so that memory remains contiguous and gives you the speedup, or push the values from the sorted array into a second array so that the new array has contiguous memory.
Assuming you MEANT to not time the actual sort, since your code doesn't time that action, it's difficult to assess any true performance difference because you've filled the array with random data. This means that one pass might have MANY more values greater than or equal to 127 (and thus running an additional command) then another pass. To really compare the two, fill your array with an identical set of fixed data. Otherwise, you'll never know if the random fill is causing the time differences you're seeing.

Why does my PHP code doesn't work anymore for no reason?

I have a for loop in my code. I haven't changed anything on this part of code for about 5-6 days and I never had problems with it.
Since yesterday I tried to reload my code and it allways gives me this error:
Maximum execution time of 30 seconds exceeded - in LogController.php line 270
Well I can't explain why but maybe someone of you could look over it.
This is my code around line 270.
$topten_sites = [];
for ($i = 0; $i <= count($sites_array); $i++) {
if ($i < 10) { // this is 270
$topten_sites[] = $sites_array[$i];
}
}
$topten_sites = collect($topten_sites)->sortByDesc('number')->all();
As I said, it worked perfectly, so why it gives me an error? If I uncomment these lines and every other line that contains the $topten_sites array, the code workes again.
This looks wrong:
for ($i = 0; $i <= $sites_array; $i++) {
if ($i < 10) { // this is 270
$topten_sites[] = $sites_array[$i];
}
}
If $sites_array is an array, it makes no sense to compare it to an integer so you probably have a never-ending loop.
If you just need the first 10 elements in another array, you can replace your loop with:
$topten_sites = array_slice($sites_array, 0, 10);
Why would You iterate entire array if You only want first 10 results?
for ($i = 0; $i < 10; $i++) {
$topten_sites[] = $sites_array[$i];
}
To answer the actual answer; code never stops working "for no reason". Code works or it doesn't, both for a reason. If it stops working something changed compared to your previous tests.
"Sometimes it works, sometimes it doesn't" falls in the same logic. Code will always behave exactly the same every time, just some of the parameters have changed, you have to find which one.
In your case, i'm guessing the entries in your array have increased. PHP and arrays aren't best friends when it comes to speed, arrays are slow. It could very well be that your array was
smaller when you tested it (wasn't probally the fastest to begin with), but now with the current amount it just hit the threshold of 30 seconds.
It could also be that a part of code before this bit of code takes a lot of time (say suddenly 28 seconds instead of 20), and your loop (which never changed) does it's job in the regular 3seconds it always does, now runs into problems
Use it like this:
$topten_sites = [];
for ($i = 0; $i <= 10; $i++) {
$topten_sites[] = $sites_array[$i];
}
$topten_sites = collect($topten_sites)->sortByDesc('number')->all();

Is PHP capable of caching count call inside loop?

I know the more efficient way to have a loop over array is a foreach, or to store count in a variable to avoid to call it multiple times.
But I am curious if PHP have some kind of "caching" stuff like:
for ($i=0; $i<count($myarray); $i++) { /* ... */ }
Does it have something similar and I am missing it, or it does not have anything and you should code:
$count=count($myarray);
for ($i=0; $i<$count; $i++) { /* ... */ }
PHP does exactly what you tell it to. The length of the array may change inside the loop, so it may be on purpose that you're calling count on each iteration. PHP doesn't try to infer what you mean here, and neither should it. Therefore the standard way to do this is:
for ($i = 0, $length = count($myarray); $i < $length; $i++)
PHP will execute the count each time the loop iterates. However, PHP does keep internal track of the array's size, so count is a relatively cheap operation. It's not as if PHP is literally counting each element in the array. But it's still not free.
Using a very simple 10 million item array doing a simple variable increment, I get 2.5 seconds for the in-loop count version, and 0.9 seconds for the count-before-loop. A fairly large difference, but not 'massive'.
edit: the code:
$x = range(1, 10000000);
$z = 0;
$start = microtime(true);
for ($i = 0; $i < count($x); $i++) {
$z++;
}
$end = microtime(true); // $end - $start = 2.5047581195831
Switching to do
$count = count($x);
for ($i = 0; $i < $count; $i++) {
and otherwise everything else the same, the time is 0.96466398239136
PHP is an imperative language, and that means it is not supposed to optimize away anything that can possibly have any effect. Given that it's also an interpreted language, it couldn't be done safely even if someone really wanted.
Plus, if you simply want to iterate over the array, you really want to use foreach. In that case, not only the count, but the whole array will be copied (and you can modify the original one as you wish). Or you can modify it in place using foreach ($arr as &$el) { $el = ... }; unset($el);. What I mean to say is that PHP (as any other language) often provides better solutions to your original problem (if you have any).

Project Euler || Question 10

I'm attempting to solve Project Euler in PHP and running into a problem with my for loop conditions inside the while loop. Could someone point me towards the right direction? Am I on the right track here?
The problem, btw, is to find the sums of all prime numbers below 2,000,000
Other note: The problem I'm encountering is that it seems to be a memory hog and besides implementing the sieve, I'm not sure how else to approach this. So, I'm wondering if I did something wrong in the implementation.
<?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);
?>
You can make a major optimization to your middle loop.
for($k=0; $k < $n; $k++)
{
if($list[$k] % $p == 0)
{
unset($list[$k]);
}
}
By beginning with 2*p and incrementing by $p instead of by 1. This eliminates the need for divisibility check as well as reducing the total iterations.
for($k=2*$p; $k < $n; $k += $p)
{
if (isset($list[k])) unset($list[$k]); //thanks matchu!
}
The suggestion above to check only odds to begin with (other than 2) is a good idea as well, although since the inner loop never gets off the ground for those cases I don't think its that critical. I also can't help but thinking the unsets are inefficient, tho I'm not 100% sure about that.
Here's my solution, using a 'boolean' array for the primes rather than actually removing the elements. I like using map,filters,reduce and stuff, but i figured id stick close to what you've done and this might be more efficient (although longer) anyway.
$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;
When I did this for project euler i used python, as I did for most. but someone who used PHP along the same lines as the one I did claimed it ran it 7 seconds (page 2's SekaiAi, for those who can look). I don't really care for his form (putting the body of a for loop into its increment clause!), or the use of globals and the function he has, but the main points are all there. My convenient means of testing PHP runs thru a server on a VMWareFusion local machine so its well slower, can't really comment from experience.
I've got the code to the point where it runs, and passes on small examples (17, for instance). However, it's been 8 or so minutes, and it's still running on my machine. I suspect that this algorithm, though simple, may not be the most effective, since it has to run through a lot of numbers a lot of times. (2 million tests on your first run, 1 million on your next, and they start removing less and less at a time as you go.) It also uses a lot of memory since you're, ya know, storing a list of millions of integers.
Regardless, here's my final copy of your code, with a list of the changes I made and why. I'm not sure that it works for 2,000,000 yet, but we'll see.
EDIT: It hit the right answer! Yay!
Set memory_limit to -1 to allow PHP to take as much memory as it wants for this very special case (very, very bad idea in production scripts!)
In PHP, use % instead of mod
The inner and outer loops can't use the same variable; PHP considers them to have the same scope. Use, maybe, $j for the inner loop.
To avoid having the prime strike itself off in the inner loop, start $j at $i + 1
On the unset, you used $arr instead of $list ;)
You missed a $ on the unset, so PHP interprets $list[j] as $list['j']. Just a typo.
I think that's all I did. I ran it with some progress output, and the highest prime it's reached by now is 599, so I'll let you know how it goes :)
My strategy in Ruby on this problem was just to check if every number under n was prime, looping through 2 and floor(sqrt(n)). It's also probably not an optimal solution, and takes a while to execute, but only about a minute or two. That could be the algorithm, or that could just be Ruby being better at this sort of job than PHP :/
Final code:
<?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);
?>

Categories