PHP Pixel Map Group Efficiency - php

I currently have a map that is 1600 x 1600 stored in MySQL(2,560,000 records). I'm rendering a simple 25x25 map out to users for interaction. Users can "claim" tiles on this map. I'd like to be able to calculate the number of open faces for tiles owned by a given user. I can divide this out by the total tiles owned to determine an arbitrary efficientcy rating.
All map coordinates are simply stored as X/Y values.
I'm looking for something that can potentially process an array of said X/Y values and determine how many open faces are accessible for each owned group. For example...
0 = player
x x x x x
x x 0 x x
x x x x x
4 open faces
x x x x x
x x 0 x x
x x 0 x x
x x x x x
6 open faces
x x x x x
x x x 0 x
x x 0 x x
x x x x x
8 open faces
Right now I'm doing some inefficient array looping to calculate this out. I have a simple counter, then I'm looping through an array of all values and am looking for values +-1 in each direction of X and Y to reduce the count. Each loop either adds 0-4 to the total counter based on the number of finds. The inherent problem with this method is that as a group grows, it will take longer and longer to calculate out. Since it's possible for one group to consume 20,000 points o rmore, it's quite a burden.
Any help is greatly appreciated.

One approach would involve creating a Point class. For example:
class Point {
public $x;
public $y;
public function __construct($x, $y){
$this->x = $x;
$this->y = $y;
}
public function getNeighbors(){
// TODO: What if we are at the edge of the board?
return array(
new Point($x+1, $y+1),
new Point($x+1, $y-1),
new Point($x-1, $y+1),
new Point($x-1, $y-1),
);
}
}
Create instances from that class for each point occupied by a user:
// Generate array of Points from database
$user_points = array(new Point(134, 245), new Point(146, 456));
Iterate through to generate all the neighbors:
// Get a flat array of neighbor Points
$neighbors = array_merge(array_map(function($point){
return $point->getNeighbors();
}, $user_points));
// TOOD: Remove Points that are equal to values in $user_points
Then, lastly, submit a COUNT query for the "neighbor" points to determine how many are occupied by other users and remove those from the total.
(Note: I've added TODOs where more work is to be done.)
The inherent problem with this method is that as a group grows, it will take longer and longer to calculate out.
You should consider using an in-memory key-value store, like Redis. But yes, the look-up time (for occupied blocks), in time complexity, appears linear with regard to the number of entries.

Here's the final block of simple code I came up with to determine the geo efficiency. Some names of things have been changed. :P
I'm running with notices on, and everything's ajax, so I decided to go with single isset checks on a multi-dimensional instead of something else.
$sql = 'SELECT map_x, map_y FROM Map WHERE person_id = :person_id';
$query = $DB->prepare($sql);
$query->execute(array(':nation_id' => $this->person_id));
$counter = 0;
$coords = array();
while($row = $query->fetch())
{
++$counter;
$coords[$row['map_x']][$row['map_y']] = 1;
}
$faces = 0;
foreach($coords as $x => $batch)
{
foreach($batch as $y => $junk)
{
$hits = 4;
if(isset($coords[$x + 1][$y]))
{
--$hits;
}
if(isset($coords[$x - 1][$y]))
{
--$hits;
}
if(isset($coords[$x][$y - 1]))
{
--$hits;
}
if(isset($coords[$x][$y + 1]))
{
--$hits;
}
$faces += $hits;
}
}

Related

Find all combinations of x numbers where they sum to Y

I'm trying to write this solution in PHP.
Inputs:
number of numbers = x
smallest number = 1
sum of numbers = y
I'm not dealing with very large numbers, largest x is approximatly 50, largest y is approximatly 80.
rules: Within each set of numbers, the number proceeding the previous must be equal to or greater.
For example
x = 3
min = 1
y = 6
solution:
(1,1,4),(1,2,3)
note that (3,2,1) isn't a solution as they are in descending order.
This is easily solved via recursion. The time complexity though will be high. For a better (but slightly more complex solution) use dynamic programming.
Here's the idea:
If the size of the set is 1 then the only possible solution is the desired sum.
If the set is larger than one then you can merge a number X between the minimum and the desired sum with a set of numbers which add up to the desired sum minus X.
function tuplesThatSumUpTo($desiredSum, $minimumNumber, $setSize) {
$tuples = [];
if ($setSize <= 1) {
return [ [ $desiredSum ] ]; //A set of sets of size 1 e.g. a set of the desired sum
}
for ($i = $minimumNumber;$i < $desiredSum;$i++) {
$partial = tuplesThatSumUpTo($desiredSum-$i, $minimumNumber,$setSize-1);
$tuples = array_merge($tuples, array_map(function ($tuple) use ($i) {
$res = array_merge([$i], $tuple);
sort($res);
return $res;
},$partial));
}
return array_unique($tuples,SORT_REGULAR);
}
See it run:
http://sandbox.onlinephpfunctions.com/code/1b0e507f8c2fcf06f4598005bf87ee98ad2505b3
The dynamic programming approach would have you instead hold an array of sets with partial sums and refer back to it to fill in what you need later on.

Rotate K coplanar points to a plane parallel to x,y plane

I'm working in php with 3D geometries(not the best choice,I know...).
I have K coplanar 3D points, also with x,y,z value. Together they form a polygon. I need to triangulate this polygon. I have already a working delaunay traingulation function which works for 2D Polygons.
So I want to rotate the given points, so that they lay on a plane parallel to the x,y plane. After that I can triangulated it using the x,y values. The following pseudocode shall describe how I want to get to this goal.
I build up the following code with reference on this (I'm usign the answer accepted from the OP): https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d, but it doesn't work as I expected. In order to know if it worked, every mapped point shall then have the same 'z' value.
Here is the question, how do I get the correct rotation matrix? Or did I made a conceptual mistake?
function matrixRotationMapping(Point $p, Point $q, Point $r)
{
$normalPolygon =calculatePlaneNormal($p, $q, $r);
$v = crossProduct($normalPolygon, new Point(0, 0, 1));
$c = dotProduct($normalPolygon, new Point(0, 0, 1));
$matrix = buildRotationMatrix($v, $c);
return $matrix;
}
function buildRotationMatrix($v, $c)
{
$R2 = new Matrix(array(array(1, -$v->z, $v->y), array($v->z, 1, -$v->x), array(-$v->y, $v->x, 1)));
$costant = 1/(1+$c);
$R3 = multiplyMatrices($R2, $R2);
$R3 = multiplyMatricesWithFactor($R3, $costant);
$finalMatrix = sumMatrices($R2, $R3);
return $finalMatrix;
}
function calc2DMapping($points)
{
$rotationMatrix = matrixRotationMapping($points[0], $points[1], $points[2]);
foreach($points as $point)
{
$mappedPoint = $rotationMatrix->multiplyWithPoint($point);
$mappedPoints[] = new MappedPoint($mappedPoint);
}
}
I found another helpful description of the problem, but I wasn't able to implement it: Mapping coordinates from plane given by normal vector to XY plane
Thanks in advance for your attention.
You need basis vectors X,Y,Z first. So let take the mid point A and two distant points to it B,C (not on single line) from your data set first. The X,Y should lie in the plane and Z should be normal to it so:
X = B-A // any non zero vector inside plane
X = X / |X| // unit in size
Y = C-A // any non zero vector inside plane
(X.Y) != 0 // but not parallel to X !!!
Y = Y / |Y| // unit in size
Compute normal to the plane your points lie in and correct Y axis.
Z = X x Y // cross product gives you perpendicular vector
Y = Z x X // now all vectors are perpendicular and unit
So feed these 3 vectors to rotation part of your transform matrix and set origin to A. But as you need to go from your data set to the plane local coordinate you need inverse matrix (or use pseudo inverse based on transposing)
Anyway now with the basis vectors you can map your plane parametrically like this:
P(u,v) = A + u*X + v*Y
Where u,v = <-inf,+inf> are surface distances form A in X,Y directions. That can get handy sometimes. If you need to compute u,v from P then exploit dot product:
u = ((P-A).X) = dot(P-A,X)
v = ((P-A).Y) = dot(P-A,Y)
Which can be also used to transform to 2D instead of using matrix ...

PHP - Optimize finding closest point in an Array

I have created a script which gets a big array of points and then finds the closest point in 3D-space based on a limited array of chosen points. It works great. However, sometimes I get like over 2 Million points to compare to an array of 256 items so it is over 530 million calculations! Which takes a considerable amount of time and power (taking that it will be comparing stuff like that few times a min).
I have a limited group of 3D coordinates like this:
array (size=XXX)
0 => 10, 20, 30
1 => 200, 20, 13
2 => 36, 215, 150
3 => ...
4 => ...
... // this is limited to max 256 items
Then I have another very large group of, let's say, random 3D coordinates which can vary in size from 2,500 -> ~ 2,000,000+ items. Basically, what I need to do is to iterate through each of those points and find the closest point. To do that I use Euclidean distance:
sq((q1-p1)2+(q2-p2)2+(q3-p3)2)
This gives me the distance and I compare it to the current closest distance, if it is closer, replace the closest, else continue with next set.
I have been looking on how to change it so I don't have to do so many calculations. I have been looking at Voronoi Diagrams then maybe place the points in that diagram, then see which section it belongs to. However, I have no idea how I can implement such a thing in PHP.
Any idea how I can optimize it?
Just a quick shot from the hip ;-)
You should be able to gain a nice speed up if you dont compare each point to each other point. Many points can be skipped because they are already to far away if you just look at one of the x/y/z coordinates.
<?php
$coord = array(18,200,15);
$points = array(
array(10,20,30),
array(200,20,13),
array(36,215,150)
);
$closestPoint = $closestDistance= false;;
foreach($points as $point) {
list($x,$y,$z) = $point;
// Not compared yet, use first poit as closest
if($closestDistance === false) {
$closestPoint = $point;
$closestDistance = distance($x,$y,$z,$coord[0],$coord[1],$coord[2]);
continue;
}
// If distance in any direction (x/y/z) is bigger than closest distance so far: skip point
if(abs($coord[0] - $x) > $closestDistance) continue;
if(abs($coord[1] - $y) > $closestDistance) continue;
if(abs($coord[2] - $z) > $closestDistance) continue;
$newDistance = distance($x,$y,$z,$coord[0],$coord[1],$coord[2]);
if($newDistance < $closestDistance) {
$closestPoint = $point;
$closestDistance = distance($x,$y,$z,$coord[0],$coord[1],$coord[2]);
}
}
var_dump($closestPoint);
function distance($x1,$y1,$z1,$x2,$y2,$z2) {
return sqrt(pow($x1-$x2,2) + pow($y1 - $y2,2) + pow($z1 - $z2,2));
}
A working code example can be found at http://sandbox.onlinephpfunctions.com/code/8cfda8e7cb4d69bf66afa83b2c6168956e63b51e

Arrangement : how many unique possibilities for x elements among n

I'm not that good at math, so I'm stuck here.
I need to get the total number of possible arrangement (I think, or permutations maybe?) of X elements amongst N.
I want to pick X distinct elements amongst N (N>=X)
order DOES matter
each element can not come more than once in a combination
=> For exemple, given $N = count(1,2,3,4,5,6,7,8,9), a valid combination of $X=6 elements could be :
- 1,4,5,3,2,8
- 4,2,1,9,7,3
What formula do I need to use in PHP to get the total number of possibilities?
There are N choices for the first element, N-1 for the second (as you have already chosen 1) then N-2 choices for the third and so on. You can express using factorials this a N! / (N-X-1)!. See https://en.wikipedia.org/wiki/Permutations
Ok, I think I got it.
$set = array(A,B,C,D,E,F,G);
$n = count($set);
$k = 6;
if($n>0)
{
if($k < $n)
{
$outcomes = gmp_fact($n) / gmp_fact($n-$k);
}
else
{
$outcomes = gmp_fact($n);
}
} else { $outcomes = 0; }
where gmp_fact($n) is the php function for $n! (n factorial), which means N x (N-1) x ... x 1

calculate moving object inside box

I have an object that has:
start location: (X0,Y0)
and speed: (Vx, Vy)
it is trapped inside a box: boxWidth, boxHeight
when the object hits the border of the box it will flip direction.
ie:
object has speed: (1,3)
now it hits the top of the box
now it's speed will be: (1,-3)
now lets say it hitted the right of the box
and it's speed will be: (-1, -3)
I already made a sekeleton for the point class.
I would need a function that I will give it an "n" time and t will return me the current object location after that time:
my class:
class Point {
protected $x0;
protected $y0;
protected $vx;
protected $vy;
protected $currentX;
protected $currentY;
protected $blockWide;
protected $blockHeight;
public function __construct($x0, $y0, $vx, $vy, $blockHeight, $blockWide) {
$this->x0 = $x0;
$this->y0 = $y0;
$this->vx = $vx;
$this->vy = $vy;
$this->blockHeight = $blockHeight;
$this->blockWide = $blockWide;
$this->currentX = $x0;
$this->currentY = $y0;
}
public function getLoc($time) {
$this->currentX = $this->getLocX($time);
$this->currentY = $this->getLocY($time);
}
protected function getLocX($time) {
$direction = 1;
$flips = 0;
$distance = $this->vx * $time;
if ( $this->blockWide - $this->x0)
return 1;
}
protected function getLocY($time) {
return 0;
}
public function printAsPoint() {
echo '(',$this->currentX,',',$this->currentY,')';
}
}
I simply dont have an idea on how to calculate it with the starting location, speed, and speed flips that will happen each time the point will reach the borders.
some code from your posts:
protected function getLocX($time) {
$corPos = $this->x0 + $time * $this->vx;
$modulo = $corPos%(2*$this->blockWide);
if($modulo > $this->blockWide)
$corPos = 2*$this->blockWide - $modulo;
if($modulo < $this->blockWide)
$corPos = $modulo;
if($modulo == $this->blockWide)
$corPos = $modulo;
return $corPos;
}
If we take only the x - direction, then the object returns to the same state (same position and same velocity) after (2*boxWidth/Vx) time.
So keep subtracting the above quantity from the time till the value of time is greater than this quantity. If you are working with integers, then you can apply the remainder operator also.
Once you get the final time figure, it will be simple to deal with it. You just have to check for one bounce at the maximum.
xFinal = xInitial + Vx * t.
if xFinal > boxWidth || xFinal < 0 it means there is a bounce and proceed accordingly.
Similarly for y direction.
Consider your problem as looking for the X and then for the Y,
considering X0 the initial position, VX the walking step
(i assume this doesn't change in absolute) ,
WX the width of your object and boxWidth the width of the box
simplifications:
- you can consider your object to be 0 width in a box of (boxWidth-WX)
- you can consider your object running at the speed of 1
in a box of (boxWidth-WX)/VX width
- Since your object changes direction each time it hits a border, we can consider it
runs in same direction into a twice bigger box (boxWidth-WX)*2/VX
- finally, the new position after n moves shall be calculated on the base of :
(X0+n) mod (boxWidth-WX)*2/VX which gives you the position in a twice as big box,
and checking if the position is bigger than boxWidth the real position
will be boxWidth-foundPosition
Pretend there are no borders, then imagine there are!
d = s * t
No borders: actual d = d
With borders: actual d =
If moving to the right:
1. Subtract the distance to the right border from d.
2. Divide result by box-width. If the quotient is odd, the remainder
indicates how far you bounced back from the left border; if the quotient
is even, the remainder shows how far you bounced back from the right
border. (or something like that...perhaps you or others can correct me.)
If moving to the left:
Reverse the ideas from 1.
Since from your comments it seems that the positive-motion idea may be working for you, here's an example with motion in the opposite direction:
Let us say s is -1 unit/sec (that is, moving to the left),
t is 6 seconds,
the object is 1 unit from the left border,
and the box width is 2 units.
Total distance then is 1 * 6 = 6. We subtract 1 unit to get to the left border
and have 5 units left.
How many times do we bounce back and forth? We divide 5 by the box width:
the quotient is 2, which means the ball went once all the way to the right, then
back again to the left border. Since the quotient is even, we know we are back
to the left border (if the quotient was odd we would be at the right border).
Any remainder indicates the last distance bouncing back, in this case from the
left border, but not bouncing as far as the right border. Our remainder in this
case is 1, meaning we bounced back from the left border 1 unit, and that's our
current location!

Categories