I can check if some integer value (which comes from user input, and thus should be filtered) is in specific range like this:
<?php
$size=50;
var_dump(in_array($size,range(1,100)));
?>
which will echo true if the size in range 1 to 100. Of course the other method is using filters:
<?php
$size=50;
$int_options = array("options"=>
array("min_range"=>0, "max_range"=>256));
var_dump(filter_var($size, FILTER_VALIDATE_INT, $int_options));
?>
But, what if I need to know if the elements of an array are in this range?(and probably remove those not) What is the best practice, considering the performance and memory usage. I prefer to use php functions instead of writing mine.
Slightly functional approach (I don't know if PHP support lambdas):
function mapper($n) { return $n >= 1 && $n <= 100 ? 1 : 0; }
...
if (array_product(array_map('mapper', $array)) == 1) { }
Not very performance nor memory efficient, though.
For removing, I'd suggest using array_filter.
function my_filter($n) { return $n >= 1 && $n <= 100; }
...
$newarray = array_filter($array, 'my_filter');
(btw, who the hell designed that language.. array_map and array_filter have different order of parameters?!)
If I were you, I will use a more simple approach:
$size=50;
if($size <= 100 && $size >= 1) {
return true;
} else {
return false;
}
No function call, simple integer comparison. Good performance.
Related
So I am trying to optimize a piece of php code that basically runs the same operations on two different datasets, based on user input. What would be a better and more optimized approach?
//$input = //user input
//$a = [1,2,3 .....];
//$b = [a,b,c .....];//both are same length - n
case 1 :
for($i =0; $i<n; $i++) {
if($input == 'a')
//doSomething with $a[i] - code here
else
//doSomething with $b[i] - code here
}
case 2 :
if($input == 'a') {
for($i =0; $i<n; $i++) {
//doSomething with $a[i] - code here
}
}
else {
for($i =0; $i<n; $i++) {
//doSomething with $b[i] - code here
}
}
case 3 :
if($input == 'a') {
for($i =0; $i<n; $i++) {
doSomething($a[i]);
}
}
else {
for($i =0; $i<n; $i++) {
doSomething($b[i]);
}
}
the operation is same in all cases
Better is always difficult to quantify, but if you want to do exactly the same processing on the inputs, just picking the one dataset depending on the input, you may be better off just setting an input array and just processing that...
if($input == 'a')
$dataset = $a;
else
$dataset = $b;
foreach ( $dataset as $dataItem ) {
//doSomething data code here
}
This might not really be the answer you're looking for, but honestly, I would just go for whatever conveys the actual use case the best and not try to optimize too much.
Performance-wise, unless the operation that checks the $input takes a lot of time (orders of magnitude more than a simple comparison), or if the operation on $dataset is very short (comparable to the input check), it simply won't matter.
Assuming $input does not change while you're processing your dataset, I'd go for case three. You're making it clear that it is the same operation, and the only difference between the different if branches is the dataset you're working with. If you're familiar with ternary operators, I'd even use this:
for($i = 0; $i < n; $i++) {
doSomething(($input == $a) ? $a[i] : $b[i]);
}
I'd take code readability over micro-optimizations any day, as long as you don't actually have performance issues with this piece of code.
As a bonus, have a read at this question about optimization: https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil
I need to find the value of x where the variance of two results (which take x into account) is the closest to 0. The problem is, the only way to do this is to cycle through all possible values of x. The equation uses currency, so I have to check in increments of 1 cent.
This might make it easier:
$previous_var = null;
$high_amount = 50;
for ($i = 0.01; $i <= $high_amount; $i += 0.01) {
$val1 = find_out_1($i);
$val2 = find_out_2();
$var = variance($val1, $val2);
if ($previous_var == null) {
$previous_var = $var;
}
// If this variance is larger, it means the previous one was the closest to
// 0 as the variance has now started increasing
if ($var > $previous_var) {
$l_s -= 0.01;
break;
}
}
$optimal_monetary_value = $i;
I feel like there is a mathematical formula that would make the "cycling through every cent" more optimal? It works fine for small values, but if you start using 1000's as the $high_amount it takes quite a few seconds to calculate.
Based on the comment in your code, it sounds like you want something similar to bisection search, but a little bit different:
function calculate_variance($i) {
$val1 = find_out_1($i);
$val2 = find_out_2();
return variance($val1, $val2);
}
function search($lo, $loVar, $hi, $hiVar) {
// find the midpoint between the hi and lo values
$mid = round($lo + ($hi - $lo) / 2, 2);
if ($mid == $hi || $mid == $lo) {
// we have converged, so pick the better value and be done
return ($hiVar > $loVar) ? $lo : $hi;
}
$midVar = calculate_variance($mid);
if ($midVar >= $loVar) {
// the optimal point must be in the lower interval
return search($lo, $loVar, $mid, $midVar);
} elseif ($midVar >= $hiVar) {
// the optimal point must be in the higher interval
return search($mid, $midVar, $hi, $hiVar);
} else {
// we don't know where the optimal point is for sure, so check
// the lower interval first
$loBest = search($lo, $loVar, $mid, $midVar);
if ($loBest == $mid) {
// we can't be sure this is the best answer, so check the hi
// interval to be sure
return search($mid, $midVar, $hi, $hiVar);
} else {
// we know this is the best answer
return $loBest;
}
}
}
$optimal_monetary_value = search(0.01, calculate_variance(0.01), 50.0, calculate_variance(50.0));
This assumes that the variance is monotonically increasing when moving away from the optimal point. In other words, if the optimal value is O, then for all X < Y < O, calculate_variance(X) >= calculate_variance(Y) >= calculate_variance(O) (and the same with all > and < flipped). The comment in your code and the way have you have it written make it seem like this is true. If this isn't true, then you can't really do much better than what you have.
Be aware that this is not as good as bisection search. There are some pathological inputs that will make it take linear time instead of logarithmic time (e.g., if the variance is the same for all values). If you can improve the requirement that calculate_variance(X) >= calculate_variance(Y) >= calculate_variance(O) to be calculate_variance(X) > calculate_variance(Y) > calculate_variance(O), you can improve this to be logarithmic in all cases by checking to see how the variance for $mid compares the the variance for $mid + 0.01 and using that to decide which interval to check.
Also, you may want to be careful about doing math with currency. You probably either want to use integers (i.e., do all math in cents instead of dollars) or use exact precision numbers.
If you known nothing at all about the behavior of the objective function, there is no other way than trying all possible values.
On the opposite if you have a guarantee that the minimum is unique, the Golden section method will converge very quickly. This is a variant of the Fibonacci search, which is known to be optimal (require the minimum number of function evaluations).
Your function may have different properties which call for other algorithms.
Why not implementing binary search ?
<?php
$high_amount = 50;
// computed val2 is placed outside the loop
// no need te recalculate it each time
$val2 = find_out_2();
$previous_var = variance(find_out_1(0.01), $val2);
$start = 0;
$end = $high_amount * 100;
$closest_variance = NULL;
while ($start <= $end) {
$section = intval(($start + $end)/2);
$cursor = $section / 100;
$val1 = find_out_1($cursor);
$variance = variance($val1, $val2);
if ($variance <= $previous_var) {
$start = $section;
}
else {
$closest_variance = $cursor;
$end = $section;
}
}
if (!is_null($closest_variance)) {
$closest_variance -= 0.01;
}
Given two functions in PHP, say
function f($n) {
return $n;
}
function g($n) {
return pow($n, (2/3));
}
How to check if a function f(n) is in Ω(g(n)), Θ(g(n)) or O(g(n)) in PHP?
What I tried so far:
$n = INF;
$A = f($n) / g($n);
if ($A == 0) {
echo "f(n) = O(g(n))";
} elseif (is_infinite($A)) {
echo "f(n) = Ω(g(n))";
} elseif ($A != 0) {
echo "f(n) = Θ(g(n))";
}
Shouldn't that work?
Your basic idea is correct: you have to find the limit of f(n)/g(n) as n grows without bound. Unfortunately there is no easy way to compute the exact limit in PHP, since that requires symbolic computations which is best left to a computer algebra system such as Mathematica or Maxima.
You can approximate the limit by computing f(n)/g(n) for increasing values of n and seeing if you get a sequence that approaches a fixed value. For example:
$n=1;
while ($n < 1e300) {
$A = f($n)/g($n);
echo $A, "\n";
$n *= 1e12;
}
In this particular case the sequence of f(n)/g(n) seems to grow without bound, so the numerical evidence suggests that f(n) is in Ω(g(n)). This is not a proof though; symbolic methods are needed for that.
Both the time and space requirements for both f() and g() are in Ω(1), Θ(1) and O(1).
Using PHP >= 5.5 if we have a method that yielded values, what would be the best method in counting these values?
What I was expecting was to be able to convert a Generator to an array and count that, however it would return an empty array. Count() also does not work as the Generator is reported as empty despite not being empty.
I'm baffled with this. If you don't need to count a generators yields then it's a nice feature otherwise I don't see much of a point for it. There is a way to detect if a generator is empty, this is by using the key() method and if it returns NULL then there are either no yields or the generator has already been iterated through which would mean the current pointer is null.
If you have to do it, following as a on-liner of native functions:
count(iterator_to_array($generator, false));
However, take care: After this your $generator is executed and consumed. So if you would put that same $generator into a foreach in a following line, it would loop 0 times.
Generators are by design highly dynamic (in contrast to fixed data structures like arrays), thats why they don't offer ->count() or ->rewind().
You should understand, that generator isn't data structure - it's an instance of Generator class and, actually, it's special sort of Iterator. Thus, you can't count its items directly (to be precise - that's because Generator class implements only Iterator interface, and not Countable interface. To be honest, I can't imagine how can it implement that)
To count values with native generator you'll have to iterate through it. But that can not be done in common sense - because in most cases it's you who'll decide how many items will be yielded. Famous xrange() sample from manual:
function xrange($start, $limit, $step = 1) {
if ($start < $limit) {
if ($step <= 0) {
throw new LogicException('Step must be +ve');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('Step must be -ve');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
-as you can see, it's you who must define borders. And final count will depend from that. Iterating through generator will have sense only with static-borders defined generator (i.e. when count of items is always static - for example, defined inside generator strictly). In any other case you'll get parameter-dependent result. For xrange():
function getCount(Generator $functor)
{
$count = 0;
foreach($functor as $value)
{
$count++;
}
return $count;
}
-and usage:
var_dump(getCount(xrange(1, 100, 10)));//10
var_dump(getCount(xrange(1, 100, 1)));//100
-as you can see, "count" will change. Even worse, generator hasn't to be finite. It may yield infinite set of values (and borders are defined in external loop, for example) - and this is one more reason which makes "counting" near senseless.
Actually, it depends in which case you are :
Case 1 : I can't count before iterating and I care about values
// The plain old solution
$count = 0;
foreach($traversable as $value) {
// Do something with $value, then…
++$count;
}
Case 2 : I can't count before iterating but I don't care about values
// let's iterator_count() do it for me
$count = iterator_count($traversable);
Case 3 : I can count before iterating but I don't care about values
I try not to use generators.
For example (with SQL backends) :
SELECT count(1) FROM mytable; // then return result
is better than
SELECT * FROM mytable; // then counting results
Other example (with xrange from Alma Do) :
// More efficient than counting by iterating
function count_xrange($start, $limit, $step = 1) {
if (0 === $step) throw new LogicException("Step can't be 0");
return (int)(abs($limit-$start) / $step) + 1;
}
Case 4 : I can count before iterating and I care about values
I can use a generator AND a count function
$args = [0,17,2];
$count = count_xrange(...$args);
$traversable = xrange(...$args);
Case 5 : Case 4, and I want all in one object
I can "decorate" an Iterator to make a Countable Iterator
function buildCountableIterator(...$args) {
$count = count_xrange(...$args);
$traversable = xrange(...$args);
return new class($count, $traversable) extends \IteratorIterator implements \Countable {
private $count;
public function __construct($count, $traversable) {
parent::__construct($traversable);
$this->count = $count;
}
public function count() {
return $this->count;
}
}
}
$countableIterator = buildCountableIterator(1, 24, 3);
// I can do this because $countableIterator is countable
$count = count($countableIterator);
// And I can do that because $countableIterator is also an Iterator
foreach($countableIterator as $item) {
// do something
}
Sources :
http://php.net/manual/en/function.iterator-count.php
http://php.net/manual/en/class.countable.php
http://php.net/manual/en/class.iteratoriterator.php
http://php.net/manual/en/language.oop5.anonymous.php
While you can't use count() you can use a reference to set the count to make it accessible to the outside world.
function generate(&$count = 0) {
// we have 4 things
$count = 4;
for($i = 0; $i < $count; $i++) {
yield $i;
}
}
$foo = generate($count);
echo $count; // 4
foreach ($foo as $i) {
echo $i;
}
Downside to this is it won't tell you how many remain but how many it started with.
This question is born out of pure laziness and the desire to do more with less code.
Say I have a variable $x which needs to be greater than 0 and less than 12, what is the fastest (least amount of code written) way to check. Is there a faster way than this.
if($x < 0 || $x > 12) {
die("invalid x value");
}
It would be nice (and I think some languages have it) to do this:
if(0 > $x > 12) {
die("invalid x value");
}
Very curious to see if there is some PHP magic I am missing out on.
You can use filter_var as a native PHP function : http://de2.php.net/manual/en/function.filter-var.php But I don't find it any better, as you will need to pass an array with min and max range, which is not fast, nor short.
Maybe a user-defined function for this will fit? Yes, you will need to write the code once, but only once.
function between($value, $from, $to) {
if ($value < $from || $value > $to) {
return false;
}
return true;
}
The function return false, if the value is less than the min bound, or greater than the max bound. Otherwise it returns true. So if you need to stop you script, if the value is NOT between, you would need to ask for the false response if(!between...
So you only call it this way:
$x = 14;
if(!between($x, 0, 12)) {
die("invalid x value");
}
Output:
invalid x value
if, for example your $x is 5 and you want to check if it's between, and if it is - to continue the script, you ask for the true response.
if(between($x, 0, 12)