I have an array of values that represent points on a line chart:
$temperatures = [23, 24, null, '', 25, '', '', null];
I'm using PHP4, but I think it can be answered in any language.
Array contains only numbers, nulls and empty strings.
Numbers represent temperatures, nulls mean that the instruments weren't working and empty strings represent neither (instruments are working, just not measuring anything).
Points must (in most cases) be connected, since it's a line chart.
I have a variable $gap that corresponds to each point and tells whether this point is connected to the next point. If it is set to true, than the points are not connected (false otherwise). For example, $gap for temperatures[0] must be set to false, since the line is drawn between temperatures[0] and temperatures[1](they are both valid temperatures). $gap fortemperatures[1]andtemperatures[2]` must be true, since there is null following. And so on.
When there is null the $gap is absolutely true. For numbers and empty strings, it depends on: if a null follows, gap is true; if a number follows, gap is false. If empty string follows, we must check if afterwards comes null or number and apply the previous sentence accordingly. If there are just empty strings following, gap is true. Here is my code that is working too slow, but produce correct results:
$limit = count($temperatures);
for ($i = 0; $i <= limit; $i++) {
$next_is_number = false;
if (is_null($temperatures[i]) {
$gap = true;
} else {
for ($y = $i + 1; $i <= limit; $i++) {
if (is_null($temperatures[$y]) {
break;
} elsif (is_numeric($temperatures[$y]) {
$next_is_number = true;
break;
}
}
if ($next_is_number) {
$gap = false;
} else {
$gap = true;
}
}
}
How can I speed it up?
Your code checks whether there is a a gap somewhere in your line chart or not.
So once a gap is found, there no reason to continue in the outer for-loop. Think of a chart of 1000 values, if there is a gap between the first two values it makes no sense to continue checking the other 998 values.
Thus, the first thing I would recommend is to set $gap = false at the beginning and to leave the loop once $gap is true. You could do that either with
1.) break (not so elegant),
2.) extract your code to a method and add a return-statement or
3.) adding a condition in the for-loop. I am not familiar with php but in most languages it is possible to do it like this:
$gap = false;
$limit = count($temperatures);
for ($i = 0; $i <= limit && !$gap; $i++) {
[...]
So once $gap is true, the outer for-loop is left.
Iterate through backwards, remembering the last valid value and putting that in when you see an empty string. Then it's O(n) worst case, not O(n^2).
Alternatively, you can work from $y - 1 to $x (or vice versa) after the inner loop, setting the values of your gaps array / outputting values, then skip past all the ones you've just done ($x = $y). This is also O(n).
Then, once you've got the algorithm as fast as you can, you can ditch PHP and write it in something like Rust or C. (I don't recall any true arrays in the language, so they're always going to be slow.)
Related
So while i was doing my homework i stuck on one point.
The excercise is based on making a function which checks if $word is a palindrome, from my tests $L works and is moving forward to right side of the word ($L starts from left, $R from right)
but $R is not working at all, if $R is swapped by a number - it works. If $R is printed, it shows right number - 5.
$word = "madam";
function palindrome($s)
{
$i = intval(strlen($s) / 2);
$L = 0;
$R = strlen($s);
$pal = true;
for($i; $i>0; $i--)
{
if($s[$L] != $s[$R]) $pal=false;
$L++;
$R--;
}
if($pal==true)
print("palindrome");
else
print("not a palindrome");
}
palindrome($word);
I expect to make $R an value, i suspect that PHP sees it as a string, not an integer, but i don't know why. I would be very happy if someone helped me with that.
If you consider string as char table, index starts at 0, but strlen count from 1 so if you have 'madam' then strlen() returns 5 but last chatacter is on $s[4], simply use:
$R = strlen($s)-1;
As a quick, off the top of my head sort of idea... no loops, just some simple string splitting, this works to check if the given string ($s) is a palindrome.
function palindrome($s) {
// split the string in two
$left = substr($s, 0, floor(strlen($s)/2));
$right = substr($s, 0-strlen($left));
// if the left half matches the REVERSE of the right
// you've got a palindrome
return $left === strrev($right);
}
$word = "madam";
echo palindrome($word) ? "Yup" : "Nope";
Basically, it just chops the word in half - reverses the right half and compares it to the left. If they match, it's a palindrome - currently it's case-sensitive though so "Madam" won't be a palindrome but that can be easily tweaked by lower-casing the whole thing first.
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.
Good afternoon, good people of SO!
I'm trying to make a binary calculator, which adds two binary strings together and returns the result in binary.
However, my loop (used for checking the input consists of 0 or 1) seems to falter and return either a 503 (Service Temporarily Unavilable) or suggests I've hit the maximum execution time (30 secs).
I can't figure out why. It seems to bypass this problem if I change && to ||, however this returns a false positive for a bad input.
Here is the code:
// Spring cleaning - add some variables
$submit = htmlspecialchars(strip_tags(stripslashes($_POST['submit'])));
$val1 = htmlspecialchars(strip_tags(stripslashes($_POST['val1'])));
$val2 = htmlspecialchars(strip_tags(stripslashes($_POST['val2'])));
$val1_length = strlen($val1);
$val2_length = strlen($val1);
$val1 = str_split($val1, 1);
$val2 = str_split($val2, 1);
// Val 1 - Checking
$count = 0; // count variable counts how many times the loop recurs and stops it appropriately
while ($count <= $val1_length) {
if(($val1[$count] != 0) || ($val1[$count] != 1)) { // checks if input is comprised of 0 or 1
showInputError();
exit(); // input does not contain 0 or 1, abort script and do not attempt further calculations
$count = $count + 1; // increment the count variable after one successful loop
}
} // Val1 was fine
Thanks in advance! :)
As bwoebi said in the comments, put the bracket one line higher in the if statement, as you're not actually counting up, so the loop will go on forever if no value is found..
$count = 0; // count variable counts how many times the loop recurs and stops it appropriately
while ($count <= $val1_length) {
if(($val1[$count] != 0) || ($val1[$count] != 1)) { // checks if input is comprised of 0 or 1
showInputError();
exit(); // input does not contain 0 or 1, abort script and do not attempt further calculations
}
$count = $count + 1; // increment the count variable after one successful loop
} // Val1 was fine
Why don't you simply use a simple regex like
$input= '1101010001010101110101';
preg_match('/^[01]+$/', $input);
It seems to me you are not verifying your $val1_length in the while loop. What happens if your POST values are empty? You will get an infinite loop, so you might want to replace the while like this:
while ($count <= $val1_length && !empty($val1_length) {...}
This is a code that I got from the site here and I'd like to know whether this implementation of A* is correct. I have looked at it and compare it with the wikipedia page and it seems valid.. The reason why I ask is because in the site it says there is still a bug in this code, I tried finding it but can't find any. I want to change it though so that it takes an origin and destination as input parameter
<?php
class AStarSolver
{
function solve(&$s)
{
include_once('PQueue.class.php');
$o = new PQueue();
$l = array();
$c = array();
$p = array();
$a = $s->getStartIndex();
$z = $s->getGoalIndex();
$d = $s->goalDistance($a);
$n0 = array('g'=>0, 'h'=>$d, 'i'=>$a, 'p'=>NULL, 'f'=>$d);
$o->push($n0, -$d);
$l[$a] = TRUE;
while (! $o->isEmpty())
{
$n = $o->pop();
if ($n['i'] == $z)
{
while ($n)
{
$p[] = $n['i'];
$n = $n['p'];
}
break;
}
foreach ($s->getNeighbors($n['i']) as $j => $w)
{
if ((isset($l[$j]) || isset($c[$j])) && isset($m) && $m['g'] <= $n['g']+$w)
continue;
$d = $s->goalDistance($j);
$m = array('g'=>$n['g']+$w, 'h'=>$d, 'i'=>$j, 'p'=>$n, 'f'=>$n['g']+$w+$d);
if (isset($c[$j]))
unset($c[$j]);
if (! isset($l[$j]))
{
$o->push($m, -$m['f']);
$l[$j] = TRUE;
}
}
$c[$n['i']] = $n;
}
return $p;
}
}
?>
The code to the Pqueue can be found here
The site suggests that the bug might be in the PQueue class.
In PQueue::pop this
$j+1 < $m
is a test whether the heap node at $i has one child (at $j) or two (at $j and $j+1).
But $m here is count($h) only on the first iteration through the loop, since the --$m in the loop condition is evaluated every time.
Move that --$m next to the array_pop where it belongs, and that will be one less bug.
Now for AStarSolver.
The variables are (relative to Wikipedia pseudocode):
$o – open set as priority queue;
$l – open set as map keyed by index;
$c – closed set as map keyed by index;
$n – current node (x);
$m – neighbour node (y) ?;
$j – neighbour node index.
Problems that I see:
$n = $o->pop() isn't followed by unset($l[$n['i']]). Since both $o and $l represent the same set, they should be kept in sync.
According to Wikipedia the closed set is only used if the heuristic is monotone (and I think a distance heuristic is monotone), and in that case, once a node is added to the closed set, it is never visited again. This code seems to implement some other pseudocode, which does remove nodes from the closed set. I think this defeats the purpose of the closed set, and the first condition in the inner loop should be
if (isset($c[$j]) || isset($l[$j]) && isset($m) && $m['g'] <= $n['g']+$w)
Then we can remove the unset($c[$j]).
$m['g'] in this condition should be the g-score of the current neighbour indexed by $j. But $m has whatever value is left over from the previous loop: the node corresponding to $j on a previous iteration.
What we need is a way to find a node and its g-score by node index. We can store the node in the $l array: instead of $l[$j] = TRUE we do $l[$j] = $m and the above condition becomes
if (isset($c[$j]) || isset($l[$j]) && $l[$j]['g'] <= $n['g']+$w)
Now the tricky bit. If the node we just found is not in the open set, we add it there (that's the $o->push and $l[$j] =).
However, if it is in the open set we just found a better path to it, so we must update it. The code doesn't do this and it's tricky because the priority queue doesn't provide a routine for increasing the priority of an element. However, we can rebuild the priority queue completely and the last bit of code in the inner loop becomes
if (! isset($l[$j])) {
$o->push($m, -$m['f']);
$l[$j] = $m; // add a new element
} else {
$l[$j] = $m; // replace existing element
$o = new PQueue();
foreach ($l as $m)
$o->push($m, -$m['f']);
}
This is not terribly efficient, but it's a starting point. Changing an element in a priority queue isn't efficient anyway, because you first have to find it.
Even without these changes the algorithm does find a path, just not the best path. You can see it in the mentioned mazes:
In the crazy maze in the third inner circuit: the taken upper path around is slightly longer than the lower path would have been because of the obstacles on the left.
In the big maze in the upper-right part of the path there's an unnecessary loop up.
Since this was on my mind, I implemented my own version of the algorithm and posted it in an answer to your previous question.
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);
?>