I already have the pieces mapped out in the array and it prints just fine
$board = array(
array('1','rs','1','rs','1','rs','1','rs'),
array('rs','1','rs','1','rs','1','rs','1'),
array('1','rs','1','rs','1','rs','1','rs'),
array('rs','bs','rs','bs','rs','bs','rs','bs'),
array('bs','rs','bs','rs','bs','rs','bs','rs'),
array('2','bs','2','bs','2','bs','2','bs'),
array('bs','2','bs','2','bs','2','bs','2'),
array('2','bs','2','bs','2','bs','2','bs')
);
1 = black pieces
2 = red pieces
rs = red square
bs = black square
this code parse the input of a player : example FROM F2 into (0,0) coordinates
function parseSquareFrom($square) {
if (strlen($square) != 2) {
return FALSE;
}
$coords = array(ord('A') - ord($square[0]), $square[1] - 1);
// Perform bounds-checking.
if ($coords[0] < 0 || $coords[0] > 7 || $coords[1] < 0 || $coords[1] > 7) {
return FALSE;
}
return $coords;
}
I have repeated the same function for the TO input ( to where the player wants to move the piece
my question is this next code a valid way to move with the functions above
$board[$coords1[0]-1][$coords1[1]+1] = $board[$coords[0]][$coords[1]];
$board[$coords[0]][$coords[1]] = 0;
//eating action
$board[$coords1[0]][$coords1[1]] = 0;
$board[$coords1[0]-2][$coords1[1]+2] = $board[$coords[0]][$coords[1]];
$way = ($_POST['way'] === 'up')? 1:-1;
$way = ($_POST['way'] === 'down')? -1:+1;
//if player is 'up' then the value of $way is 1 so
$board[$x+(-1*$way)][$y+(1*$way)] = $board[$coords[0]][$coords[1]]; // position 2,2 becomes 1,3
//if player is not 'up' then the value of $way is -1 so
$board[$x+(-1*$way)][$y+(1*$way)] = $board[$coords[0]][$coords[1]]; // position 2,2 becomes 3,1
I plan to have a function to update the tile movements in the screen so the piece moves as the it highlights the piece as is moving to the next square
this is using serialize into file to hold start positions, movements, kings and queens positions
I'm going to assume that $coords is a $_POST variable like a suggested on the first part of this question. If that's the case, the first part of your code is correct:
$board[$coords1[0]-1][$coords1[1]+1] = $board[$coords[0]][$coords[1]];
$board[$coords[0]][$coords[1]] = 0;
This moves a piece diagonally one step up-right in the board.
The second part on the other hand skips the actual 'eating' action. Unless, the location specified by the user is the one where the enemy piece is. In which case you code would work.
As for the bound-checking you do, you're not being bullet-proof since adding 2 in the eating move, while a piece is next to a border would result in your code trying to place the piece out of the board. So you could check bounds, depending on the move, so if the move is a standard diagonal one, you should check that the ending position is within the limits, not the starting one since you are assuming that piece is in a correct position before.
I think you have a few issues
$board[$coords1[0]-1][$coords1[1]+1] = $board[$coords[0]][$coords[1]];
if $coords[1] == 7 (which appears to be allowed) then $coords[1]+1 == 8 which is out of bounds. Let alone the coords +/- 2 we see later.
You probably don't want that 'bs'/'rs' in your storage, all of that can be inferred.
$getSquareColor = function($x,$y){return ($x+$y)/2 == 1 ? 'red' : 'black';} //or better yet, const or enum
function getSquareColor($x, $y) //alternatively
{
if ( ($x+$y) % 2 == 0) return 'black';
else return 'red';
}
ps - if you drop those dollar signs the function is just as good in javascript
Related
I want to list the latitude and longitude of itinerary. It could be all points or it could be all points in 1-2 kilometers.
What I'm trying to do is: user selected A as the starting point and B as the ending point. I want to show some places near the road between A and B on the map. But I need a positions for this.
As an example, a JavaScript code is shared here and It is said that this can be done with DirectionsResult Object.
var request = {
origin: start_point,
destination: end_point,
travelMode: google.maps.TravelMode.DRIVING
};
var directionsService = new google.maps.DirectionsService();
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var path = (response.routes[0].overview_path);
}
});
But I'm trying to do this with php and I have to do this with php.
I read google map api. I've also read the yandex map api, but this seems to be done only with javascript.
Does anyone know a way to do this with php?
From comments I understand the question is to find (using PHP) the intermediate lat,lng pairs that can be extracted from the polyline points in a google directions query.
This is a bit unusual because people normally use the polyline points for map drawing in the browser, and so the JavaScript libraries are well equipped for this task. However, not so in PHP.
The points data appears in the JSON result object as string of ascii characters, sometimes quite long and always 'unreadable'. Into this string is encoded a list of intermediate lat lng pairs between the start and end of each leg. The coding method is presented at the google site https://developers.google.com/maps/documentation/utilities/polylinealgorithm and the algorithm below is just a reversal of that and is commented accordingly.
The example shows a directions find between 2 points, on crescent shaped streets, in Perth, Australia. The start-end points were chosen to encourage multiple intermediate points as would be needed to draw the route. Substitute your own search as needed.
Note that the JSON also provides these fields also at the end of each results object.
"overview_polyline" : {
"points" : "~n{aEmbwaU_B#cCBk#Lo#d#UVOb#Mh#Ab####BBF#DGNABD`#Fh#Pb#VZn#b#d#J"
},
This is much less detailed and less accurate (if you draw will probably depart from actual road lines on map), but can also be decoded in the same way.
The best intermediate points are however, by iterating through the steps using:
"polyline" : {
"points" : "~n{aEmbwaUg##w#?{A?g#BUBUHSJ[XUVOb#Mh#Ab#"
},
Finally, the original source for the algorithm can be found here http://unitstep.net/blog/2008/08/02/decoding-google-maps-encoded-polylines-using-php/. So thanks to Peter Chng for this work back in 2008! Peter also acknowledges Mark MClure who did the original coding in JavaScript. I hacked about with and added more comments - to make more aligned with the google recipe, but no more.
I have also just realised there is this link https://github.com/emcconville/google-map-polyline-encoding-tool which (I think but have not tested) provides a class and a CLI tool to do the conversions both ways.
$json = file_get_contents("https://maps.googleapis.com/maps/api/directions/json?origin=20%20%20Kintyre%20Crescent,%20Churchlands&destination=%2018Kinross%20Crescent,%20Churchlands&key=");
$details = json_decode($json,true);
print_r($details); // show the full result
$points = $details['routes'][0]['legs'][0]['steps'][0]['polyline']['points'];
echo($points); // show the points string for one leg
// show the start and end locations for that leg
print_r($details['routes'][0]['legs'][0]['steps'][0]['start_location']);
print_r($details['routes'][0]['legs'][0]['steps'][0]['end_location']);
// work out the intermdiate points (normally used for drawing)
$decodedPoints= decodePolylinePoints($points);
print_r($decodedPoints); // print out the intermediate points
// This function decodes the polylone points in PHP
function decodePolylinePoints($pointsString)
{
$len = strlen($pointsString);
$latLons = array(); // the output array
$lat = 0; // temp storage for lat and lng
$lng = 0;
$index = 0; // index to curent character
while ($index < $len) // process each lat,lng pair
{
// first build the lat
// NOTE: first lat is an absolute value
// NOTE: subsequent lats are offsets from previous values for coding efficiency
$char = 0; // char as read from points string
$shift = 0; // cumulative shift amount
$value = 0; // temp value during computation
do // Read, convert and shift 5 bit chunks until terminator is reached to get lat
{
$char = ord(substr($pointsString, $index++)) - 63; // return ascii value less 63
$value |= ($char & 0x1f) << $shift; // convert to 5 bit and shift left
$shift += 5; // next shift is 5 extra
}
while ($char >= 0x20); // value of 20 indicates end of lat
$lat += (($value & 1) ? ~($value >> 1) : ($value >> 1)); // convert negative values and save
// now build the lng
// NOTE: first lng is an absolute value
// NOTE: subsequent lngs are offsets from previous values for coding efficiency
$shift = 0;
$value = 0;
do // build up lng from 5 bit chunks
{
$char= ord(substr($pointsString, $index++)) - 63; // return ascii value less 63
$value |= ($char & 0x1f) << $shift; // convert to 5 bit and shift left
$shift += 5; // next shift is 5 extra
}
while ($char >= 0x20); // value of 20 indicates end of lng
$lng += (($value & 1) ? ~($value >> 1) : ($value >> 1)); // convert negative values and save
$latLons[] = array($lat * 1e-5, $lng * 1e-5); // original values were * 1e5
}
return $latLons; // points array converted to lat,lngs
}
I have a list of coordinate to be sorted with a spiral algorithm. My need is to start on the middle of the area and "touch" any coordinate.
To simplify this is the representation of the (unsorted) list of coordinates (x,y marked with a "dot" on following image).
CSV list of coordinates is available here.
X increase from left to right
Y increases from TOP to BOTTOM
Every coordinate is not adjacent to the following one but are instead distanciated by 1 or 2 dice (or more in certain case).
Starting from the center of the area, I need to touch any coordinate with a spiral movement:
to parse each coordinate I've drafted this PHP algorithm:
//$missing is an associative array having as key the coordinate "x,y" to be touched
$direction = 'top';
$distance = 1;
$next = '128,127'; //starting coordinate
$sequence = array(
$next;
)
unset($missing[$next]);
reset($missing);
$loopcount = 0;
while ($missing) {
for ($loop = 1; $loop <= 2; $loop++) {
for ($d = 1; $d <= $distance; $d++) {
list($x,$y) = explode(",", $next);
if ($direction == 'top') $next = ($x) . "," . ($y - 1);
elseif ($direction == 'right') $next = ($x + 1) . "," . ($y);
elseif ($direction == 'bottom') $next = ($x) . "," . ($y + 1);
elseif ($direction == 'left') $next = ($x - 1) . "," . ($y);
if ($missing[$next]) {
unset($missing[$next]); //missing is reduced every time that I pass over a coordinate to be touched
$sequence[] = $next;
}
}
if ($direction == 'top') $direction = 'right';
elseif ($direction == 'right') $direction = 'bottom';
elseif ($direction == 'bottom') $direction = 'left';
elseif ($direction == 'left') $direction = 'top';
}
$distance++;
}
but as coordinate are not equidistant from each other, I obtain this output:
As is clearly visible, the movement in the middle is correct whereas and accordingly with the coordinate position, at a certain instant the jump between each coordinate are not anymore coherent.
How can I modify my code to obtain an approach like this one, instead?
To simplify/reduce the problem: Imagine that dots on shown above image are cities that the salesman have to visit cirurarly. Starting from the "city" in the middle of the area, the next cities to be visited are the ones located near the starting point and located on North, East, Soutch and West of the starting point. The salesman cannot visit any further city unless all the adjacent cities in the round of the starting point hadn't been visited. All the cities must be visited only one time.
Algorithm design
First, free your mind and don't think of a spiral! :-) Then, let's formulate the algorithms constraints (let's use the salesman's perspective):
I am currently in a city and am looking where to go next. I'll have to find a city:
where I have not been before
that is as close to the center as possible (to keep spiraling)
that is as close as possible to my current city
Now, given these three constraints you can create a deterministic algorithm that creates a spiral (well at least for the given example it should, you probably can create cases that require more effort).
Implementation
First, because we can walk in any direction, lets generally use the Euclidean distance to compute distances.
Then to find the next city to visit:
$nextCost = INF;
$nextCity = null;
foreach ($notVisited as $otherCity) {
$cost = distance($current_city, $other_city) + distance($other_city, $centerCity);
if ($cost < $nextCost) {
$nextCost = $cost;
$nextCity = $otherCity;
}
}
// goto: $nextCity
Just repeat this until there are no more cities to visit.
To understand how it works, consider the following picture:
I am currently at the yellow circle and we'll assume the spiral up to this point is correct. Now compare the length of the yellow, pink and blue lines. The length of those lines is basically what we compute using the distance functions. You will find that in every case, the next correct city has the smallest distance (well, at least as long as we have as many points everywhere, you probably can easily come up with a counter-example).
This should get you started to implement a solution for your problem.
(Correctness) Optimization
With the current design, you will have to compare the current city to all remaining cities in each iteration. However, some cities are not of interest and even in the wrong direction. You can further optimize the correctness of the algorithm by excluding some cities from the search space before entering the foreach loop shown above. Consider this picture:
You will not want to go to those cities now (to keep spiraling, you shouldn't go backwards), so don't even take their distance into account. Albeit this is a little more complicated to figure out, if your data points are not as evenly distributed as in your provided example, this optimization should provide you a healthy spiral for more disturbed datasets.
Update: Correctness
Today it suddenly struck me and I rethought the proposed solution. I noticed a case where relying on the two euclidean distances might yield unwanted behavior:
It is easily possible to construct a case where the blue line is definitely shorter than the yellow one and thus gets preferred. However, that would break the spiral movement. To eliminate such cases we can make use of the travel direction. Consider the following image (I apologize for the hand-drawn angles):
The key idea is to compute the angle between the previous travel direction and the new travel direction. We are currently at the yellow dot and need to decide where to go next. Knowing the previous dot, we can obtain a vector representing the previous direction of the movement (e.g. the pink line).
Next, we compute the vector to each city we consider and compute the angle to the previous movement vector. If that vector is <= 180 deg (case 1 in the image), then the direction is ok, otherwise not (case 2 in the image).
// initially, you will need to set $prevCity manually
$prevCity = null;
$nextCost = INF;
$nextCity = null;
foreach ($notVisited as $otherCity) {
// ensure correct travel direction
$angle = angle(vectorBetween($prevCity, $currentCity), vectorBetween($currentCity, $otherCity));
if ($angle > 180) {
continue;
}
// find closest city
$cost = distance($current_city, $other_city) + distance($other_city, $centerCity);
if ($cost < $nextCost) {
$nextCost = $cost;
$nextCity = $otherCity;
}
}
$prevCity = $currentCity;
// goto: $nextCity
Pay attention to compute the angle and vectors correctly. If you need help on that, I can elaborate further or simply ask a new question.
The problem seems to be in the if-conditional when you missing traverse a co-ordinate, I.e because of rounding of the corners. A else conditional with a reverse to the previous calculation of the co-ordinate would fix it.
There is quite a popular program (I forget the name) which generates triangles, where on each side there is a question or answer, and each triangle fits together such that the answer on one triangle matches the question on another, and when put together correctly it creates a larger shape (usually a regular hexagon).
I am trying to write a script, where $t is a 2D array containing the cards:
$t = array();
// $t['1'] represents the 'center' triangle in this basic example
$t['1'] = array(
'1', // One side of T1, which is an answer
'3-1', // Another side, this is a question
'2+1' // Final side, another question
);
// These cards go around the outside of the center card
$t['2'] = array(
'2-1' // This is a question on one side of T2, the other sides are blank
);
$t['3'] = array(
'2' // This is an answer on one side of T3, the other sides are blank
);
$t['4'] = array(
'3' // This is an answer on one side of T4, the other sides are blank
);
What now need it to do, is say, for example, "T1-S1 matches with T2, T1-S2 matches with T3, T1-S3 matches with T4". I have tried, and what I have so far is below:
foreach ($t as $array) {
foreach ($array as $row) {
$i = 0;
while ($i <= 4) {
if(in_array($row, $t[$i])){
echo $row . ' matches with triangle ' . $i . '<br />';
}
$i++;
}
}
}
Note: The code above is for a simplified version where all the questions were 'solved', and it was just matching the two sides.
After running my code, I get this output:
1 matches with triangle 1
1 matches with triangle 2
2 matches with triangle 1
2 matches with triangle 3
3 matches with triangle 1
3 matches with triangle 4
1 matches with triangle 1
1 matches with triangle 2
2 matches with triangle 1
2 matches with triangle 3
3 matches with triangle 1
3 matches with triangle 4
The problem is, that $row only tells me the side of a triangle, not the actual triangle. So my question is this:
How do I make my script work, such that it outputs "Ta-Sb matches with Tc-Sd" where a is the triangle, b is the side, and c is the triangle it matches with, and d is the side it matches with, assuming that in each array the values for the sides are in order?
I hope that the question is clear, however feel free to ask any questions.
Also, ideally once it has matched Ta-Sb with Tc-Sd, it should not match Tc-Sd with Ta-Sb. Is that possible too?
I find it easier to approach these types of problems with objects rather than arrays. Too complicated to remember what each array level means and how they line up. So I might do something like this:
<?
// I say Polygon instead of triangle because ideally the logic should scale for squares, octagons, anything! But start with triangles of course.
class Polygon{
var $Sides = array(); // Side objects - there should be 3 of them for a triangle
var $matches = array(); // holds the ids of the matching polygonn - keys line up with $Sides
function __construct(){
$Sides[0] = new Side();
$Sides[1] = new Side();
$Sides[2] = new Side();
}
}
class Side{
var $Question; // Question object
var $state; // 'q' or 'a' - does this side show the question or answer?
function __construct(){
$Question = new Question();
}
}
class Question{
var $id; // database id of the question
var $question;
var $answer;
}
?>
To populate:
<?php
$Triangle[0]=new Polygon();
$Triangle[0]->Side[0]->Question->id=1;
$Triangle[0]->Side[0]->Question->question='Yo momma serves more requests than what?';
$Triangle[0]->Side[0]->Question->answer='HTTP';
$Triangle[0]->Side[0]->state='q'; // This side shows the question
$Triangle[0]->matches[0]= 4; // Side 0 of this triangle matches a side of triangle 4
// write a loop that does this for all triangles using whatever your logic is for matching them up
?>
Now you can easily know which triangle, side, question, or match you're dealing with by saying for example:
$Polygon[2]->Sides[1]->state (means that side of that triangle should show an answer not a question )
$Polygon[0]->Sides[3]->Question->id (would hold a question's id)
$Polygon[1]->matches[2] (would hold the key of the triangle that matches side 2 of polygon 1)
It might feel like a leap if you're not used to objects, but this is a pretty easy way into it, you can just treat them like glorified arrays and forget about all the other stuff objects can do for now.
After you fill them with values - to get the matches you just loop through each Polygon and output whatever you need.
Hope this helps!
Does this help?
foreach ($t as $ti => $array) {
foreach ($array as $ri => $row) {
$i = 0;
while ($i <= 4) {
if(in_array($row, $t[$i])){
echo $row.' '.$ti.' '.$ri.' matches with triangle ' . $i . '<br />';
}
$i++;
}
}
}
I am working on a Web Application that includes long listings of names. The client originally wanted to have the names split up into divs by letter so it is easy to jump to a particular name on the list.
Now, looking at the list, the client pointed out several letters that have only one or two names associated with them. He now wants to know if we can combine several consecutive letters if there are only a few names in each.
(Note that letters with no names are not displayed at all.)
What I do right now is have the database server return a sorted list, then keep a variable containing the current character. I run through the list of names, incrementing the character and printing the opening and closing div and ul tags as I get to each letter. I know how to adapt this code to combine some letters, however, the one thing I'm not sure about how to handle is whether a particular combination of letters is the best-possible one. In other words, say that I have:
A - 12 names
B - 2 names
C - 1 name
D - 1 name
E - 1 name
F - 23 names
I know how to end up with a group A-C and then have D by itself. What I'm looking for is an efficient way to realize that A should be by itself and then B-D should be together.
I am not really sure where to start looking at this.
If it makes any difference, this code will be used in a Kohana Framework module.
UPDATE 2012-04-04:
Here is a clarification of what I need:
Say the minimum number of items I want in a group is 30. Now say that letter A has 25 items, letters B, C, and D, have 10 items each, and letter E has 32 items. I want to leave A alone because it will be better to combine B+C+D. The simple way to combine them is A+B, C+D+E - which is not what I want.
In other words, I need the best fit that comes closest to the minimum per group.
If a letter contains more than 10 names, or whatever reasonable limit you set, do not combine it with the next one. However, if you start combining letters, you might have it run until 15 or so names are collected if you want, as long as no individual letter has more than 10. That's not a universal solution, but it's how I'd solve it.
I came up with this function using PHP.
It groups letters that combined have over $ammount names in it.
function split_by_initials($names,$ammount,$tollerance = 0) {
$total = count($names);
foreach($names as $name) {
$filtered[$name[0]][] = $name;
}
$count = 0;
$key = '';
$temp = array();
foreach ($filtered as $initial => $split) {
$count += count($split);
$temp = array_merge($split,$temp);
$key .= $initial.'-';
if ($count >= $ammount || $count >= $ammount - $tollerance) {
$result[$key] = $temp;
$count = 0;
$key = '';
$temp = array();
}
}
return $result;
}
the 3rd parameter is used for when you want to limit the group to a single letter that doesn't have the ammount specified but is close enough.
Something like
i want to split in groups of 30
but a has 25
to so, if you set a tollerance of 5, A will be left alone and the other letters will be grouped.
I forgot to mention but it returns a multi dimensional array with the letters it contains as key then the names it contains.
Something like
Array
(
[A-B-C-] => Array
(
[0] => Bandice Bergen
[1] => Arey Lowell
[2] => Carmen Miranda
)
)
It is not exactly what you needed but i think it's close enough.
Using the jsfiddle that mrsherman put, I came up with something that could work: http://jsfiddle.net/F2Ahh/
Obviously that is to be used as a pseudocode, some techniques to make it more efficient could be applied. But that gets the job done.
Javascrip Version: enhanced version with sort and symbols grouping
function group_by_initials(names,ammount,tollerance) {
tolerance=tollerance||0;
total = names.length;
var filtered={}
var result={};
$.each(names,function(key,value){
val=value.trim();
var pattern = /[a-zA-Z0-9&_\.-]/
if(val[0].match(pattern)) {
intial=val[0];
}
else
{
intial='sym';
}
if(!(intial in filtered))
filtered[intial]=[];
filtered[intial].push(val);
})
var count = 0;
var key = '';
var temp = [];
$.each(Object.keys(filtered).sort(),function(ky,value){
count += filtered[value].length;
temp = temp.concat(filtered[value])
key += value+'-';
if (count >= ammount || count >= ammount - tollerance) {
key = key.substring(0, key.length - 1);
result[key] = temp;
count = 0;
key = '';
temp = [];
}
})
return result;
}
I am trying to create a little php script that can make my life a bit easier.
Basically, I am going to have 21 text fields on a page where I am going to input 20 different numbers. In the last field I will enter a number let's call it the TOTAL AMOUNT. All I want the script to do is to point out which numbers from the 20 fields added up will come up to TOTAL AMOUNT.
Example:
field1 = 25.23
field2 = 34.45
field3 = 56.67
field4 = 63.54
field5 = 87.54
....
field20 = 4.2
Total Amount = 81.90
Output: field1 + fields3 = 81.90
Some of the fields might have 0 as value because sometimes I only need to enter 5-15 fields and the maximum will be 20.
If someone can help me out with the php code for this, will be greatly appreciated.
If you look at oezis algorithm one drawback is immediately clear: It spends very much time summing up numbers which are already known not to work. (For example if 1 + 2 is already too big, it doesn't make any sense to try 1 + 2 + 3, 1 + 2 + 3 + 4, 1 + 2 + 3 + 4 + 5, ..., too.)
Thus I have written an improved version. It does not use bit magic, it makes everything manual. A drawback is, that it requires the input values to be sorted (use rsort). But that shouldn't be a big problem ;)
function array_sum_parts($vals, $sum){
$solutions = array();
$pos = array(0 => count($vals) - 1);
$lastPosIndex = 0;
$currentPos = $pos[0];
$currentSum = 0;
while (true) {
$currentSum += $vals[$currentPos];
if ($currentSum < $sum && $currentPos != 0) {
$pos[++$lastPosIndex] = --$currentPos;
} else {
if ($currentSum == $sum) {
$solutions[] = array_slice($pos, 0, $lastPosIndex + 1);
}
if ($lastPosIndex == 0) {
break;
}
$currentSum -= $vals[$currentPos] + $vals[1 + $currentPos = --$pos[--$lastPosIndex]];
}
}
return $solutions;
}
A modified version of oezis testing program (see end) outputs:
possibilities: 540
took: 3.0897309780121
So it took only 3.1 seconds to execute, whereas oezis code executed 65 seconds on my machine (yes, my machine is very slow). That's more than 20 times faster!
Furthermore you may notice, that my code found 540 instead of 338 possibilities. This is because I adjusted the testing program to use integers instead of floats. Direct floating point comparison is rarely the right thing to do, this is a great example why: You sometimes get 59.959999999999 instead of 59.96 and thus the match will not be counted. So, if I run oezis code with integers it finds 540 possibilities, too ;)
Testing program:
// Inputs
$n = array();
$n[0] = 6.56;
$n[1] = 8.99;
$n[2] = 1.45;
$n[3] = 4.83;
$n[4] = 8.16;
$n[5] = 2.53;
$n[6] = 0.28;
$n[7] = 9.37;
$n[8] = 0.34;
$n[9] = 5.82;
$n[10] = 8.24;
$n[11] = 4.35;
$n[12] = 9.67;
$n[13] = 1.69;
$n[14] = 5.64;
$n[15] = 0.27;
$n[16] = 2.73;
$n[17] = 1.63;
$n[18] = 4.07;
$n[19] = 9.04;
$n[20] = 6.32;
// Convert to Integers
foreach ($n as &$num) {
$num *= 100;
}
$sum = 57.96 * 100;
// Sort from High to Low
rsort($n);
// Measure time
$start = microtime(true);
echo 'possibilities: ', count($result = array_sum_parts($n, $sum)), '<br />';
echo 'took: ', microtime(true) - $start;
// Check that the result is correct
foreach ($result as $element) {
$s = 0;
foreach ($element as $i) {
$s += $n[$i];
}
if ($s != $sum) echo '<br />FAIL!';
}
var_dump($result);
sorry for adding a new answer, but this is a complete new solution to solve all problems of life, universe and everything...:
function array_sum_parts($n,$t,$all=false){
$count_n = count($n); // how much fields are in that array?
$count = pow(2,$count_n); // we need to do 2^fields calculations to test all possibilities
# now i want to look at every number from 1 to $count, where the number is representing
# the array and add up all array-elements which are at positions where my actual number
# has a 1-bit
# EXAMPLE:
# $i = 1 in binary mode 1 = 01 i'll use ony the first array-element
# $i = 10 in binary mode 10 = 1010 ill use the secont and the fourth array-element
# and so on... the number of 1-bits is the amount of numbers used in that try
for($i=1;$i<=$count;$i++){ // start calculating all possibilities
$total=0; // sum of this try
$anzahl=0; // counter for 1-bits in this try
$k = $i; // store $i to another variable which can be changed during the loop
for($j=0;$j<$count_n;$j++){ // loop trough array-elemnts
$total+=($k%2)*$n[$j]; // add up if the corresponding bit of $i is 1
$anzahl+=($k%2); // add up the number of 1-bits
$k=$k>>1; //bit-shift to the left for looking at the next bit in the next loop
}
if($total==$t){
$loesung[$i] = $anzahl; // if sum of this try is the sum we are looking for, save this to an array (whith the number of 1-bits for sorting)
if(!$all){
break; // if we're not looking for all solutions, make a break because the first one was found
}
}
}
asort($loesung); // sort all solutions by the amount of numbers used
// formating the solutions to getting back the original array-keys (which shoud be the return-value)
foreach($loesung as $val=>$anzahl){
$bit = strrev(decbin($val));
$total=0;
$ret_this = array();
for($j=0;$j<=strlen($bit);$j++){
if($bit[$j]=='1'){
$ret_this[] = $j;
}
}
$ret[]=$ret_this;
}
return $ret;
}
// Inputs
$n[0]=6.56;
$n[1]=8.99;
$n[2]=1.45;
$n[3]=4.83;
$n[4]=8.16;
$n[5]=2.53;
$n[6]=0.28;
$n[7]=9.37;
$n[8]=0.34;
$n[9]=5.82;
$n[10]=8.24;
$n[11]=4.35;
$n[12]=9.67;
$n[13]=1.69;
$n[14]=5.64;
$n[15]=0.27;
$n[16]=2.73;
$n[17]=1.63;
$n[18]=4.07;
$n[19]=9.04;
$n[20]=6.32;
// Output
$t=57.96;
var_dump(array_sum_parts($n,$t)); //returns one possible solution (fuc*** fast)
var_dump(array_sum_parts($n,$t,true)); // returns all possible solution (relatively fast when you think of all the needet calculations)
if you don't use the third parameter, it returns the best (whith the least amount numbers used) solution as array (whith keys of the input-array) - if you set the third parameter to true, ALL solutions are returned (for testing, i used the same numbers as zaf in his post - there are 338 solutions in this case, found in ~10sec on my machine).
EDIT:
if you get all, you get the results ordered by which is "best" - whithout this, you only get the first found solution (which isn't necessarily the best).
EDIT2:
to forfil the desire of some explanation, i commented the essential parts of the code . if anyone needs more explanation, please ask
1. Check and eliminate fields values more than 21st field
2. Check highest of the remaining, Add smallest,
3. if its greater than 21st eliminate highest (iterate this process)
4. If lower: Highest + second Lowest, if equal show result.
5. if higher go to step 7
6. if lower go to step 4
7. if its lower than add second lowest, go to step 3.
8. if its equal show result
This is efficient and will take less execution time.
Following method will give you an answer... almost all of the time. Increase the iterations variable to your taste.
<?php
// Inputs
$n[1]=8.99;
$n[2]=1.45;
$n[3]=4.83;
$n[4]=8.16;
$n[5]=2.53;
$n[6]=0.28;
$n[7]=9.37;
$n[8]=0.34;
$n[9]=5.82;
$n[10]=8.24;
$n[11]=4.35;
$n[12]=9.67;
$n[13]=1.69;
$n[14]=5.64;
$n[15]=0.27;
$n[16]=2.73;
$n[17]=1.63;
$n[18]=4.07;
$n[19]=9.04;
$n[20]=6.32;
// Output
$t=57.96;
// Let's try to do this a million times randomly
// Relax, thats less than a blink
$iterations=1000000;
while($iterations-->0){
$z=array_rand($n, mt_rand(2,20));
$total=0;
foreach($z as $x) $total+=$n[$x];
if($total==$t)break;
}
// If we did less than a million times we have an answer
if($iterations>0){
$total=0;
foreach($z as $x){
$total+=$n[$x];
print("[$x] + ". $n[$x] . " = $total<br/>");
}
}
?>
One solution:
[1] + 8.99 = 8.99
[4] + 8.16 = 17.15
[5] + 2.53 = 19.68
[6] + 0.28 = 19.96
[8] + 0.34 = 20.3
[10] + 8.24 = 28.54
[11] + 4.35 = 32.89
[13] + 1.69 = 34.58
[14] + 5.64 = 40.22
[15] + 0.27 = 40.49
[16] + 2.73 = 43.22
[17] + 1.63 = 44.85
[18] + 4.07 = 48.92
[19] + 9.04 = 57.96
A probably inefficient but simple solution with backtracking
function subset_sums($a, $val, $i = 0) {
$r = array();
while($i < count($a)) {
$v = $a[$i];
if($v == $val)
$r[] = $v;
if($v < $val)
foreach(subset_sums($a, $val - $v, $i + 1) as $s)
$r[] = "$v $s";
$i++;
}
return $r;
}
example
$ns = array(1, 2, 6, 7, 11, 5, 8, 9, 3);
print_r(subset_sums($ns, 11));
result
Array
(
[0] => 1 2 5 3
[1] => 1 2 8
[2] => 1 7 3
[3] => 2 6 3
[4] => 2 9
[5] => 6 5
[6] => 11
[7] => 8 3
)
i don't think the answer isn't as easy as nik mentioned. let's ay you have the following numbers:
1 2 3 6 8
looking for an amount of 10
niks solution would do this (if i understand it right):
1*8 = 9 = too low
adding next lowest (2) = 11 = too high
now he would delete the high number and start again taking the new highest
1*6 = 7 = too low
adding next lowest (2) = 9 = too low
adding next lowest (3) = 12 = too high
... and so on, where the perfect answer would simply
be 8+2 = 10... i think the only solution is trying every possible combination of
numbers and stop if the amaunt you are looking for is found (or realy calculate all, if there are different solutions and save which one has used least numbers).
EDIT: realy calculating all possible combiations of 21 numbers will end up in realy, realy, realy much calculations - so there must be any "intelligent" solution for adding numbers in a special order (lik that one in niks post - with some improvements, maybe that will bring us to a reliable solution)
Without knowing if this is a homework assignment or not, I can give you some pseudo code as a hint for a possible solution, note the solution is not very efficient, more of a demonstration.
Hint:
Compare each field value to all field value and at each iteration check if their sum is equal to TOTAL_AMOUNT.
Pseudo code:
for i through field 1-20
for j through field 1-20
if value of i + value of j == total_amount
return i and j
Update:
What you seem to be having is the Subset sum problem, given within the Wiki link is pseudo code for the algorithm which might help point you in the right direction.