Why is range() not inclusive when given with float range and interval? - php

The documentation states that the $end of the range is inclusive. And this is the case most of the time, but when both $end and $step are floats, the last value is missing. Why is that?
print_r(range(1, 13, 1));
print_r(range(1, 13, 0.1));
print_r(range(0.1, 1.3, 0.1));
Output:
Array
(
[0] => 1
[1] => 2
// ...
[11] => 12
[12] => 13
)
Array
(
[0] => 0.1
[1] => 0.2
// ...
[119] => 12.9
[120] => 13
)
Array
(
[0] => 0.1
[1] => 0.2
// ...
[10] => 1.1
[11] => 1.2
// 12 => 1.3 is missing
)

The range is inclusive; however, your assumptions about the numbers adding up are incorrect.
0.1 cannot be represented in binary with exact precision. When you use it in a calculation in php, you'll actually get a number that's a little higher or lower. Take a look at the following codepad:
http://codepad.org/MkoWgAA1
<?php
$sum = 1.0 + 0.1 + 0.1;
if ($sum > 1.2) {
print("1.2 > 1.2");
} else if ($sum < 1.2) {
print("1.2 < 1.2");
} else {
print("1.2 == 1.2");
}
Output:
1.2 > 1.2

Related

Divide list of numbers into given number parts with certain range

I am working on a project where I have to divide numbers into certain parts.
right now I have 2 numbers max number: 300 and min number 240.
Here a person can add any number such as 5 so all numbers will show in the range such as 300, 285,270,255,240.
My code is:
$amp_starting_price = 300;
$amp_lowest_accepted_price = 240;
$quantity_available = 5);
$r = range($amp_lowest_accepted_price, $amp_starting_price, $quantity_available);
Output: Array (
[0] => 240
[1] => 245
[2] => 250
[3] => 255
[4] => 260
[5] => 265
[6] => 270
[7] => 275
[8] => 280
[9] => 285
[10] => 290
[11] => 295
[12] => 300 )
This is how I need it to show. Divide into 5 or any given number of parts into a particular range such as 300 to 240.
Output: Array (
[0] => 240
[3] => 255
[6] => 270
[9] => 285
[12] => 300 )
I have edited my first answer to fully fit the desired output:
<?php
// set number of parts including the start point
$parts = 5;
// set the increment
$increment = 5;
// set max and min values of the range
$amp_starting_price = 300;
$amp_lowest_accepted_price = 240;
// check if division is exact and all parts will be equal in number
if (($mod = fmod(($amp_starting_price-$amp_lowest_accepted_price), ($parts-1))) == 0) {
$quantity_available = ($amp_starting_price-$amp_lowest_accepted_price)/($parts-1);
} else {
die("Error: Division is not exact. Exist a floating point remainder: $mod\n");
}
// get the increment range
$r_increment = range($amp_lowest_accepted_price, $amp_starting_price, $increment);
// get the parts range
$r_parts = range($amp_lowest_accepted_price, $amp_starting_price, $quantity_available);
// output the result
print_r(array_intersect($r_increment,$r_parts));
It outputs this result:
Array
(
[0] => 240
[3] => 255
[6] => 270
[9] => 285
[12] => 300
)

Generate equal set of number minus to plus with PHP

i need help to create set of numbers below with PHP.
example:
$i = 7
result:
-3,-2,-1,0,1,2,3
Another example:
$i = 3
result:
-1,0,1
Thanks
You can use range, with minimum/maximum values of +/-floor($i/2) e.g.
$i = 7;
print_r(range(-floor($i / 2), floor($i / 2)));
Output:
Array
(
[0] => -3
[1] => -2
[2] => -1
[3] => 0
[4] => 1
[5] => 2
[6] => 3
)
If you want it to work with even numbers, you have to decide whether you want the range to have more negative values or positive values, and adjust the min/max accordingly:
$i = 4;
print_r(range(-floor($i / 2), floor($i / 2) - 1));
$i = 4;
print_r(range(1 - floor($i / 2), floor($i / 2)));
Output:
Array
(
[0] => -2
[1] => -1
[2] => 0
[3] => 1
)
Array
(
[0] => -1
[1] => 0
[2] => 1
[3] => 2
)
Demo on 3v4l.org
Once you decide which direction you prefer to have more values if the input is even, you can make the code work for even & odd values by using % to determine if the input is odd or even. For more negative values:
print_r(range(-floor($i / 2), floor($i / 2) - ($i + 1) % 2));
For more positive values:
print_r(range(($i + 1) % 2 - floor($i / 2), floor($i / 2)));
Demo on 3v4l.org

Why for with float step dont do last iteration?

Why does my for loop with a float step (0.1) don't do the last iteration in PHP - any version?
My test code:
$procents_list = [];
for($i = 0.5; $i <= 1.5; $i += 0.1)
{
$procents_list[] = $i;
}
print_r($procents_list);
Iteration from 5 to 15 with step 1 all ok.
Why that print:
Array
(
[0] => 0.5
[1] => 0.6
[2] => 0.7
[3] => 0.8
[4] => 0.9
[5] => 1
[6] => 1.1
[7] => 1.2
[8] => 1.3
[9] => 1.4
)
Computers use Base-2 (0,1) instead of Base-10 (0,1,2,...9) to represent numbers. In Base-10, some numbers cannot be represented exactly (like 1/3=0.33333). In Base-2, a different set of numbers cannot be represented exactly. In fact, 1/10, which is 0.1 in decimal, has no exact representation in binary. So, math with floats can do weird things and should never be taken as exact values.
I couldn't figure out how to get PHP to print out all the digits without rounding, but writing the same example in Java prints out the following values:
0.5
0.6
0.70000005
0.8000001
0.9000001
1.0000001
1.1000001
1.2000002
1.3000002
1.4000002
So, the value you were getting was slightly larger than 1.5 and the loop thus ended.
In this case, one alternative is to just use integers for the loop and divide by 10 when putting them in the array:
$procents_list = [];
for ($i = 5; $i <= 15; $i += 1)
{
$procents_list[] = $i / 10;
}
print_r($procents_list);

Sorting a multidimensional array for ranking using PHP in CodeIgniter?

I'm looking to use the results in an array and sort them using a ranking algorithm similar to say Hacker News or Reddit.
For example, paraphrasing the HN algorithm:
Score = P / (T+2)^G
where,
P = points of an item (e.g. votes + comments + likes)
T = time since submission (in hours)
G = Gravity, (on HN defaults to 1.8)
From what I understand, I need to use a PHP sorting array, but the PHP manual is confusing, and the similar answers on StackOverflow have very specific answers without many comments as to what the function is doing. e.g. here, here & here.
My data looks as follows
Array
(
[0] => Array
(
[post_created] => 2011-12-12 07:18:17
[post_num_likes] => 1
[post_num_comments] => 0
[post_message] => Some message
[votes] => 16
)
[1] => Array
(
[post_created] => 2011-12-11 22:17:16
[post_num_likes] => 0
[post_num_comments] => 4
[post_message] => Another message
[votes] => 21
)
[2] => Array
(
[post_created] => 2011-12-11 20:21:11
[post_num_likes] => 1
[post_num_comments] => 2
[post_message] => Next message
[votes] => 1
)
[3] => Array
(
[post_created] => 2011-12-11 20:11:47
[post_num_likes] => 0
[post_num_comments] => 0
[post_message] => Something else
[votes] => 0
)
[4] => Array
(
[post_created] => 2011-12-11 20:09:46
[post_num_likes] => 1
[post_num_comments] => 0
[post_message] => Another message
[votes] => 5
)
So far as I understand, I need to do something like the following:
// Send array as 2nd parameter due to way you call functions in CodeIgniter
uksort($array, array('Class_name','postrank'));
function postrank($a, $b) {
// some sorting function here
return strcmp($a, $b);
}
I've tried copying and pasting various functions, but as they're not so well commented, it's hard to know what's going on.
How might I go about recreating a similar post ranking sorting function with the above data?
amidoinitrite?
SELECT * , (
votes + post_num_comments + post_num_likes - 1
) / POW( ROUND( (
TIME_TO_SEC( TIMEDIFF( NOW( ) , post_created ) ) / 3600 ) + 2 ) , 1.8
) AS score
FROM hacker_news
ORDER BY score DESC
The formula on website is actually: Score = (P-1) / (T+2) ^ G
My example code for PHP older than 5.3 :)
<?php
function customSort($a, $b){
foreach(array('a', 'b') as $vn){
$tmp = $$vn;
$p = $tmp['post_num_likes'] + $tmp['post_num_comments'] + $tmp['votes'];
$t = ceil((time() - strtotime($tmp['post_created'])) / 3600);
$$vn = $p / pow($t + 2, 1.81);
}
return $a - $b;
}
usort($array, 'customSort');
var_dump($array);
$score = function($arr) {
extract($arr);
return ($votes + $comments + $likes) / pow($created_time + 2, 1.8);
};
usort($array, function($a, $b) use ($score) {
return $score($a) - $score($b);
});
This needs php 5.3 because I used anonymous functions.

Find missing numbers in array

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!"
}
}

Categories