I've been thinking about ways to program for exteme distances, in the game Hellion for example, orbits could be near to scale in the ranges of millions of kilometers. However there was a common glitch where movement would be very choppy the further you were from the object you were orbiting. I might be wrong in my speculation to why that was, but my best guess was that it was down to loss of precision at that distance.
As a little exercise I've been thinking about ways to solve that problem and what I currently have is a a pretty basic unit staged distance system.
class Distance
{
public const MAX_AU = 63018.867924528;
public const MAX_MM = 149598000000000;
private $ly = 0;
private $au = 0;
private $mm = 0;
public function add(Distance $add): Distance
{
$distance = new Distance();
$distance->mm = $this->mm + $add->mm;
if ($distance->mm > self::MAX_MM) {
$distance->mm-= self::self::MAX_MM;
$distance->au++;
}
$distance->au+= $this->au + $add->au;
if ($distance->au > self::MAX_AU) {
$distance->au-= self::self::MAX_AU;
$distance->ly++;
}
$distance->ly+= $this->ly + $add->ly;
return $distance;
}
}
I put in the addition method, which is written as though by hand. I didn't want to use arbitary precision because it would have been too extreme for calculating the smaller distances a player would normally interact with.
My question is; is this how something like this is normally done? and if not, could someone please explain what is wrong with this (inefficient for example), and how it could be done better?
Thanks
PS. I am aware that in the context of a game, this is normally handled with sub-grids, but to simulate how objects in orbit would drift apart, thats what this is for.
You can use the BCMath functions. BCMath supports numbers of any size and precision.
Example:
$mm = "149598000000000";
$au = "63018.867924528";
$sum = bcadd($mm,$au,10);
echo $sum;
//149598000063018.8679245280
Related
During deep researching about hash and zval structure and how arrays are based on it, faced with strange insert time.
Here is example:
$array = array();
$someValueToInsert = 100;
for ($i = 0; $i < 10000; ++$i) {
$time = microtime(true);
array_push($array, $someValueToInsert);
echo $i . " : " . (int)((microtime(true) - $time) * 100000000) . "</br>";
}
So, I found that every 1024, 2024, 4048... element will be inserted using much more time(>~x10).
It doesn't depends will I use array_push, array_unshift, or simply $array[] = someValueToInsert.
I'm thinking about that in Hash structure:
typedef struct _hashtable {
...
uint nNumOfElements;
...
} HashTable;
nNumOfElements has default max value, but it doesn't the answer why does it took more time to insert in special counters(1024, 2048...).
Any thoughts ?
While I would suggest double checking my answer on the PHP internals list, I believe the answer lay in zend_hash_do_resize(). When more elements are needed in the hash table, this function is called and the extant hash table is doubled in size. Since the table starts life at 1024, this doubling explains the results you've observed. Code:
} else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
void *old_data = HT_GET_DATA_ADDR(ht);
Bucket *old_buckets = ht->arData;
HANDLE_BLOCK_INTERRUPTIONS();
ht->nTableSize += ht->nTableSize;
ht->nTableMask = -ht->nTableSize;
HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), ht->u.flags & HASH_FLAG_PERSISTENT));
memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
pefree(old_data, ht->u.flags & HASH_FLAG_PERSISTENT);
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
I am uncertain if the remalloc is the performance hit, or if the rehashing is the hit, or the fact that the whole block is uninterruptable. Would be interesting to put a profiler on it. I think some might have already done that for PHP 7.
Side note, the Thread Safe version does things differently. I'm not overly familiar with that code, so there may be a different issue going on if your using ZTS.
I think it is related to implementation of dynamic arrays.
See here "Geometric expansion and amortized cost" http://en.wikipedia.org/wiki/Dynamic_array
To avoid incurring the cost of resizing many times, dynamic arrays resize by a large amount, **such as doubling in size**, and use the reserved space for future expansion
You can read about arrays in PHP here as well https://nikic.github.io/2011/12/12/How-big-are-PHP-arrays-really-Hint-BIG.html
It is a standard practice for dynamic arrays. E.g. check here C++ dynamic array, increasing capacity
capacity = capacity * 2; // doubles the capacity of the array
I need to go through an array containing points in a map and check their distance from one another. I need to count how many nodes are within 200m and 50m of each one. It works fine for smaller amounts of values. However when I tried to run more values through it (around 4000 for scalability testing) an error occurs saying that I have reached the maximum execution time of 300 seconds. It needs to be able to handle at least this much within 300 seconds if possible.
I have read around and found out that there is a way to disable/change this limit, but I would like to know if there is a simpler way of executing the following code so that the time it takes to run it will decrease.
for($i=0;$i<=count($data)-1;$i++)
{
$amount200a=0;
$amount200p=0;
$amount50a=0;
$amount50p=0;
$distance;
for($_i=0;$_i<=count($data)-1;$_i++)
{
$distance=0;
if($data[$i][0]===$data[$_i][0])
{
}
else
{
//echo "Comparing ".$data[$i][0]." and ".$data[$_i][0]." ";
$lat_a = $data[$i][1] * PI()/180;
$lat_b = $data[$_i][1] * PI()/180;
$long_a = $data[$i][2] * PI()/180;
$long_b = $data[$_i][2] * PI()/180;
$distance =
acos(
sin($lat_a ) * sin($lat_b) +
cos($lat_a) * cos($lat_b) * cos($long_b - $long_a)
) * 6371;
$distance*=1000;
if ($distance<=50)
{
$amount50a++;
$amount200a++;
}
else if ($distance<=200)
{
$amount200a++;
}
}
}
$amount200p=100*number_format($amount200a/count($data),2,'.','');
$amount50p=100*number_format($amount50a/count($data),2,'.','');
/*
$dist[$i][0]=$data[$i][0];
$dist[$i][1]=$amount200a;
$dist[$i][2]=$amount200p;
$dist[$i][3]=$amount50a;
$dist[$i][4]=$amount50p;
//*/
$dist.=$data[$i][0]."&&".$amount200a."&&".$amount200p."&&".$amount50a."&&".$amount50p."%%";
}
Index 0 contains the unique ID of each node, 1 contains the latitude of each node and
index 2 contains the longitude of each node.
The error occurs at the second for loop inside the first loop. This loop is the one comparing the selected map node to other nodes. I am also using the Haversine Formula.
first of all, you are performing in big O notation: O(data^2), which is gonna be slow as hell , and really, either there are 2 possible solutions. Find a proven algorithm that solves the same problem in a better time. Or if you cant, start moving stuff out of the innner for loop, and mathmatically prove if you can convert the inner for loop to mostly simple calculations, which is often something you can do.
after some rewriting, I see some possiblities:
If $data is not a SPLFixedArray (which has a FAR Better access time, ) then make it. since you are accessing that data so many times (4000^2)*2.
secound, write cleaner code. although the optizmier will do its best, if you dont try either to minize the code (which only makes it more readable), then it might not be able to do it as well as possible.
and move intermediate results out of the loops, also something like the size of the array.
Currently you're checking all points against all other points, where in fact you only need to check the current point against all remaining points. The distance from A to B is the same as the distance from B to A, so why calculate it twice?
I would probably make an adjacent array that counts how many nodes are within range of each other, and increment pairs of entries in that array after I've calculated that two nodes are within range of each other.
You should probably come up with a very fast approximation of the distance that can be used to disregard as many nodes as possible before calculating the real distance (which is never going to be super fast).
Generally speaking, beyond algorithmic optimisations, the basic rules of optimisation are:
Don't any processing that you don't have to do: Like not multiplying $distance by 1000. Just change the values you're testing against from 20 and 50 to 0.02 and 0.05, respectively.
Don't call any function more often than you have to: You only need to call count($data) once before any processing starts.
Don't calculate constant values more than once: PI()/180, for example.
Move all possible processing outside of loops. I.e. precalculate as much as possible.
Another minor point which will make your code a little easier to read:
for( $i = 0; $i <= count( $data ) - 1; $i++ ) is the same as:
for( $i = 0; $i < count( $data ); $i++ )
Try this:
$max = count($data);
$CONST_PI = PI() / 180;
for($i=0;$i<$max;$i++)
{
$amount200a=0;
$amount50a=0;
$long_a = $data[$i][2] * $CONST_PI;
$lat_a = $data[$i][1] * $CONST_PI;
for($_i=0;$_i<=$max;$_i++)
//or use for($_i=($i+1);$_i<=$max;$_i++) if you did not need to calculate already calculated in other direction
{
$distance=0;
if($data[$i][0]===$data[$_i][0]) continue;
$lat_b = $data[$_i][1] * $CONST_PI;
$long_b = $data[$_i][2] * $CONST_PI;
$distance =
acos(
sin($lat_a ) * sin($lat_b) +
cos($lat_a) * cos($lat_b) * cos($long_b - $long_a)
) * 6371;
if ($distance<=0.2)
{
$amount200a++;
if ($distance<=0.05)
{
$amount50a++;
}
}
} // for %_i
$amount200p=100*number_format($amount200a/$max,2,'.','');
$amount50p=100*number_format($amount50a/$max,2,'.','');
$dist.=$data[$i][0]."&&".$amount200a."&&".$amount200p."&&".$amount50a."&&".$amount50p."%%";
} // for $i
It will be better to read I think and if you change the commented out line of the for $_i it will be faster at all :)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I saw on StackOverflow a "point in polygon" raytracing algorithm that I implemented in my PHP Code. Most of the time, it works well, but in some complicated cases, with complex polygons and vicious points, it fails and it says that point in not in polygon when it is.
For example:
You will find here my Polygon and Point classes: pointInPolygon method is in Polygon class. At the end of the file, there are two points that are supposed to lie inside the given polygon (True on Google Earth). The second one works well, but the first one is buggy :( .
You can easily check the polygon on Google Earth using this KML file.
Have been there :-) I also travelled through Stackoverflow's PiP-suggestions, including your reference and this thread. Unfortunately, none of the suggestions (at least those I tried) were flawless and sufficient for a real-life scenario: like users plotting complex polygons on a Google map in freehand, "vicious" right vs left issues, negative numbers and so on.
The PiP-algorithm must work in all cases, even if the polygon consists of hundreds of thousands of points (like a county-border, nature park and so on) - no matter how "crazy" the polygon is.
So I ended up building a new algorithm, based on some source from an astronomy-app:
//Point class, storage of lat/long-pairs
class Point {
public $lat;
public $long;
function Point($lat, $long) {
$this->lat = $lat;
$this->long = $long;
}
}
//the Point in Polygon function
function pointInPolygon($p, $polygon) {
//if you operates with (hundred)thousands of points
set_time_limit(60);
$c = 0;
$p1 = $polygon[0];
$n = count($polygon);
for ($i=1; $i<=$n; $i++) {
$p2 = $polygon[$i % $n];
if ($p->long > min($p1->long, $p2->long)
&& $p->long <= max($p1->long, $p2->long)
&& $p->lat <= max($p1->lat, $p2->lat)
&& $p1->long != $p2->long) {
$xinters = ($p->long - $p1->long) * ($p2->lat - $p1->lat) / ($p2->long - $p1->long) + $p1->lat;
if ($p1->lat == $p2->lat || $p->lat <= $xinters) {
$c++;
}
}
$p1 = $p2;
}
// if the number of edges we passed through is even, then it's not in the poly.
return $c%2!=0;
}
Illustrative test :
$polygon = array(
new Point(1,1),
new Point(1,4),
new Point(4,4),
new Point(4,1)
);
function test($lat, $long) {
global $polygon;
$ll=$lat.','.$long;
echo (pointInPolygon(new Point($lat,$long), $polygon)) ? $ll .' is inside polygon<br>' : $ll.' is outside<br>';
}
test(2, 2);
test(1, 1);
test(1.5333, 2.3434);
test(400, -100);
test(1.01, 1.01);
Outputs :
2,2 is inside polygon
1,1 is outside
1.5333,2.3434 is inside polygon
400,-100 is outside
1.01,1.01 is inside polygon
It is now more than a year since I switched to the above algorithm on several sites. Unlike the "SO-algorithms" there have not been any complaints so far. See it in action here (national mycological database, sorry for the Danish). You can plot a polygon, or select a "kommune" (a county) - ultimately compare a polygon with thousands of points to thousands of records).
Update
Note, this algorithm is targeting geodata / lat,lngs which can be very precise (n'th decimal), therefore considering "in polygon" as inside polygon - not on border of polygon. 1,1 is considered outside, since it is on the border. 1.0000000001,1.01 is not.
I have a rectangular map, stored as multidimensional array (ie $map[row][col]) and I have to track down which squares are seen by a player, placed anywhere on this map.
Player visibility is circular with unknown radius (but given at run-time) and I only need integer solutions.
I know that circumference formula is
x^2 + y^2 <= r^2
but how can I store everything?
I need these values since then I can "reveal" map squares.
The best would be a multidimesional array (ie __$sol[x][y]__).
This is a piece of code that I'm using. It's not corrected since it assumes that vision is a square and not a circle.
Calculating the square
$this->vision_offsets_2 = array();
//visibility given as r^2
$mx = (int)(sqrt($this->viewradius2));
$mxArr = range($mx * -1, $mx + 1);
foreach ($mxArr as $d_row)
{
foreach ($mxArr as $d_col)
{
$this->vision_offsets_2[] = array($d_row, $d_col);
}
}
This is how I apply that
foreach($player as $bot)
{
foreach($visibility as $offset)
{
$vision_row = $offset[0] + $bot[0];
$vision_col = $offset[1] + $bot[1];
if(isset($map[$vision_row][$vision_col]))
{
if( $map[$vision_row][$vision_col] == UNSEEN) {
$map[$vision_row][$vision_col] = LAND; }
}
}
}
Here you can find the bot view: as you can see is a non perfect circle.
How can I track it? By the way, in this example radius^2 is 55, the orange circle is the player, brown squares are visible ones.
Structure
You're already referencing terrain in a grid. Store terrain objects in those grid values. Apply attributes to those objects. Check with something like
$map[$x][$y]->isVisible($player);
You'll need some methods in there for setting vision and tests for checking the user that is passed against a list of users who can see it. While you're at it, setup other related methods in those objects (I see references to land... isLand() and isWater() perhaps?).
You can even have vision cascade within objects such that you only need to move the position of a user and the object takes care of triggering off all the code to set nearby plots of land to visible.
Math
We are given circumference.
double diameter = circumference / 3.14159
double radius = diameter / 2 //Normally done in one step / variable
Now we must know the distance between two points to compare it. Let's use map[4][7] and map[3][9].
int x0 = 4;
int y0 = 7;
int x1 = 3;
int y1 = 9;
double distance = Math.sqrt(
Math.pow(x0 - x1, 2) +
Math.pow(y0 - y1, 2)
);
System.out.println(distance); //2.23606797749979
Test distance > radius.
Testing each square
Put the above in a method: visibleFrom(Square target)
radius should be a globally accessible static variable when comparing.
Your Square object should be able to hand over its coordinates.
target.getX()
target.getY()
Some optimizations can be had
Only checking things for circular distance when they're in the square.
Not checking anything for circular distance when purely along the x or y axis.
Figuring out the largest square that fits inside the circle and not checking boxes in that range for circular distance.
Remember that premature optimization and over optimization are pitfalls.
A function like this would tell you if a map square is visible (using the distance of the centers of the squares as a metric; if you want to define visibility in another manner, which you probably would, things get much more complicated):
function is_visible($mapX, $mapX, $playerX, $playerY, $r) {
return sqrt(pow($mapX - $playerX, 2) + pow($mapY - $playerY, 2)) <= $r;
}
You probably don't really need to store these values since you can easily calculate them on demand.
I think that Bresenham's circle drawing algorithm is what you're looking for.
I don't know exactly what you want, but here's some things that should help you along. As a warning these are untested, but the logic is sound.
//You mentioned circumference, this will find out the circumference but I don't
//think you actually need it.
$circumference_length = 2 * $visibility_range * 3.1415;
//Plug in the player and target coordinates and how far you can see, this will
//tell you if the player can see it. This can be optimized using your object
//and player Objects.
function canSee($player_x, $player_y, $vision_length, $target_x, $target_y){
$difference_x = $target_x - $player_x;
$difference_y = $target_y - $player_y;
$distance = sqrt((pow($difference_x,2) + pow($difference_y, 2));
if($vision < $distance){
return false;
} else {
return true;
}
}
Edit: In response to your clarification, you can use the above function to figure out if you should show the terrain objects or not.
foreach($player as $bot)
{
foreach($terrain_thing as $terrain)
{
//ASSUMING THAT [0] IS ALWAYS X AND [1] IS ALWAYS y, set a third variable
//to indicate visibility
$terrain["is_visible"] = canSee($bot[0], $bot[1], $visibility_range, $terrain[0], $terrain[1])
}
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 1 year ago.
Improve this question
Is there a software converter out there that can automatically convert this python code to PHP?
#!/usr/bin/python
import math
def calcNumEntropyBits(s):
if len(s) <= 0: return 0.0
symCount = {}
for c in s:
if c not in symCount: symCount[c] = 1
else: symCount[c] += 1
entropy = 0.0
for c,n in symCount.iteritems():
prob = n / float(len(s))
entropy += prob * (math.log(prob)/math.log(2))
if entropy >= 0.0: return 0.0
else: return -(entropy*len(s))
def testEntropy(s):
print "Bits of entropy in '%s' is %.2f" % (s, calcNumEntropyBits(s))
testEntropy('hello world')
testEntropy('bubba dubba')
testEntropy('aaaaaaaaaaa')
testEntropy('aaaaabaaaaa')
testEntropy('abcdefghijk')
I'm not aware of any Python-to-PHP converter in the wild, but it should be a trivial task to port and the similarities are quite easy to spot:
function calcNumEntropyBits($s) {
if (strlen($s) <= 0) return 0.0;
$symCount = array();
foreach (str_split($s) as $c) {
if (!in_array($c,$symCount)) $symCount[$c] = 1;
else $symCount[$c] ++;
}
$entropy = 0.0;
foreach ($symCount as $c=>$n) {
$prob = $n / (float)strlen($s);
$entropy += $prob * log($prob)/log(2);
}
if ($entropy >= 0.0) return 0.0;
else return -($entropy*strlen($s));
}
function testEntropy($s):
printf("Bits of entropy in '%s' is %.2f",$s,calcNumEntropyBits($s));
testEntropy('hello world');
testEntropy('bubba dubba');
testEntropy('aaaaaaaaaaa');
testEntropy('aaaaabaaaaa');
testEntropy('abcdefghijk');
The last few lines in the first function could have also been written as a standard PHP ternary expression:
return ($entropy >= 0.0)? 0.0: -($entropy*strlen($s));
I created a python-to-php converter called py2php. It can auto-translate the basic logic and then you will need to tweak library calls, etc. Still experimental.
Here is auto-generated PHP from the python provided by the OP.
<?php
require_once('py2phplib.php');
require_once( 'math.php');
function calcNumEntropyBits($s) {
if ((count($s) <= 0)) {
return 0.0;
}
$symCount = array();
foreach( $s as $c ) {
if (!$symCount.__contains__($c)) {
$symCount[$c] = 1;
}
else {
$symCount[$c] += 1;
}
}
$entropy = 0.0;
foreach( $symCount->iteritems() as $temp_c ) {
$prob = ($n / float(count($s)));
$entropy += ($prob * (math::log($prob) / math::log(2)));
}
if (($entropy >= 0.0)) {
return 0.0;
}
else {
return -($entropy * count($s));
}
}
function testEntropy($s) {
pyjslib_printFunc(sprintf('Bits of entropy in \'%s\' is %.2f', new pyjslib_Tuple([$s, calcNumEntropyBits($s)])));
}
testEntropy('hello world');
testEntropy('bubba dubba');
testEntropy('aaaaaaaaaaa');
testEntropy('aaaaabaaaaa');
testEntropy('abcdefghijk');
It would not run correctly due to the math import and __contains__, but those would be easy enough to fix by hand.
I am about 1/2 way done making a PHP interpreter in Python and I can tell you flat out that there are literally dozens of major edge cases that play out to thousands of possibilities that would make it almost impossible to port Python to PHP. Python has a much more robust grammar then PHP while further foward in the language, Python's stdlib is probably one of the most advanced in comparison to any other language in it's class.
My recommendation is to take your question one step further back, to why do you need a set of Python based logic in PHP. Alternatives to attempting to port/translate your code could include subprocessing from PHP to Python, using Gearman to have Python do work in the backend while PHP handles view logic, or a much more involved solution would be to implement a service bus or message queue between a PHP application and Python services.
PS. Apologies for any readability issues, finishing a 2 day sprint just now.
No such tool exists, you'll have to port the code yourself
I changed the library py2php from https://github.com/dan-da/py2php and forked it into a new repository at https://github.com/bunkahle/py2php
You now can also use the python math library which is translated to PHP code. Still you have to do adaptations to your code in order to get it to work.
I wondered myself that same question, and I found this PyToPhp.py file in the GitHubGist site. It is simple, and seem an start point for the begining.
I'm going to take a look to it!!!