Related
Suppose an array is give:
$given_array=Array(
[0] => 30
[1] => 45
[2] => 60
[3] => 75
[4] => 90
[5] => 105
[6] => 120
[7] => 135)
A number is given example: 195
Number until 195 needs to be inserted with a difference of 15
So resulting array is:
Array(
[0] => 30
[1] => 45
[2] => 60
[3] => 75
[4] => 90
[5] => 105
[6] => 120
[7] => 135
[8] => 150
[9] => 165
[10] => 180
[11] => 195)
Need to know the best approach to do so time required is least.
So far i have tried:
$given_num=195;
if((given_num-$given_array[count($given_array)-1])!=15 && count($given_array)>0){
while(($given_array[count($given_array)-1]+15)<=given_num){
$given_array[]=$given_array[count($given_array)-1]+15;
}
}
Results are correct but not time feasible
I see that the goal is to have an array with subsequent numbers divisible by 15. Unless you really have to reuse the old array (I wouldn't know why), I
would suggest creating a new array with range():
$given_array = range($given_array[0], 195, 15);
Haven't tried yours and not sure whats wrong with it regarding speed but try this.
<?php
$given_array=[30, 45, 60, 75, 90, 105, 120, 135];
$newNumber = 195;
$count = floor(($newNumber - $given_array[count($given_array)-1]) / 15);
for($i=0; $i<$count; $i++) {
array_push($given_array, $given_array[count($given_array)-1]+15);
}
print_r($given_array);
example: https://3v4l.org/vPiAW
I would say:
$given_num = 195;
$last = end($given_array);
while ($given_num > $last) {
$last = min($last + 15, $given_num);
$given_array[] = $last;
}
But the range-version is quite nice =)
Another short solution using end function:
while (($last = end($given_array)) < 195) $given_array[] = $last+15;
// now, $given_array contains all the needed items
Given an array and I need to to display the average after every seventh row
Array
(
[0] => Array
(
[date] => 01-03-2015
[site_id] => 1
[starting_reading] => 567
[close_reading] => 567
)
[1] => Array
(
[date] => 03-03-2015
[site_id] => 1
[starting_reading] => 567
[close_reading] => 567
)
[2] => Array
(
[date] => 08-03-2015
[site_id] => 1
[starting_reading] => 567
[close_reading] => 567
)
)
Now what i need, is to display all avg reading in 7 days like:
1 to 7
--------------------- avg=close-start----------
8 to 14
--------------------- avg=close-start----------
15 to 21
--------------------- avg=close-start----------
22 to 28
--------------------- avg=close-start----------
29 to 31
--------------------- avg=close-start----------
date starting_reading close_reading
01-03-2015
03-03-2015
--------------------- avg=close-start----------
08-03-2015
--------------------- avg=close-start----------
//$arr is as the array you described
foreach($arr as $k => $v){
//$k will be some integer 0...count($arr)-1 assuming a 'normal' index
//$v is your child array, where you can access attributes such as $v['date']
//every seventh row can be done in a number of ways, the easiest way is:
if($k % 7 == 0){
//then show new line. This will happen every time the number divides into seven. 0, 7, 14, 21... etc.
var_dump($v); will output your child of the seventh (multiple) item
}
}
Including your example for dates and averages assuming array is correctly formatted
//$arr is as the array you described
$close = 0;
$start = 0;
foreach($arr as $k => $v){
//$k will be some integer 0...count($arr)-1 assuming a 'normal' index
//$v is your child array, where you can access attributes such as $v['date']
//every seventh row can be done in a number of ways, the easiest way is:
if($k % 7 == 0){
$close = 0; //reset close
$start = 0; //reset start
}
$close += (int)$v['close_reading'];
$start += (int)$v['starting_reading'];
if(($k + 1) % 7 == 0){ //this is the last row, 6, 13, 20 etc.
echo ($k-5).' to '.($k+1);
echo 'Average: '.(($close - $start) / 7);
}
}
I have a script that builds an array of week numbers for the last 12 weeks like so:
$week_numbers = range(date('W'), date('W')-11, -1);
However, if the current week number is 1, then this will return an array like so:
Array
(
[0] => 1
[1] => 0
[2] => -1
[3] => -2
[4] => -3
[5] => -4
[6] => -5
[7] => -6
[8] => -7
[9] => -8
[10] => -9
[11] => -10
)
But I need this array to look like this instead:
Array
(
[0] => 1
[1] => 52
[2] => 51
[3] => 50
[4] => 49
[5] => 48
[6] => 47
[7] => 46
[8] => 45
[9] => 44
[10] => 43
[11] => 42
)
Can anyone see a simple solution to this?
I have thought about doing something like this (not tested):
$current_week_number = date('W');
if($current_week_number<12){
// Calculate the first range of week numbers (for current year)
$this_year_week_numbers = range(date('W'), 1, -1);
// Calculate the next range of week numbers (for last year)
$last_year_week_numbers = range(52, 52-(11-$current_week_number), -1);
// Combine the two arrays to return the week numbers for the last 12 weeks
$week_numbers = array_merge($this_year_week_numbers,$last_year_week_numbers);
}else{
// Calculate the week numbers the easy way
$week_numbers = range(date('W'), date('W')-11, -1);
}
one idea
$i = 1;
while ($i <= 11) {
echo date('W', strtotime("-$i week")); //1 week ago
$i++;
}
if you arent scared of loops you can do this:
$week_numbers = range(date('W'), date('W')-11, -1);
foreach($week_numbers as $key => $value) { if($value < 1) $week_numbers[$key] += 52; }
You can do a modulo % trick:
$week_numbers = range(date('W'), date('W')-11, -1);
foreach ($week_numbers as $i => $number) {
$week_numbers[$i] = (($week_numbers[$i] + 52 - 1) % 52) + 1;
}
// -1 +1 is to change the range from 0-51 to 1-52
I've found that using modulo like this is often useful for date calculations, you can something similar for months, using 12.
Well, I think the easiest way is to create array after getting dates:
$week_numbers = array_map(function($iDay)
{
return ($iDay+52)%52?($iDay+52)%52:52;
}, range(date('W'), date('W')-11));
-note, that you can not do just % since 52%52 will be 0 (and you want 52)
I need to generate random UNIQUE numbers within a range, how can I do that? I can generate random number by
generator:
$arr = [];
$x = rand($min, $max);
$len = count($arr);
$flag = 0;
for($i = 0; $i < $len; $i++)
{
if ($flag === 1)
goto generator;
if ($x === $arr[$i])
$flag = 1;
}
$arr[$index] = $x;
$index++;
goto generator;
I know this code is bad, so I need a better optimized code of my version !
help !
example:
if i need to generate 3 numbers within 1 to 15 they should be like 5, 9, 1 but not 3,1,2 [with in 1 - 3 (numbers i want to generate) ]
Array with range of numbers at random order:
$numbers = range(1, 20);
shuffle($numbers);
Wrapped function:
function UniqueRandomNumbersWithinRange($min, $max, $quantity) {
$numbers = range($min, $max);
shuffle($numbers);
return array_slice($numbers, 0, $quantity);
}
Example:
<?php
print_r( UniqueRandomNumbersWithinRange(0,25,5) );
?>
Result:
Array
(
[0] => 14
[1] => 16
[2] => 17
[3] => 20
[4] => 1
)
$len = 10; // total number of numbers
$min = 100; // minimum
$max = 999; // maximum
$range = []; // initialize array
foreach (range(0, $len - 1) as $i) {
while(in_array($num = mt_rand($min, $max), $range));
$range[] = $num;
}
print_r($range);
I was interested to see how the accepted answer stacks up against mine. It's useful to note, a hybrid of both may be advantageous; in fact a function that conditionally uses one or the other depending on certain values:
# The accepted answer
function randRange1($min, $max, $count)
{
$numbers = range($min, $max);
shuffle($numbers);
return array_slice($numbers, 0, $count);
}
# My answer
function randRange2($min, $max, $count)
{
$i = 0;
$range = array();
while ($i++ < $count) {
while(in_array($num = mt_rand($min, $max), $range));
$range[] = $num;
}
return $range;
}
echo 'randRange1: small range, high count' . PHP_EOL;
$time = microtime(true);
randRange1(0, 9999, 5000);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;
echo 'randRange2: small range, high count' . PHP_EOL;
$time = microtime(true);
randRange2(0, 9999, 5000);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;
echo 'randRange1: high range, small count' . PHP_EOL;
$time = microtime(true);
randRange1(0, 999999, 6);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;
echo 'randRange2: high range, small count' . PHP_EOL;
$time = microtime(true);
randRange2(0, 999999, 6);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;
The results:
randRange1: small range, high count
0.019910097122192
randRange2: small range, high count
1.5043621063232
randRange1: high range, small count
2.4722430706024
randRange2: high range, small count
0.0001051425933837
If you're using a smaller range and a higher count of returned values, the accepted answer is certainly optimal; however as I had expected, larger ranges and smaller counts will take much longer with the accepted answer, as it must store every possible value in range. You even run the risk of blowing PHP's memory cap. A hybrid that evaluates the ratio between range and count, and conditionally chooses the generator would be the best of both worlds.
The idea consists to use the keys, when a value is already present in the array keys, the array size stays the same:
function getDistinctRandomNumbers ($nb, $min, $max) {
if ($max - $min + 1 < $nb)
return false; // or throw an exception
$res = array();
do {
$res[mt_rand($min, $max)] = 1;
} while (count($res) !== $nb);
return array_keys($res);
}
Pro: This way avoids the use of in_array and doesn't generate a huge array. So, it is fast and preserves a lot of memory.
Cons: when the rate (range/quantity) decreases, the speed decreases too (but stays correct). For a same rate, relative speed increases with the range size.(*)
(*) I understand that fact since there are more free integers to select (in particular for the first steps), but if somebody has the mathematical formula that describes this behaviour, I am interested by, don't hesitate.
Conclusion: The best "general" function seems to be a mix between this function and #Anne function that is more efficient with a little rate. This function should switch between the two ways when a certain quantity is needed and a rate (range/quantity) is reached. So the complexity/time of the test to know that, must be taken in account.
If you want to generate 100 numbers that are random, but each number appearing only once, a good way would be to generate an array with the numbers in order, then shuffle it.
Something like this:
$arr = array();
for ($i=1;$i<=101;$i++) {
$arr[] = $i;
}
shuffle($arr);
print_r($arr);
Output will look something like this:
Array
(
[0] => 16
[1] => 93
[2] => 46
[3] => 55
[4] => 18
[5] => 63
[6] => 19
[7] => 91
[8] => 99
[9] => 14
[10] => 45
[11] => 68
[12] => 61
[13] => 86
[14] => 64
[15] => 17
[16] => 27
[17] => 35
[18] => 87
[19] => 10
[20] => 95
[21] => 43
[22] => 51
[23] => 92
[24] => 22
[25] => 58
[26] => 71
[27] => 13
[28] => 66
[29] => 53
[30] => 49
[31] => 78
[32] => 69
[33] => 1
[34] => 42
[35] => 47
[36] => 26
[37] => 76
[38] => 70
[39] => 100
[40] => 57
[41] => 2
[42] => 23
[43] => 15
[44] => 96
[45] => 48
[46] => 29
[47] => 81
[48] => 4
[49] => 33
[50] => 79
[51] => 84
[52] => 80
[53] => 101
[54] => 88
[55] => 90
[56] => 56
[57] => 62
[58] => 65
[59] => 38
[60] => 67
[61] => 74
[62] => 37
[63] => 60
[64] => 21
[65] => 89
[66] => 3
[67] => 32
[68] => 25
[69] => 52
[70] => 50
[71] => 20
[72] => 12
[73] => 7
[74] => 54
[75] => 36
[76] => 28
[77] => 97
[78] => 94
[79] => 41
[80] => 72
[81] => 40
[82] => 83
[83] => 30
[84] => 34
[85] => 39
[86] => 6
[87] => 98
[88] => 8
[89] => 24
[90] => 5
[91] => 11
[92] => 73
[93] => 44
[94] => 85
[95] => 82
[96] => 75
[97] => 31
[98] => 77
[99] => 9
[100] => 59
)
If you need 5 random numbers between 1 and 15, you should do:
var_dump(getRandomNumbers(1, 15, 5));
function getRandomNumbers($min, $max, $count)
{
if ($count > (($max - $min)+1))
{
return false;
}
$values = range($min, $max);
shuffle($values);
return array_slice($values,0, $count);
}
It will return false if you specify a count value larger then the possible range of numbers.
You can try next code:
function unique_randoms($min, $max, $count) {
$arr = array();
while(count($arr) < $count){
$tmp =mt_rand($min,$max);
if(!in_array($tmp, $arr)){
$arr[] = $tmp;
}
}
return $arr;
}
Get a random number. Is it stored in the array already? If not, store it. If so, then go get another random number and repeat.
When creating an application where I needed to generate 30,000 unique numbers within a larger range, I was able to cut down processing time from 25 seconds to 1.5 seconds using this method.
The idea is that PHP is much faster at generating random numbers than it is at checking the existence of items in an array - that is why using a while(in_array) loop can be slow.
$count = 0;
$collectNumbers = [];
while ($count < 30000) {
for ($i = 0; $i < 60000; $i++) {
$rand = mt_rand(1, 100000);
$collectNumbers[] = $rand;
}
$unique = array_unique($collectNumbers);
$count = count($unique);
}
$finalArray = array_slice($unique, 0, 30000);
This will return 30,000 unique numbers extremely quickly. Using an iteration value that is double the amount of numbers you need to generate can increase the likelihood of unique numbers the first iteration... but regardless of how many iterations it takes, this will produce a result faster than checking your array for repeated numbers in a loop.
This probably will solve your problem:
<?php print_r(array_rand(range(1,50), 5)); ?>
I guess this is probably a non issue for most but I tried to solve it. I think I have a pretty decent solution. In case anyone else stumbles upon this issue.
function randomNums($gen, $trim, $low, $high)
{
$results_to_gen = $gen;
$low_range = $low;
$high_range = $high;
$trim_results_to= $trim;
$items = array();
$results = range( 1, $results_to_gen);
$i = 1;
foreach($results as $result)
{
$result = mt_rand( $low_range, $high_range);
$items[] = $result;
}
$unique = array_unique( $items, SORT_NUMERIC);
$countem = count( $unique);
$unique_counted = $countem -$trim_results_to;
$sum = array_slice($unique, $unique_counted);
foreach ($sum as $key)
{
$output = $i++.' : '.$key.'<br>';
echo $output;
}
}
randomNums(1100, 1000 ,890000, 899999);
This is how I would do it.
$randnum1 = mt_rand(1,20);
$nomatch = 0;
while($nomatch == 0){
$randnum2 = mt_rand(1,20);
if($randnum2 != $randnum1){
$nomatch = 1;
}
}
$nomatch = 0;
while($nomatch == 0){
$randnum3 = mt_rand(1,20);
if(($randnum3 != $randnum1)and($randnum3 != $randnum2)){
$nomatch = 1;
}
}
Then you can echo the results to check
echo "Random numbers are " . $randnum1 . "," . $randnum2 . ", and " . $randnum3 . "\n";
The "shuffle" method has a MAJOR FALW. When the numbers are big, shuffle 3 billion indexs will instantly CAUSE 500 error. Here comes a best solution for really big numbers.
function getRandomNumbers($min, $max, $total) {
$temp_arr = array();
while(sizeof($temp_arr) < $total) $temp_arr[rand($min, $max)] = true;
return $temp_arr;
}
Say I want to get 10 unique random numbers from 1 billion to 4 billion.
$random_numbers = getRandomNumbers(1000000000,4000000000,10);
PS: Execution time: 0.027 microseconds
Simply use this function and pass the count of number you want to generate
Code:
function randomFix($length)
{
$random= "";
srand((double)microtime()*1000000);
$data = "AbcDE123IJKLMN67QRSTUVWXYZ";
$data .= "aBCdefghijklmn123opq45rs67tuv89wxyz";
$data .= "0FGH45OP89";
for($i = 0; $i < $length; $i++)
{
$random .= substr($data, (rand()%(strlen($data))), 1);
}
return $random;}
The best way to generate the unique random number is
<?php
echo md5(uniqid(mt_rand(), true).microtime(true));
?>
I'm trying to find each missing number in an array like the following.
Array (
[0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 6 [6] => 7 [7] => 8
[8] => 9 [9] => 10 [10] => 11 [11] => 12 [12] => 13 [13] => 14 [14] => 15
[15] => 16 [16] => 17 [17] => 18 [18] => 19 [19] => 20 [20] => 21 [21] => 22
[22] => 23 [23] => 24 [24] => 25 [25] => 26 [26] => 27 [27] => 28 [28] => 29
[29] => 30 [30] => 31 [31] => 32 [32] => 33 [33] => 34 [34] => 35 [35] => 36
[36] => 37 [37] => 38 [38] => 39 [39] => 40 [40] => 41 [41] => 42 [42] => 43
[43] => 44 [44] => 45 [45] => 46 [46] => 47 [47] => 48 [48] => 49 [49] => 50
[50] => 51 [51] => 52 [52] => 53 [53] => 54 [54] => 55 [55] => 56 [56] => 57
[57] => 58 [58] => 59 [59] => 60 [60] => 61 [61] => 62 [62] => 63 [63] => 64
[64] => 67 [65] => 68 [66] => 69
)
The numbers 65,66 are missing in this particular array.
My question how do I figure out which numbers are missing with the help of PHP. Specifically what I need to find out is the lowest missing number.
Why: Because then I can assign that number to a member as an id.
You can make use of array_diff and range functions as:
// given array. 3 and 6 are missing.
$arr1 = array(1,2,4,5,7);
// construct a new array:1,2....max(given array).
$arr2 = range(1,max($arr1));
// use array_diff to get the missing elements
$missing = array_diff($arr2,$arr1); // (3,6)
I'm assuming the number is the element, not the key, of the array. I'm also assuming that the numbers start from 1, not 0.
$Expected = 1;
foreach ($InputArray as $Key => $Number)
{
if ($Expected != $Number)
{
break;
}
$Expected++;
}
echo $Number;
For big sorted arrays of unique numbers, you can binary search the array for either the lowest or highest unused number. Cost=Log2N. Example: 65536 items can be searched in 16 loops since
if ( arr[hi] - arr[lo] > hi - lo )
... there are unused numbers in that range ...
So (I don't know PHP, but it can be translated...):
lo = first entry index
hi = last entry index
if ( arr[hi] - arr[lo] == hi - lo )
return arr[hi]+1; // no gaps so return highest + 1
do
{
mid = (lo + hi) / 2;
if ( arr[mid] - arr[lo] > mid - lo ) // there is a gap in the bottom half somewhere
hi = mid; // search the bottom half
else
lo = mid; // search the top half
} while ( hi > lo + 1 ); // search until 2 left
return arr[lo]+1;
If given input is not in sorted order and size of input is very large then we can use following logic in any programming language:
Algorithm
bring smaller chunk into memory from large input
initialize three variables say min = 0, max = 0 and missingIds = []
scan smaller chunked input from left to right
if scannedValue found in missingIds
then,
pop scannedValue from missingIds
go to next value;
If scanned value is near to min
then,
find all the missing numbers between scannedValue and min, push into missingIds
min = scannedValue;
Else if scanned value is near to max
then,
find all the missing numbers between scannedValue and max, push into missingIds
max = scannedValue;
repeat above steps until large input scanned from left to right
Example in PHP
<?php
$largeInput = [40,41,42,43,44,45,1,2,3,4,5,6,7,8,9,10,11,12,13,14,35,36,37,38,39,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,67,68,69,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34];
$missingIds = [];
$min = 0;
$max = 0;
$chunkSize = 10;
$chunkNo = 0;
$currentInput = array_slice($largeInput, $chunkNo, $chunkSize);
while(count($currentInput) > 0) {
foreach($currentInput as $id) {
if(in_array($id,$missingIds)) {
$missingIds = array_diff($missingIds,[$id]);
continue;
}
if($id <= $min) {
$distMin = $min - $id;
if($distMin > 2) {
$tempArr = range($id+1,$min-1);
$missingIds = array_merge($missingIds, $tempArr);
$tempArr = [];
} else if ($distMin > 1) {
$tempArr = [$id+1];
$missingIds = array_merge($missingIds, $tempArr);
$tempArr = [];
}
$min = $id;
} else if ($id >= $max){
$distMax = $id - $max;
if($distMax > 2) {
$tempArr = range($max+1,$id-1);
$missingIds = array_merge($missingIds, $tempArr);
$tempArr = [];
} else if ($distMax > 1) {
$tempArr = [$max+1];
$missingIds = array_merge($missingIds, $tempArr);
$tempArr = [];
}
$max = $id;
}
}
$chunkNo++;
$currentInput = array_slice($largeInput, $chunkNo, $chunkSize);
}
print_r($missingIds);
//$idArrayMissing = array([0] => 1, [1] => 2, [2] => 4, [3] => 5, [4] => 6, [5] => 7);
$idArrayMissing = array(1, 2, 4, 5, 6, 7);
//$idArrayFull = array([0] => 1, [1] => 2, [2] => 3, [3] => 4, [4] => 5, [5] => 6);
$idArrayFull = array(1, 2, 3, 4, 5, 6);
function gap($arr)
{
while (list($k, $v) = each($arr))
if ($k != ($v-1))
return $k;
return -1;
}
print "ok:" . gap($idArrayMissing) . "<br/>\n";
print "full:" . gap($idArrayFull) . "<br/>\n";
The return of the gap function can be 2 values:
-1 could indicate that the array has been traversed and there are no free slots or
$k+1 which could indicate that the first free slot is on the end of the array.
It can also be done easily by using in_array() function like this:
// lets say $InputArray has all the data
// lets declare a variable which we will search inside the $InputArray array and lets initialize it with either 0 or 1 or with the minimum value found inside $InputArray
$start_counting = 1;
$max_value = count($InputArray);
if (!(in_array($start_counting, $InputArray)))
{
echo "Value: ".$start_counting." is missing!"."<br>" ;
}
else{
if($start_counting <= $max_value -1)
{$start_counting++;}
}
else if($start_counting > $max_value -1)
{
echo "All missing numbers printed!"
}
}