Calculate greatest number of days between two consecutive dates - php

If you have an array of ISO dates, how would you calculate the most days between two sequential dates from the array?
$array = array('2009-03-11', '2009-03-12', '2009-04-12', '2009-05-03', '2009-10-30');
I think I need a loop, some sort of iterating variable and a sort. I can't quite figure it out.
This is actually being output from MYSQL.

Here is how you can do it in PHP:
<?php
$array = array('2009-03-11', '2009-03-12', '2009-04-12', '2009-05-03', '2009-10-30');
# PHP was throwing errors until I set this
# it may be unnecessary depending on where you
# are using your code:
date_default_timezone_set("GMT");
$max = 0;
if( count($array) > 1 ){
for($i = 0; $i < count($array) - 1; $i++){
$start = strtotime( $array[$i] );
$end = strtotime( $array[$i + 1] );
$diff = $end - $start;
if($diff > $max) $max = $diff;
}
}
$max = $max / (60*60*24);
?>
It loops throw your items (it executes one less time than there are number of items) and compares each one. If the comparison is larger than the next, it updates max. Time is in seconds, so after the loop is over we convert the seconds into days.

This PHP script will give you the largest interval
1 ){
for($i = 0; $i $maxinterval) $maxinterval = $days;
}
}
?>

EDIT:
As [originally] worded the question can be understood in [at least ;-)] two ways:
A) The array contains a list of dates in ascending order. The task is to find the longuest period (expressed in number of days) between to consecutive dates in the array.
B) The array is not necessarily sorted. The task is to find the longuest period (expr. in number of days) between any two dates in the array
The following provides an answer to the "B" understanding of the question. For a response to "A", see dcneiner's solution
No Sorting needed!...
If it comes from MySQL, you may have this DBMS returns directly the MIN and MAX values for the considered list.
EDIT: As indicated by Darkerstar, the way the way the data is structured [and also the existing SQL query which returns the complete list as indicated in the question] generally dictate the way the query which produces the MIN and MAX value should be structured.
Maybe something like this:
SELECT MIN(the_date_field), MAX(the_date_field)
FROM the_table
WHERE -- whatever where conditions if any
--Note: no GROUP BY needed
If, somehow, you cannot use SQL, a single pass through the list will allow you to obtain the MIN and MAX value in the list (in O(n) time, that is).
Algorithm is trivial:
Set Min and Max Value to first item in [unsorted] list.
Iterate through each following item in the list, comparing it with the Min Value and replacing it if found smaller, and doing like-wise for the Max value...
With Min and Max values in hand, a simple difference gives the max number of days...
In PHP, it's looks like the following:
<?php
$array = array('2009-03-11', '2009-03-12', '2009-04-12', '2009-05-03', '2009-10-30');
# may need this as suggested by dcneiner
date_default_timezone_set("GMT");
$max = $array[0];
$min = $max;
for($i = 1; $i < count($array); $i++){
// Note that since the strings in the array are in the format YYYY-MM-DD,
// they can be compared as-is without requiring say strtotime conversion.
if ($array[$i] < $min)
$min = $array[$i];
if ($array[$i] > $max)
$max = $array[$i];
}
$day_count = (strtotime($max) - strtotime($min)) / (60*60*24);
?>

Related

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.

How can I sum groups of X elements together?

I need to get a valid timestamp from the following array:
$arrkeys = array(
"t" => [
1442238840,60,120,180,240,300,360,420,480,540,600,660,720,780,840,900,960,
1442239860,60,120,180,240,300,360,420,480,540,600,660,720,780,840,900,960,
1442240880,60,120,180,240,300,360,420,480,540,600,660,720,780,840,900,960
]
);
I need to sum the first valid timestamp 1442238840 with the following 17 numbers together, to get a correct timestamp, and then, the second timestamp 1442239860, etc...
Example:
1442238840 + 60;
1442238840 + 120;
1442238840 + 180;
etc...
I can't figure out how I could do this,
some things I've tried:
Attempt No. 1
//Pseudo-code
foreach($arrkeys["t"] as $t){
if(strlen($t) < 10){
//Search for the first valid timestamp and sum?
}
}
Attempt No. 2
//Not working.
$array_size = 17;
/* I know that every 17 (counting from 0) have a valid timestamp.
so that 0 = 1st valid timestamp, 20 = 2nd valid timestamp. */
for ($i = 0; $i < $array_size; $i++) {
do {
echo $i;
} while ($i > 0);
I don't know if I'm going the right way to solve this, I don't know how to do this efficiently, or at least do it.
Any suggestions?
I love that you tried different things. That's good!
You tried to solve the problem yourself and array_chunk() will help you a lot here, since you can chunk your array into groups of 17 elements and then use array_sum() to sum each group of 17 elements together, e.g.
$result = array_chunk($arrkeys["t"], 17);
$result = array_map("array_sum", $result);

avoid duplicates amongst creating random numbers

if I want to create some variables that contain random numbers that differ from each other, right now this is the method i'm using(in this case i'm using PHP to set example):
while($rand1==$rand2 or $rand2==$rand3 or $rand3==$rand4 or $rand4==$rand5 or
rand1==$rand3 or $rand2==$rand4 or $rand3==$rand5 or $rand5==$rand4 or
$rand1==$rand4 or $rand2==$rand5 or $rand4==$rand3 or $rand5==$rand3 or
$rand1==$rand5 or $rand3==$rand2 or $rand4==$rand1 or $rand5==$rand2 or
$rand2==$rand1 or $rand3==$rand1 or $rand4==$rand2 or $rand5==$rand1):
$rand1=rand(1,1000);
$rand2=rand(1,1000);
$rand3=rand(1,1000);
$rand4=rand(1,1000);
$rand5=rand(1,1000);
endwhile;
so, if I want to create 100 variables all with differing numbers,I wouldn't want to use this method for obvious reason. Any better alternatives?
put them in an array and search the array.
$arr = array();
$arr[] = rand(1,1000);
You can just loop through as many as you like
This gives you 5 unique randoms in the given range:
$a = range(1, 1000);
shuffle($a);
$randoms = array_slice($a, 0, 5);
(I assume performance is not critical).
In a more efficient way,
$randoms = array();
while(count($randoms) < 5)
$randoms[rand(1, 1000)] = 1;
$randoms = array_keys($randoms);
From your code I assume you need numbers from 1 to 1000 and you don't want duplicates. Here's a way not to loop through your random numbers every time since it's time consuming if you need 100 random numbers:
$pool = range(1, 1000);
$numbers = array();
for ($i = 0; $i < 5; $i++) {
$random = rand(0, count($pool) - 1);
$numbers[$i] = $pool[$random];
$pool[$random] = array_pop($pool);
}
Just change 5 to whatever you need or use a variable.
This works if you have a pool of numbers to extract from, that's why we pop the last element and we replace the number we extracted already to avoid duplicates. We have to count the elements in the pool each time because it shrinks by one element on each iteration. You could probably save the initial value and decrement it based on $i if your pool is not a native array so you don't waste time counting and you just do basich math.

PHP Generate x amount of random odd numbers within a range

I need to generate x amount of random odd numbers, within a given range.
I know this can be achieved with simple looping, but I'm unsure which approach would be the best, and is there a better mathematical way of solving this.
EDIT: Also I cannot have the same number more than once.
Generate x integer values over half the range, and for each value double it and add 1.
ANSWERING REVISED QUESTION: 1) Generate a list of candidates in range, shuffle them, and then take the first x. Or 2) generate values as per my original recommendation, and reject and retry if the generated value is in the list of already generated values.
The first will work better if x is a substantial fraction of the range, the latter if x is small relative to the range.
ADDENDUM: Should have thought of this approach earlier, it's based on conditional probability. I don't know php (I came at this from the "random" tag), so I'll express it as pseudo-code:
generate(x, upper_limit)
loop with index i from upper_limit downto 1 by 2
p_value = x / floor((i + 1) / 2)
if rand <= p_value
include i in selected set
decrement x
return/exit if x <= 0
end if
end loop
end generate
x is the desired number of values to generate, upper_limit is the largest odd number in the range, and rand generates a uniformly distributed random number between zero and one. Basically, it steps through the candidate set of odd numbers and accepts or rejects each one based how many values you still need and how many candidates still remain.
I've tested this and it really works. It requires less intermediate storage than shuffling and fewer iterations than the original acceptance/rejection.
Generate a list of elements in the range, remove the element you want in your random series. Repeat x times.
Or you can generate an array with the odd numbers in the range, then do a shuffle
Generation is easy:
$range_array = array();
for( $i = 0; $i < $max_value; $i++){
$range_array[] .= $i*2 + 1;
}
Shuffle
shuffle( $range_array );
splice out the x first elements.
$result = array_slice( $range_array, 0, $x );
This is a complete solution.
function mt_rands($min_rand, $max_rand, $num_rand){
if(!is_integer($min_rand) or !is_integer($max_rand)){
return false;
}
if($min_rand >= $max_rand){
return false;
}
if(!is_integer($num_rand) or ($num_rand < 1)){
return false;
}
if($num_rand <= ($max_rand - $min_rand)){
return false;
}
$rands = array();
while(count($rands) < $num_rand){
$loops = 0;
do{
++$loops; // loop limiter, use it if you want to
$rand = mt_rand($min_rand, $max_rand);
}while(in_array($rand, $rands, true));
$rands[] = $rand;
}
return $rands;
}
// let's see how it went
var_export($rands = mt_rands(0, 50, 5));
Code is not tested. Just wrote it. Can be improved a bit but it's up to you.
This code generates 5 odd unique numbers in the interval [1, 20]. Change $min, $max and $n = 5 according to your needs.
<?php
function odd_filter($x)
{
if (($x % 2) == 1)
{
return true;
}
return false;
}
// seed with microseconds
function make_seed()
{
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
srand(make_seed());
$min = 1;
$max = 20;
//number of random numbers
$n = 5;
if (($max - $min + 1)/2 < $n)
{
print "iterval [$min, $max] is too short to generate $n odd numbers!\n";
exit(1);
}
$result = array();
for ($i = 0; $i < $n; ++$i)
{
$x = rand($min, $max);
//not exists in the hash and is odd
if(!isset($result{$x}) && odd_filter($x))
{
$result[$x] = 1;
}
else//new iteration needed
{
--$i;
}
}
$result = array_keys($result);
var_dump($result);

Comparing large numbers of values using php arrays

I have to compare two very large number of values, for that I put them in arrays but it didn't work. Below is the code I use. Is this the most efficient way? I have set the time and memory to unlimited as well. error 101 (connection reset) unknown error this is error shown by chrome
for ($k = 0; $k < sizeof($pid); $k++) {
$out = 0;
for ($m = 0; $m < sizeof($oid); $m++) {
if ($pid[$k] == $oid[$m]) // $pid have 300000 indexes
//and $oid have about 500000 indexes
{
$out++;
}
}
if ($out) {
echo "OID for ID ".$pid[$k]." = ".$out;
echo "<br>";
}
}
Doesn't work how? Won't give you an answer? You're comparing every possible pair. How many combinations is that? More than 10^13. That will take something like an hour on a modern machine, if you don't run out of memory first.A more efficient way would be to sort them first: NlogN + MlogM + N + M time, instead of N*M time.Sorting a list of size x with a comparison sort takes x*log(x) time. Then, you can walk from the front of each list once, confident that if there are any matches you will find them. This takes linear time.

Categories