PHP more efficient if else conditional logic - php

I'm not very skilled when it comes to PHP but I've created this and it's probably not the best way to put it together but the only one that came to my mind right now.
What are we looking at?
It's the Apple Watch 'standing up in each hour' activity ring. This ring holds 12 image slices so called 'steps' because you have to stand up at least 12 times a day till you reach your goal (100% full circle) this means when you stand for 14 or 16 times a day the ring will loop further around 100% but the only difference between slide 2 and 14 is the background behind the circle will already be filled.
Because I don't like to create 24 slices but only 12, I will rotate slide number 12 (full circle) X degree when the value gets over 12+ this will keep the amount of used images on the page low because we keep using slide number 12 most of the time only rotate it around for step 12 till 24.
Example: the blue middle ring
PHP wise everything is working so far but you can imagine how tall those if else list will become when I start the red 'move' or calories burned circle. This circle has 100 slices or better called steps. The green circle has 30 slices.
That's why I was wondering if there is maybe a more lean way to make those if else steps. Maybe performance wise this can be done a lot better.
Thanks already..

This looks like a simple math problem. Without looking at all the details and making it even shorter (there is probably no need for an if statement at all...), you can already see that:
if ($stand > 12) {
$stanr = ($stand % 12) * 30;
$stand = 12;
}

Erm...
$stanr = 0;
if ($stand <= 12) {
} elseif (($stand > 12) and ($stand < 24) {
$stanr = ($stand - 12) * 30;
$stand = 12;
else {
$stand = 0;
}

Related

How to get web server average load level in percentages using PHP function sys_getloadavg()?

PHP function sys_getloadavg() returns an array with three values showing average number of processes in the system run-queue over the last 1, 5 and 15 minutes, respectively.
How to convert this production to percentages?
Percentages are relative measurement units. This means that we must know a range or minimum and maximum values of the measured quantity. Function sys_getloadavg() evaluates performance of whole system, not separate CPU load level, or usage of memory, file system or database. It returns float numbers showing how many processes were in run-queue for the last interval of time.
I did some experiment with my MacBook Pro (8 CPU cores) and PHP 7.0 to figure out range of values produced by sys_getloadavg(). I've got average figures between 1.3 and 3.2. When I run video conversion program in parallel, the maximum result jumped up to 18.9. By the way, in all cases I didn't fix substantial losses in web page loading speed. It means that whole system was not overloaded.
Let's take for 100% of system load situation when web page don't load for reasonable time, let say 10 sec. I don't know what values will return sys_getloadavg() in this case, but I think it will be something big.
My solution is very simple. Let's measure system average load level and persistently store results as minimum and maximum values. When system works faster or slower we will update min and max by new values. So, our program will 'learn' the system and becomes more and more precise. The value of the last minute will be compared with stored range and converted to percents like (loadavg - min)/((max - min) / 100):
$performance = sys_getloadavg();
try {
$rangeFile = 'sys_load_level.txt';
$range = file($rangeFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$performance = array_merge($performance, $range);
$min = min($performance);
$max = max($performance);
if ($range[0] > $min || $range[1] < $max)
file_put_contents($rangeFile, [$min.PHP_EOL, $max.PHP_EOL]);
}
catch (Exception $e) {
$min = min($performance);
$max = max($performance);
file_put_contents($rangeFile, [$min.PHP_EOL, $max.PHP_EOL]);
}
$level = intval(($performance[0] - $min) / (($max - $min) / 100.0));

Determining if two time ranges overlap at any point [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Determine Whether Two Date Ranges Overlap
I am trying to work out if two time ranges in PHP overlap. I've been referring to Determine Whether Two Date Ranges Overlap for my initial try, however, it's not matching all cases. If a time range is nested in between the start and end times of another time range, it's not being matched. If it overlaps the beginning or the end of the shift, or if the shifts are exact matches, it works as expected.
Check out this image of what I'm talking about:
Basically, I am trying to hide any orange shifts if they overlap any red shifts anywhere. Here's the relevant portion of code I'm trying to use to make this happen.
if(($red['start'] <= $orange['end']) && ($red['end'] >= $orange['start'])) {
//Conflict handling
}
The values of the variables are UNIX timestamps. Working through the numbers logically, I understand why the statement above fails. There are obviously ways I could do more logic to determine if the one shift falls in the other shift (which is what I may need to do), but I was hoping for a more universal catch.
EDIT: Adding the values of each block's start and end time. I agree what I have should work. The fact that it isn't is where my issue lies. I'm probably overlooking something dumb.
orange-start = 1352899800
orange-end = 1352907000
red-start = 1352923200
red-end = 1352926200
Therefore my logic would state:
if((1352923200 <= 1352907000) && (1352926200 >= 1352899800))
So following that, the first comparison fails.
EDIT 2: It looks like my logic is sound (which I thought was the case), and my issue is something related to the UNIX timestamp not matching the actual time being displayed. I thank those who worked though this with me and help me discover that as being the issue. I wish I could accept both Andrey's and Jason's answers.
If you have two ranges [b1, e1] and [b2, e2] (where it is already established that b1 < e1 and b2 < e2) then the overlap is detected by the following logical expression
not (e2 < b1 or e1 < b2)
which can be rewritten as
e2 >= b1 and e1 >= b2
In your syntax that would be
if(($orange['end'] >= $red['start']) && ($red['end'] >= $orange['start'])) {
//Conflict handling
}
I.e. you got it correctly. Why you are claiming "Working through the numbers logically, I understand why the statement above fails." is not clear to me. What exactly fails? (And I don't know why is everyone coming up with ridiculously "overengineered" checks, with more than two comparisons.)
Of course, you have to decide whether touching ranges are considered overlapping and adjust the strictness of the comparisons accordingly.
P.S. The sample ranges you provided in your edit do not overlap, and your comparison correctly recognizes it as no-conflict situation. I.e. everything works as it should. Where do you see the problem?
The logic is correct. The timestamps you provided for $red (8-8:50pm) and $orange (1:30-3:30pm) do not overlap.
Given correct values (that reflect your screenshot), the overlap is indeed found:
function show_date($value, $key) {
echo $key, ': ', date('r', $value), PHP_EOL;
}
$red = array('start' => strtotime('today, 2pm'), 'end' => strtotime('today, 2:45pm'));
$orange = array('start' => strtotime('today, 1:30pm'), 'end' => strtotime('today, 4pm'));
array_walk($red, 'show_date');
array_walk($orange, 'show_date');
if (($red['start'] <= $orange['end']) && ($red['end'] >= $orange['start'])) {
echo 'Conflict handling';
}
My guess would be you have a timezone conversion issue.
You need to check if the you have a "RED" task which starts OR ends between the start and the end of an "ORANGE" task. Like this you should detect every "ORANGE" task overlapping a "RED" task.
if((($red['start'] <= $orange['end']) && ($red['start'] >= $orange['start'])) ||
(($red['end'] <= $orange['end']) && ($red['end'] >= $orange['start'])) ) {
//Conflict handling
}
EDIT: as stated by AndreyT this is kind of overkill and you can do better with less check
if ((($red['start'] <= $orange['end']) && ($red['start'] >= $orange['start']))
|| (($red['end'] <= $orange['end']) && ($red['end'] >= $orange['start']))
|| (($red['start'] >= $orange['start']) && ($red['end'] >= $orange['end']))
) {
// conflict happens if Red starts sometime between Orange start and end
// or if Red ends sometime between Orange start and end
// or if Red starts before Orange starts and ends after Orange ends
}

How can I create a specified amount of random values that all equal up to a specified number in PHP?

For example, say I enter '10' for the amount of values, and '10000' as a total amount.
The script would need to randomize 10 different numbers that all equal up to 10000. No more, no less.
But it needs to be dynamic, as well. As in, sometimes I might enter '5' or '6' or even '99' for the amount of values, and any number (up to a billion or even higher) as the total amount.
How would I go about doing this?
EDIT: I should also mention that all numbers need to be a positive integer
The correct answer here is unbelievably simple.
Just imagine a white line, let's say 1000 units long.
You want to divide the line in to ten parts, using red marks.
VERY SIMPLY, CHOOSE NINE RANDOM NUMBERS and put a red paint mark at each of those points.
It's just that simple. You're done!
Thus, the algorithm is:
(1) pick nine random numbers between 0 and 1000
(2) put the nine numbers, a zero, and a 1000, in an array
(3) sort the array
(4) using subtraction get the ten "distances" between array values
You're done.
(Obviously if you want to have no zeros in your final set, in part (1) simply rechoose another random number if you get a collision.)
Ideally as programmers, we can "see" visual algorithms like this in our heads -- try to think visually whatever we do!
Footnote - for any non-programmers reading this, just to be clear pls note that this is like "the first thing you ever learn when studying computer science!" i.e. I do not get any credit for this, I just typed in the answer since I stumbled on the page. No kudos to me!
Just for the record another common approach (depending on the desired outcome, whether you're dealing with real or whole numbers, and other constraints) is also very "ah hah!" elegant. All you do is this: get 10 random numbers. Add them up. Remarkably simply, just: multiply or divide them all by some number, so that, the total is the desired total! It's that easy!
maybe something like this:
set max amount remaining to the target number
loop for 1 to the number of values you want - 1
get a random number from 0 to the max amount remaining
set new max amount remaining to old max amount remaining minus the current random number
repeat loop
you will end up with a 'remainder' so the last number is determined by whatever is left over to make up the original total.
Generate 10 random numbers till 10000 .
Sort them from big to small : g0 to g9
g0 = 10000 - r0
g1 = r0 - r1
...
g8 = r8 - r9
g9 = r9
This will yield 10 random numbers over the full range which add up to 10000.
I believe the answer provided by #JoeBlow is largely correct, but only if the 'randomness' desired requires uniform distribution. In a comment on that answer, #Artefacto said this:
It may be simple but it does not generate uniformly distributed numbers...
Itis biased in favor of numbers of size 1000/10 (for a sum of 1000 and 10 numbers).
This begs the question which was mentioned previously regarding the desired distribution of these numbers. JoeBlow's method does ensure a that element 1 has the same chance at being number x as element 2, which means that it must be biased towards numbers of size Max/n. Whether the OP wanted a more likely shot at a single element approaching Max or wanted a uniform distribution was not made clear in the question. [Apologies - I am not sure from a terminology perspective whether that makes a 'uniform distribution', so I refer to it in layman's terms only]
In all, it is incorrect to say that a 'random' list of elements is necessarily uniformly distributed. The missing element, as stated in other comments above, is the desired distribution.
To demonstrate this, I propose the following solution, which contains sequential random numbers of a random distribution pattern. Such a solution would be useful if the first element should have an equal chance at any number between 0-N, with each subsequent number having an equal chance at any number between 0-[Remaining Total]:
[Pseudo code]:
Create Array of size N
Create Integer of size Max
Loop through each element of N Except the last one
N(i) = RandomBetween (0, Max)
Max = Max - N(i)
End Loop
N(N) = Max
It may be necessary to take these elements and randomize their order after they have been created, depending on how they will be used [otherwise, the average size of each element decreases with each iteration].
Update: #Joe Blow has the perfect answer. My answer has the special feature of generating chunks of approximately the same size (or at least a difference no bigger than (10000 / 10)), leaving it in place for that reason.
The easiest and fastest approach that comes to my mind is:
Divide 10000 by 10 and store the values in an array. (10 times the value 10000)
Walk through every one of the 10 elements in a for loop.
From each element, subtract a random number between (10000 / 10).
Add that number to the following element.
This will give you a number of random values that, when added, will result in the end value (ignoring floating point issues).
Should be half-way easy to implement.
You'll reach PHP's maximum integer limit at some point, though. Not sure how far this can be used for values towards a billion and beyond.
Related: http://www.mathworks.cn/matlabcentral/newsreader/view_thread/141395
See this MATLAB package. It is accompanied with a file with the theory behind the implementation.
This function generates random, uniformly distributed vectors, x = [x1,x2,x3,...,xn]', which have a specified sum s, and for which we have a <= xi <= b, for specified values a and b. It is helpful to regard such vectors as points belonging to n-dimensional Euclidean space and lying in an n-1 dimensional hyperplane constrained to the sum s. Since, for all a and b, the problem can easily be rescaled to the case where a = 0 and b = 1, we will henceforth assume in this description that this is the case, and that we are operating within the unit n-dimensional "cube".
This is the implementation (© Roger Stafford):
function [x,v] = randfixedsum(n,m,s,a,b)
% Rescale to a unit cube: 0 <= x(i) <= 1
s = (s-n*a)/(b-a);
% Construct the transition probability table, t.
% t(i,j) will be utilized only in the region where j <= i + 1.
k = max(min(floor(s),n-1),0); % Must have 0 <= k <= n-1
s = max(min(s,k+1),k); % Must have k <= s <= k+1
s1 = s - [k:-1:k-n+1]; % s1 & s2 will never be negative
s2 = [k+n:-1:k+1] - s;
w = zeros(n,n+1); w(1,2) = realmax; % Scale for full 'double' range
t = zeros(n-1,n);
tiny = 2^(-1074); % The smallest positive matlab 'double' no.
for i = 2:n
tmp1 = w(i-1,2:i+1).*s1(1:i)/i;
tmp2 = w(i-1,1:i).*s2(n-i+1:n)/i;
w(i,2:i+1) = tmp1 + tmp2;
tmp3 = w(i,2:i+1) + tiny; % In case tmp1 & tmp2 are both 0,
tmp4 = (s2(n-i+1:n) > s1(1:i)); % then t is 0 on left & 1 on right
t(i-1,1:i) = (tmp2./tmp3).*tmp4 + (1-tmp1./tmp3).*(~tmp4);
end
% Derive the polytope volume v from the appropriate
% element in the bottom row of w.
v = n^(3/2)*(w(n,k+2)/realmax)*(b-a)^(n-1);
% Now compute the matrix x.
x = zeros(n,m);
if m == 0, return, end % If m is zero, quit with x = []
rt = rand(n-1,m); % For random selection of simplex type
rs = rand(n-1,m); % For random location within a simplex
s = repmat(s,1,m);
j = repmat(k+1,1,m); % For indexing in the t table
sm = zeros(1,m); pr = ones(1,m); % Start with sum zero & product 1
for i = n-1:-1:1 % Work backwards in the t table
e = (rt(n-i,:)<=t(i,j)); % Use rt to choose a transition
sx = rs(n-i,:).^(1/i); % Use rs to compute next simplex coord.
sm = sm + (1-sx).*pr.*s/(i+1); % Update sum
pr = sx.*pr; % Update product
x(n-i,:) = sm + pr.*e; % Calculate x using simplex coords.
s = s - e; j = j - e; % Transition adjustment
end
x(n,:) = sm + pr.*s; % Compute the last x
% Randomly permute the order in the columns of x and rescale.
rp = rand(n,m); % Use rp to carry out a matrix 'randperm'
[ig,p] = sort(rp); % The values placed in ig are ignored
x = (b-a)*x(p+repmat([0:n:n*(m-1)],n,1))+a; % Permute & rescale x
return

How can I get X amount of files per folder with math?

I actually have it done, I did this math equation about 2 years ago and I am having trouble understanding it now.
Basicly I use this so that when users upload photos to my site, I can balance them out with only X amount of photo per folder.
This gives me something like this 1/1 1/2 1/3 1/4 ----1/10 2/1 2/2 2/3 and so on but I need to modify it to go 3 folders deep, each folder should have a limit of the number 1-9 or 1-10 then it will increase that number to the next
So if a large enough number is entered into my function below and the result is 3/10 then when the right number of objects is reached it would bump up to 4/1 then when so many thousands objects go by again it will jump to 4/2. What I am wanting to do is make it 3 numbers/levels deep 3/10/2 would go to 3/10/3 when it got to 3/10/10 it would go 4/1/1 4/1/2 4/1/3 when the third place got to 10 it would make it got to 4/2/1
<?PHP
function uploadfolder($x) {
$dir = floor($x/18001) + 1;
$sdir = ceil($x/2000) % 9;
return "$dir/$sdir";
}
?>
I spent a lot of time 2 years ago to get it to do this with 2 levels deep and I just kind of got lucky and now it is somewhat confusing for me looking back at it
It seems to do roughly this:
It will package 2000 pictures into a subdirectory (0..8) using the line
$sdir = ceil($x/2000) % 9
Spelled out: how many times does 2000 fit into $x. As you limit this to 9 subdirectories using the modulo 9, you would get the 18001rst photo into subdirectory 0 again.
The upper level changes therefore using 18001 as limit. All photos from 1..18000 go into directory 1. (The +1 just shifts the interval to start from 1. That is the line
$dir = floor($x/18001) + 1;
Now you could go about it like this for 3 levels (pseudocode, as I do not know PHP):
function uploadfolder($x) {
$numOfPics = 2000;
$numOfFolders = 9;
$topdir = ceil($x / ($numOfPics * $numOfFolders * $numOfFolders));
$middir = floor($x / ($numOfPics * $numOfFolders)) % $numOfFolders + 1;
$botdir = (floor($x / $numOfPics) % $numOfFolders) + 1;
return "$topdir/$middir/$botdir";
}

PHP question on while query

Could someone help me figure out how to do this.
I have a game where a player can use potions to enhance their abilities.
This is on a timer which works fine. However Im now looking to add to this query when the player uses 2 different type of potions. I can get it to work but not 100% how I wish.
Player uses potion 1 and their stats are boosted by 20% for 20minutes.
Player then uses potion 2 and their stats are boosted by 60% for 20minutes.
With my code at the moment
$check = sprintf("SELECT time,strmod FROM `effects` WHERE `userid` = %u", $userid);
$exe = mysql_query($check);
while($bonus = mysql_fetch_array($exe))
{
$last = $bonus['time'];
$strmod=$bonus['strmod']);
It will display the users bonus (base strength in this example is 2,364,195)
Potion 1 boost your base strength to 2837034 (+20%)
Potion 2 boost your base strength to 3782712 (+60%)
Is there a solution where I can get the second 60% potion to take into account the bonus received from the 1st potion. So 60% of 2837034 rather than 2364195. Before I consider recoding the whole thing :D
I hope thats clear bit hard to explain.
Thanks
I presume the database table contains the list of potions in effect. Try something which keeps the value over the while loop...
$multiplier = 1;
while($bonus = mysql_fetch_array($exe))
{
$last = $bonus['time'];
$strmod=$bonus['strmod']);
$multiplier += $strmod/100; // convert from percent to decimal
}
$currStrength = $multiplier*$baseStrength;
Probably not the way your doing it, since your not recording the original players HP, you just have the current (after modifiers have been applied) HP.

Categories