Generate Interpolation Function from points PHP - php

I need to make a PHP script that generates the interpolation function from the set of points.
I have decided to use the Lagrange Interpolation because it was easiest for me to find the example that generates the function from a list of input points. The issues with other methods is that I couldn't find an example that generates the function -> all other examples for all other interpolations only generate additional points and not the function out of the existing points.
The source that I've used to find the example for the Lagrange Interpolation is: http://www2.lawrence.edu/fast/GREGGJ/Math420/Section_3_1.pdf
I've decided to replicate this example in my PHP code.
/**
* Generate one basis polynomial function
* #param type $points array of points
* #param type $basisPolynomial Each basis polynomial will be stored in the array of values so that it can be appended to the final function
* #param type $allXValues all x values for the point
* #param type $i current index of the basis polynomial
*/
function generateBasisPolynomial(&$basisPolynomial, $allXValues, $i) {
$basisPolynomial[$i] = "(";
$divisor = "(";
for ($j = 0; $j < count($allXValues); $j++) {
if ($j == $i) {
continue;
}
$basisPolynomial[$i] .= "(x-$allXValues[$j])*";
$divisor .="($allXValues[$i]-$allXValues[$j])*";
}
//multiply the divisor by 1, because the previous loop has * at the end of the equation
$divisor .="1)";
$basisPolynomial[$i] .="1)/$divisor";
}
/**
* Function that generates the Lagrange interpolation from the list of points
* #param type $points
* #return string
*/
function generateLagrangeInterpolation($points) {
$numberOfPoints = count($points);
if ($numberOfPoints < 2) {
return "NaN";
} else {
//source http://www2.lawrence.edu/fast/GREGGJ/Math420/Section_3_1.pdf
//for each point, construct the basis polynomial
//for a sequence of x values, we will have n basis polynomials,
//Example:
//if we, for example have a sequence of four points, with their sequence of x values being {x0,x1,x2,x3}
//then we construct the basis polynomial for x0 by doing the following calculation:
//F(x) = ((x-x1)*(x-x2)*(x-x3))/((x0-x1)*(x0-x2)*(x0-x3)) -> where x is an unknown variable.
$basisPolynomial = array();
//get all x values from the array of points so that we can access them by index
$allXValues = array_keys($points);
$allYValues = array_values($points);
//Because the Y values are percentages, we want to divide them by 100.
$allYValues = array_map(function($val) {
return $val / 100;
}, $allYValues);
$returnFunction = "";
for ($i = 0; $i < $numberOfPoints; $i++) {
generateBasisPolynomial($basisPolynomial, $allXValues, $i);
//multiply this basis polynomial by y value
$returnFunction .="$allYValues[$i]*$basisPolynomial[$i]+";
}
//Append 0 to the end of the function because the above loop returns a function with a +
//at the end so we want to make it right
$returnFunction .="0";
echo $returnFunction;
}
}
//$points = array("4.1168" => "0.213631", "4.19236" => "0.214232", "4.20967" => "0.21441", "4.46908" => "0.218788");
$points = array("0.1" => "5", "0.3" => "10", "0.5" => "30", "0.6" => "60", "0.8" => "70");
generateLagrangeInterpolation($points);
What I am getting as a result is the following function:
0.05*((x-0.3)*(x-0.5)*(x-0.6)*(x-0.8)*1)/((0.1-0.3)*(0.1-0.5)*(0.1-0.6)*(0.1-0.8)*1)+0.1*((x-0.1)*(x-0.5)*(x-0.6)*(x-0.8)*1)/((0.3-0.1)*(0.3-0.5)*(0.3-0.6)*(0.3-0.8)*1)+0.3*((x-0.1)*(x-0.3)*(x-0.6)*(x-0.8)*1)/((0.5-0.1)*(0.5-0.3)*(0.5-0.6)*(0.5-0.8)*1)+0.6*((x-0.1)*(x-0.3)*(x-0.5)*(x-0.8)*1)/((0.6-0.1)*(0.6-0.3)*(0.6-0.5)*(0.6-0.8)*1)+0.7*((x-0.1)*(x-0.3)*(x-0.5)*(x-0.6)*1)/((0.8-0.1)*(0.8-0.3)*(0.8-0.5)*(0.8-0.6)*1)+0
I don't care that the expression is simplified and calculated fully (however if you have any advice or code that could do that for me it would be a huge plus).
If I look at the simplified expression it looks something like this:
(47500*x^4-79300*x^3+42245*x^2-8699*x+480)/(-840)
However if I try to paste that function into http://fooplot.com -> I get that the graph is passing through the points defined as the input parameters, however, I'm not sure if the graph for the other points is correct as it looks like it's Y values go into minus values when X <=0 or x>=1.
Do you advise that I use the different function or the existing error in the interpolation can be reduced if I had more input points? I have to be honest that I am a poor mathematician so any real example of a more accurate method or example in the code would be greatly appreciated.
Thanks

Here's what you can try:
function basisPolynomial($points, $j, $x) {
$xj = $points[$j][0]; //Assume a point is an array of 2 numbers
$partialProduct = 1;
//Product loop
for ($m = 0;$i < count($points);$m++) {
if ($m === $j) { continue; }
$partialProduct *= ($x - $points[$m][0])/($xj-$points[$m][0]);
}
return $partialProduct;
}
function lagrangePolynomial($points,$x) {
$partialSum = 0;
for ($j = 0;$j < count($points);$j++) {
$partialSum += $points[$j][1]*basisPolynomial($points,$j,$x);
}
return $partialSum;
}
Now if you need to plot it you can generate a list of points that can be used in a plotting function e.g.
$points = <my points>;
$plotPoints = [];
for ($i = 0;$i < 10;$i+= 0.1) { //for example
$plotPoints[] = [ $i, lagrangePolynomial($points,$i) ];
}
If you want to just use the to directly plot you need to use a plotting tool like gnuplot to define the functions and have it determine how to plot them.
Update: http://www.physics.brocku.ca/Courses/5P10/lectures/lecture_10_handout.pdf seems to have a gnuplot example of exactly what you need but it feels like cheating of sorts

I'm not much familiarized with the object oriented implementation of php, but in java I do this little baby ;)
import java.util.*;
import java.util.Arrays;
import java.util.List;
public class Run{
public static void main(String[] args){
int[] parameters = new int[]{-1, 2, 4, 3};
Binom b = new Binom(1, 1, parameters[1], 0);
Polinom p = new Polinom(b);
for(int i = 2; i < parameters.length; i++)
p.joinBinom(new Binom(1, 1, -1 * parameters[i], 0));
System.out.println(p.toString() + " / (" + getDenominator(parameters) + ")");
}
public static int getDenominator(int[] params){
int result = 1;
for(int i = 1; i < params.length; i++)
result *= params[0] - params[i];
return result;
}
}
class Monomial{
private int constant = 1;
private int pow = 0;
public int getConstant(){
return this.constant;
}
public void sumConstant(int value){
this.constant += value;
}
public boolean hasVariable(){
return this.pow > 0;
}
public int getPow(){
return this.pow;
}
public Monomial(int constant, int pow){
this.constant = constant;
this.pow = pow;
}
public ArrayList<Monomial> multiply(Binom a){
Monomial first = new Monomial(this.constant * a.getFirst().getConstant(), this.pow + a.getFirst().getPow());
Monomial second = new Monomial(this.constant * a.getSecond().getConstant(), this.pow + a.getSecond().getPow());
System.out.print("\t" + this.toString() + "\t* (" + a.getFirst().toString() + " " + a.getSecond().toString() + ")");
System.out.print("\t= " + first.toString() + "\t");
System.out.println(second.toString());
return (new Binom(first, second)).toList();
}
public String toString(){
String result = "";
if(this.constant == 1){
if(!this.hasVariable())
result += this.constant;
}
else
result += this.constant;
if(this.hasVariable()){
result += "X";
if(this.pow > 1)
result += "^" + this.pow;
}
return result;
}
}
class Binom{
private Monomial first;
private Monomial second;
public Monomial getFirst(){
return this.first;
}
public Monomial getSecond(){
return this.second;
}
public Binom(int constant1, int pow1, int constant2, int pow2){
this.first = new Monomial(constant1, pow1);
this.second = new Monomial(constant2, pow2);
}
public Binom(Monomial a, Monomial b){
this.first = a;
this.second = b;
}
public ArrayList<Monomial> toList(){
ArrayList<Monomial> result = new ArrayList<>();
result.add(this.first);
result.add(this.second);
return result;
}
}
class Polinom{
private ArrayList<Monomial> terms = new ArrayList<>();
public Polinom(Binom b){
this.terms.add(b.getFirst());
this.terms.add(b.getSecond());
}
private void compact(){
for(int i = 0; i < this.terms.size(); i++){
Monomial term = this.terms.get(i);
for(int j = i + 1; j < this.terms.size(); j++){
Monomial test = this.terms.get(j);
if(term.getPow() == test.getPow()){
term.sumConstant(test.getConstant());
this.terms.remove(test);
j--;
}
}
}
}
public void joinBinom(Binom b){
ArrayList<Monomial> result = new ArrayList<>();
for(Monomial t : this.terms){
result.addAll(t.multiply(b));
}
this.terms = result;
this.compact();
}
public String toString(){
String result = "";
for(Monomial t : this.terms)
result += (t.getConstant() < 0 ? " " : " +") + t.toString();
return "(" + result + ")";
}
}
which return:
X * (X -4) = X^2 -4X
2 * (X -4) = 2X -8
X^2 * (X -3) = X^3 -3X^2
-2X * (X -3) = -2X^2 6X
-8 * (X -3) = -8X 24
( +X^3 -5X^2 -2X +24) / (-60)

Looks like the current algorithm for Lagrange interpolation method provides the correct results. To correct the errors in calculation, more base points can be provided. Also, to multiply the unknown variables in mathematic function, a function example was left in one of the answers.
Thanks everyone.

Related

How to organize in chunks a not accurate division?

I have a number of participants and a number of groups, and I have to organize the participants into groups.
Example:
10/3 = 3, 3 and 4.
10/9 = 2,2,2 and 4.
23/3 = 6,6,6 and 5.
I have tried with array_chunk using the size paramether as a rounded result of participants/groups but it Did not work well.
Edit with my problem solved.
$groups = $this->request->data['phases_limit'];
$classified_lmt = $this->request->data['classified_limit'];
$participants = count($game->user_has_game);
$participants_lmt = floor($participants / $groups);
$remainders = $participants % $groups;
if ($groups > $participants) {
throw new \Exception("Há mais grupos que participantes");
}
for ($i=0; $i < $groups; $i++) {
$p = $this->Phase->newEntity();
$p->name = 'Grupo #' . $game->id;
$p->game_id = $game->id;
$p->classified_limit = $classified_lmt;
$this->Phase->save($p);
// add the number of participants per group
for ($j=0; $j < $participants_lmt; $j++) {
$user_has_game = array_pop($game->user_has_game);
$g = $this->Phase->GroupUserHasGame->newEntity();
$g->group_id = $p->id;
$g->user_has_game_id = $user_has_game->id;
$this->Phase->GroupUserHasGame->save($g);
}
// check if it is the last iteration
if (($groups - 1) == $i) {
// add the remainders on the last iteration
for ($k=0; $k < $remainders; $k++) {
$user_has_game = array_pop($game->user_has_game);
$g = $this->Phase->GroupUserHasGame->newEntity();
$g->group_id = $p->id;
$g->user_has_game_id = $user_has_game->id;
$this->Phase->GroupUserHasGame->save($g);
}
}
}
Have you tried the modulus operator? It gives you the remainder after dividing the numerator by the denominator.
For example, if you want to split 10 people into 3 groups:
floor(10 / 3) = 3; // people per group
10 % 3 = 1; // 1 person left over to add to an existing group.
Edit - I included the following function as part of my original answer. This doesn't work for OP, however I want to leave it here, as it may help others.
function group($total, $groups)
{
// Calculate participants per group and remainder
$group = floor($total / $groups);
$remainder = $total % $groups;
// Prepare groupings and append remaining participant to first group
$groupings = array_fill(0, $groups, $group);
$groupings[0] += $remainder;
return $groupings;
}
Not sure there are off-the-shelf libraries for that. I just implemented something similar in Java if you need some ideas:
public List<Integer> createDistribution(int population_size, int groups) {
List<Integer> lst = new LinkedList();
int total = 0;
for (double d : createDistribution(groups)) {
// this makes smaller groups first int i = new Double(population_size * d).intValue();
int i = (int)Math.round(population_size * d);
total += i;
lst.add(i);
}
// Fix rounding errors
while (total < population_size) {
int i = r.nextInt(groups);
lst.set(i, lst.get(i) + 1);
total += 1;
}
while (total > population_size) {
int i = r.nextInt(groups);
if (lst.get(i) > 0) {
lst.set(i, lst.get(i) - 1);
total -= 1;
}
}
return lst;
}

Choosing coins with least or no change given

I am making a game which consists of coin denominations of $10, $5, $3, and $1. The player may have 0 or more of each type of currency in his inventory with a maximum of 15 coins in total. I am trying to figure out how to properly select coins so that the least amount of change is given in return. At first I thought this was going to be easy to solve, but now I'm having trouble wrapping my head around it.
Here are two examples that explain the situation further:
Example 1:
The user is carrying these coins: $5, $3, $3, $3, $1, $1, $1, $1 and want to buy an item for $12. The solution would be to pay with $5, $3, $3, $1 and give no change.
Example 2:
The user does not have any $1 coins, and is carrying $5, $3, $3, $3, $3. An item is bought for $12 so they pay with $5, $3, $3, and $3 and change of $2 is given back.
Since we select the larger coins first, what I can't figure out is how to know if there are enough lower valued coins ($1 in this case) in the player's inventory to accommodate example 1, and if there aren't enough to use more of the higher valued coins as in example 2.
A further issue is seen in the following example, though I'd be happy just getting the above two examples working:
Example 3:
The user is carrying these coins: $5, $3, $3, $3. The player buys something for $6. It would be better to use $3 and $3 and return no change rather than using $5 and $3 and give $2 in change.
I believe the first two examples can be solved using recursion and a variation of the greedy algorithm.
For the bounty award:
I have added my own answer below as a temporary solution for now. However, I like the approach of Mr. Llama's below (see the link he references) and would like to find a PHP example to satisfy this. I believe this approach does not need recursion and uses memoization.
If there are multiple options for the least amount of change then I would like the tie to be given to the one that pays with the least amount of coins.
The problem can be defined as:
Return a subset of items where the sum is closest to x, but >= x.
This problem is called the subset sum problem. It is NP-complete. You won't find a perfect algorithm that runs in pseudo-polynomial time, only imperfect heuristics.
However, if the number of coins is very small, then an exhaustive search of the solution space will certainly work.
If the number of coins is larger, then you should look at Wikipedia for an overview: https://en.wikipedia.org/wiki/Subset_sum_problem#Polynomial_time_approximate_algorithm
I had a similar problem except instead of being allowed to go over, the combination had to stay under the target amount. In the end, I used the dynamic approach presented in this answer. You should be able to use it too.
It goes something like this:
Start with a list consisting of a single empty element.
For each entry in the list...
Copy the entry and add to it the first coin (not coin value!) that it doesn't contain.
Store the new element in the original list if and only if* its new sum value doesn't already exist in the list.
Repeat step 2 until you make a pass where no new elements are added to the list
Iterate the result list and keep the best combination (using your criteria)
*: We can make this optimization because we don't particularly care which coins are used in the combination, only the sum value of the collection of coins.
The above algorithm can be optimized a bit if you use the sum value as the key.
I have come up with the following solution. If others can critique it for me I would appreciate it.
<?php
$coin_value = array(10,5,3,1);
$inventory = array(1,2,0,2);
$price = 17;
for ($i = 3; $i >= 0; $i--){
$btotal = 0;
$barray = array();
for ($j = 0; $j < 4; $j++){
$remaining = $price - $btotal;
$to_add = floor($remaining / $coin_value[$j]);
if ($i != 3 && $i == $j){
$to_add++;
}
if ($inventory[$j] < $to_add){
$to_add = $inventory[$j];
}
$btotal += $to_add * $coin_value[$j];
for ($k = 0; $k < $to_add; $k++){
$barray[] = $coin_value[$j];
}
if ($btotal >= $price)
break 2; //warning: breaks out of outer loop
}
}
$change_due = $btotal - $price;
print_r($barray);
echo "Change due: \$$change_due\n";
?>
It covers examples 1 and 2 in my original question, but does not cover example 3. However, I think it will do for now unless someone can come up with a better solution. I decided not to use recursion as it would seem to take too much time.
You can use a stack to enumerate valid combinations. The version below uses a small optimization, calculating if a minimum of the current denomination is needed. More than one least change combinations are returned if there are any, which could be restricted with memoization; one could also add an early exit if the current denomination could complete the combination with zero change. I hope the laconically commented code is self-explanatory (let me know if you'd like further explanation):
function leastChange($coin_value,$inventory,$price){
$n = count($inventory);
$have = 0;
for ($i=0; $i<$n; $i++){
$have += $inventory[$i] * $coin_value[$i];
}
$stack = [[0,$price,$have,[]]];
$best = [-max($coin_value),[]];
while (!empty($stack)){
// each stack call traverses a different set of parameters
$parameters = array_pop($stack);
$i = $parameters[0];
$owed = $parameters[1];
$have = $parameters[2];
$result = $parameters[3];
// base case
if ($owed <= 0){
if ($owed > $best[0]){
$best = [$owed,$result];
} else if ($owed == $best[0]){
// here you can add a test for a smaller number of coins
$best[] = $result;
}
continue;
}
// skip if we have none of this coin
if ($inventory[$i] == 0){
$result[] = 0;
$stack[] = [$i + 1,$owed,$have,$result];
continue;
}
// minimum needed of this coin
$need = $owed - $have + $inventory[$i] * $coin_value[$i];
if ($need < 0){
$min = 0;
} else {
$min = ceil($need / $coin_value[$i]);
}
// add to stack
for ($j=$min; $j<=$inventory[$i]; $j++){
$stack[] = [$i + 1,$owed - $j * $coin_value[$i],$have - $inventory[$i] * $coin_value[$i],array_merge($result,[$j])];
if ($owed - $j * $coin_value[$i] < 0){
break;
}
}
}
return $best;
}
Output:
$coin_value = [10,5,3,1];
$inventory = [0,1,3,4];
$price = 12;
echo json_encode(leastChange($coin_value,$inventory,$price)); // [0,[0,1,2,1],[0,1,1,4],[0,0,3,3]]
$coin_value = [10,5,3,1];
$inventory = [0,1,4,0];
$price = 12;
echo json_encode(leastChange($coin_value,$inventory,$price)); // [0,[0,0,4]]
$coin_value = [10,5,3,1];
$inventory = [0,1,3,0];
$price = 6;
echo json_encode(leastChange($coin_value,$inventory,$price)); // [0,[0,0,2]]
$coin_value = [10,5,3,1];
$inventory = [0,1,3,0];
$price = 7;
echo json_encode(leastChange($coin_value,$inventory,$price)); // [-1,[0,1,1]]
Update:
Since you are also interested in the lowest number of coins, I think memoization could only work if we can guarantee that a better possibility won't be skipped. I think this can be done if we conduct our depth-first-search using the most large coins we can first. If we already achieved the same sum using larger coins, there's no point in continuing the current thread. Make sure the input inventory is presenting coins sorted in descending order of denomination size and add/change the following:
// maximum needed of this coin
$max = min($inventory[$i],ceil($owed / $inventory[$i]));
// add to stack
for ($j=$max; $j>=$min; $j--){
The solution I was able to made covers the 3 examples posted in your question. And always gives the change with as few coins as possible.
The tests I made seemed to be executed very fast.
Here I post the code:
<?php
//Example values
$coin_value = array(10,5,3,1);
$inventory = array(5,4,3,0);
$price = 29;
//Initialize counters
$btotal = 0;
$barray = array(0,0,0,0);
//Get the sum of coins
$total_coins = array_sum($inventory);
function check_availability($i) {
global $inventory, $barray;
$a = $inventory[$i];
$b = $barray[$i];
$the_diff = $a - $b;
return $the_diff != 0;
}
/*
* Checks the lower currency available
* Returns index for arrays, or -1 if none available
*/
function check_lower_available() {
for ($i = 3; $i >= 0; $i--) {
if (check_availability($i)) {
return $i;
}
}
return -1;
}
for($i=0;$i<4;$i++) {
while(check_availability($i) && ($btotal + $coin_value[$i]) <= $price) {
$btotal += $coin_value[$i];
$barray[$i]++;
}
}
if($price != $btotal) {
$buf = check_lower_available();
for ($i = $buf; $i >= 0; $i--) {
if (check_availability($i) && ($btotal + $coin_value[$i]) > $price) {
$btotal += $coin_value[$i];
$barray[$i]++;
break;
}
}
}
// Time to pay
$bchange = 0;
$barray_change = array(0,0,0,0);
if ($price > $btotal) {
echo "You have not enough money.";
}
else {
$pay_msg = "You paid $".$btotal."\n\n";
$pay_msg.= "You used ".$barray[0]." coins of $10\n";
$pay_msg.= "You used ".$barray[1]." coins of $5\n";
$pay_msg.= "You used ".$barray[2]." coins of $3\n";
$pay_msg.= "You used ".$barray[3]." coins of $1\n\n\n";
// Time to give change
$the_diff = $btotal - $price;
if (!empty($the_diff)) {
for ($i = 0; $i < 4; $i++) {
while($the_diff >= $coin_value[$i]) {
$bchange += $coin_value[$i];
$barray_change[$i]++;
$the_diff -= $coin_value[$i];
}
}
$check_sum = array_sum($inventory) - array_sum($barray);
$check_sum+= array_sum($barray_change);
$msg = "";
if ($check_sum < 15) {
$change_msg = "Your change: $".$bchange."\n\n";
$change_msg.= "You received ".$barray_change[0]." coins of $10\n";
$change_msg.= "You received ".$barray_change[1]." coins of $5\n";
$change_msg.= "You received ".$barray_change[2]." coins of $3\n";
$change_msg.= "You received ".$barray_change[3]." coins of $1\n\n";
$msg = $pay_msg.$change_msg;
}
else {
$msg = "You have not enough space to hold the change.\n";
$msg.= "Buy cancelled.\n";
}
}
else {
$msg = $pay_msg."You do not need change\n";
}
if ($check_sum < 15) {
for ($i = 0; $i < 4; $i++) {
$inventory[$i] -= $barray[$i];
$total_coins-= $barray[$i];
}
for ($i = 0; $i < 4; $i++) {
$inventory[$i] += $barray_change[$i];
$total_coins+= $barray[$i];
}
}
echo $msg;
echo "Now you have:\n";
echo $inventory[0]." coins of $10\n";
echo $inventory[1]." coins of $5\n";
echo $inventory[2]." coins of $3\n";
echo $inventory[3]." coins of $1\n";
}
I don't know PHP so I've tried it in Java. I hope that is ok as its the algorithm that is important.
My code is as follows:
package stackoverflow.changecalculator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ChangeCalculator
{
List<Integer> coinsInTil = new ArrayList<>();
public void setCoinsInTil(List<Integer> coinsInTil)
{
this.coinsInTil = coinsInTil;
}
public Map<String, List> getPaymentDetailsFromCoinsAvailable(final int amountOwed, List<Integer> inPocketCoins)
{
List<Integer> paid = new ArrayList<>();
int remaining = amountOwed;
// Check starting with the largest coin.
for (Integer coin : inPocketCoins)
if (remaining > 0 && (remaining - coin) >= 0) {
paid.add(coin);
remaining = remaining - coin;
}
ProcessAlternative processAlternative = new ProcessAlternative(amountOwed, inPocketCoins, paid, remaining).invoke();
paid = processAlternative.getPaid();
remaining = processAlternative.getRemaining();
removeUsedCoinsFromPocket(inPocketCoins, paid);
int changeOwed = payTheRestWithNonExactAmount(inPocketCoins, paid, remaining);
List<Integer> change = calculateChangeOwed(changeOwed);
Map<String, List> result = new HashMap<>();
result.put("paid", paid);
result.put("change", change);
return result;
}
private void removeUsedCoinsFromPocket(List<Integer> inPocketCoins, List<Integer> paid)
{
for (int i = 0; i < inPocketCoins.size(); i++) {
Integer coin = inPocketCoins.get(i);
if (paid.contains(coin))
inPocketCoins.remove(i);
}
}
private List<Integer> calculateChangeOwed(int changeOwed)
{
List<Integer> change = new ArrayList<>();
if (changeOwed < 0) {
for (Integer coin : coinsInTil) {
if (coin + changeOwed == 0) {
change.add(coin);
changeOwed = changeOwed + coin;
}
}
}
return change;
}
private int payTheRestWithNonExactAmount(List<Integer> inPocketCoins, List<Integer> paid, int remaining)
{
if (remaining > 0) {
for (int coin : inPocketCoins) {
while (remaining > 0) {
paid.add(coin);
remaining = remaining - coin;
}
}
}
return remaining;
}
}
The ProcessAlternative class handles cases where the largest coin doesn't allow us to get a case where there is no change to be returned so we try an alternative.
package stackoverflow.changecalculator;
import java.util.ArrayList;
import java.util.List;
// if any remaining, check if we can pay with smaller coins first.
class ProcessAlternative
{
private int amountOwed;
private List<Integer> inPocketCoins;
private List<Integer> paid;
private int remaining;
public ProcessAlternative(int amountOwed, List<Integer> inPocketCoins, List<Integer> paid, int remaining)
{
this.amountOwed = amountOwed;
this.inPocketCoins = inPocketCoins;
this.paid = paid;
this.remaining = remaining;
}
public List<Integer> getPaid()
{
return paid;
}
public int getRemaining()
{
return remaining;
}
public ProcessAlternative invoke()
{
List<Integer> alternative = new ArrayList<>();
int altRemaining = amountOwed;
if (remaining > 0) {
for (Integer coin : inPocketCoins)
if (altRemaining > 0 && factorsOfAmountOwed(amountOwed).contains(coin)) {
alternative.add(coin);
altRemaining = altRemaining - coin;
}
// if alternative doesn't require change, use it.
if (altRemaining == 0) {
paid = alternative;
remaining = altRemaining;
}
}
return this;
}
private ArrayList<Integer> factorsOfAmountOwed(int num)
{
ArrayList<Integer> aux = new ArrayList<>();
for (int i = 1; i <= num / 2; i++)
if ((num % i) == 0)
aux.add(i);
return aux;
}
}
I worked in it by doing a test for example 1, then for example 2, and lastly moved on to example 3. The process alternative bit was added here and the alternative for the original test coins returned 0 change required so I updated to the amount input to 15 instead of 12 so it would calculate the change required.
Tests are as follows:
package stackoverflow.changecalculator;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ChangeCalculatorTest
{
public static final int FIFTY_PENCE = 0;
public static final int TWENTY_PENCE = 1;
public static final int TEN_PENCE = 2;
public static final int FIVE_PENCE = 3;
public static final int TWO_PENCE = 4;
public static final int PENNY = 5;
public ChangeCalculator calculator;
#Before
public void setUp() throws Exception
{
calculator = new ChangeCalculator();
List<Integer> inTil = new ArrayList<>();
inTil.add(FIFTY_PENCE);
inTil.add(TWENTY_PENCE);
inTil.add(TEN_PENCE);
inTil.add(FIVE_PENCE);
inTil.add(TWO_PENCE);
inTil.add(PENNY);
calculator.setCoinsInTil(inTil);
}
#Test
public void whenHaveExactAmount_thenNoChange() throws Exception
{
// $5, $3, $3, $3, $1, $1, $1, $1
List<Integer> inPocket = new ArrayList<>();
inPocket.add(5);
inPocket.add(3);
inPocket.add(3);
inPocket.add(3);
inPocket.add(1);
inPocket.add(1);
inPocket.add(1);
inPocket.add(1);
Map<String, List> result = calculator.getPaymentDetailsFromCoinsAvailable(12, inPocket);
List change = result.get("change");
assertTrue(change.size() == 0);
List paid = result.get("paid");
List<Integer> expected = new ArrayList<>();
expected.add(5);
expected.add(3);
expected.add(3);
expected.add(1);
assertEquals(expected, paid);
}
#Test
public void whenDoNotHaveExactAmount_thenChangeReturned() throws Exception {
// $5, $3, $3, $3, $3
List<Integer> inPocket = new ArrayList<>();
inPocket.add(5);
inPocket.add(3);
inPocket.add(3);
inPocket.add(3);
inPocket.add(3);
Map<String, List> result = calculator.getPaymentDetailsFromCoinsAvailable(15, inPocket);
List change = result.get("change");
Object actual = change.get(0);
assertEquals(2, actual);
List paid = result.get("paid");
List<Integer> expected = new ArrayList<>();
expected.add(5);
expected.add(3);
expected.add(3);
expected.add(3);
expected.add(3);
assertEquals(expected, paid);
}
#Test
public void whenWeHaveExactAmountButItDoesNotIncludeBiggestCoin_thenPayWithSmallerCoins() throws Exception {
// $5, $3, $3, $3
List<Integer> inPocket = new ArrayList<>();
inPocket.add(5);
inPocket.add(3);
inPocket.add(3);
inPocket.add(3);
Map<String, List> result = calculator.getPaymentDetailsFromCoinsAvailable(6, inPocket);
List change = result.get("change");
assertTrue(change.size() == 0);
List paid = result.get("paid");
List<Integer> expected = new ArrayList<>();
expected.add(3);
expected.add(3);
assertEquals(expected, paid);
}
}
The tests are not the cleanest yet but they are all passing thus far. I may go back and add some more test cases later to see if I can break it but don't have time right now.
This answer is based off of גלעד-ברקן's answer. I am posting it here as per his request. While none of the answers were quite the one that I was looking for I found that this was the best option posted. Here is the modified algorithm that I am currently using:
<?php
function leastChange($inventory, $price){
//NOTE: Hard coded these in the function for my purposes, but $coin value can be passed as a parameter for a more general-purpose algorithm
$num_coin_types = 4;
$coin_value = [10,5,3,1];
$have = 0;
for ($i=0; $i < $num_coin_types; $i++){
$have += $inventory[$i] * $coin_value[$i];
}
//NOTE: Check to see if you have enough money to make this purchase
if ($price > $have){
$error = ["error", "Insufficient Funds"];
return $error;
}
$stack = [[0,$price,$have,[]]];
$best = [-max($coin_value),[]];
while (!empty($stack)){
// each stack call traverses a different set of parameters
$parameters = array_pop($stack);
$i = $parameters[0];
$owed = $parameters[1];
$have = $parameters[2];
$result = $parameters[3];
if ($owed <= 0){
//NOTE: check for new option with least change OR if same as previous option check which uses the least coins paid
if ($owed > $best[0] || ($owed == $best[0] && (array_sum($result) < array_sum($best[1])))){
//NOTE: add extra zeros to end if needed
while (count($result) < 4){
$result[] = 0;
}
$best = [$owed,$result];
}
continue;
}
// skip if we have none of this coin
if ($inventory[$i] == 0){
$result[] = 0;
$stack[] = [$i + 1,$owed,$have,$result];
continue;
}
// minimum needed of this coin
$need = $owed - $have + $inventory[$i] * $coin_value[$i];
if ($need < 0){
$min = 0;
} else {
$min = ceil($need / $coin_value[$i]);
}
// add to stack
for ($j=$min; $j<=$inventory[$i]; $j++){
$stack[] = [$i + 1,$owed - $j * $coin_value[$i],$have - $inventory[$i] * $coin_value[$i],array_merge($result,[$j])];
if ($owed - $j * $coin_value[$i] < 0){
break;
}
}
}
return $best;
}
Here is my test code:
$start = microtime(true);
$inventory = [0,1,3,4];
$price = 12;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";
$inventory = [0,1,4,0];
$price = 12;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";
$inventory = [0,1,4,0];
$price = 6;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";
$inventory = [0,1,4,0];
$price = 7;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";
$inventory = [1,3,3,10];
$price=39;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";
$inventory = [1,3,3,10];
$price=45;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";
//stress test
$inventory = [25,25,25,1];
$price=449;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";
$time_elapsed = microtime(true) - $start;
echo "\n Time taken: $time_elapsed \n";
The result:
[0,[0,1,2,1]]
[0,[0,0,4,0]]
[0,[0,0,2,0]]
[-1,[0,1,1,0]]
[0,[1,3,3,5]]
["error","Insufficient Funds"]
[-1,[25,25,25,0]]
Time taken: 0.0046839714050293
Of course that time is in microseconds and therefore it executed in a fraction of a second!
This is my solution i do not know how efficient is it but it works,i am open for suggestion.
<?php
$player=array(0,3,1,0);//how much coins you have
$player_copy=$player;
$coin_count=array(0,0,0,0);//memorize which coins you gave
$coin_value=array(1,3,5,10);
$price=6; //price of item
$price_copy=$price;
$z=3;
$change=array(-1,-1,-1,-1,-1); //memorise possible changes you can get
$k=0;
$flag=0;
label1: for($j=3;$j>=0;$j--){
$coin_count[$j]=0;
$player[$j]=$player_copy[$j];
}
for($j=$z;$j>=0;$j--){
while(($price>0) && 1<=$player[$j]){
$price-=$coin_value[$j];
$player[$j]--;
$coin_count[$j]++;
}
}
$change[$k++]=$price;
if($price!=0){
for($j=$z;$j>=0;$j--)
if($price_copy>$coin_value[$j]){
$z=$j-1;
$price=$price_copy;
goto label1;
}
$flag=1;
}
//find minimum change
$minv=$change[0];
for($i=1;$change[$i]>=0 and $i<4;$i++)
if($change[$i]>$minv)
$minv=$change[$i];
$i;
//when you find minimum change find which coins you have used
for($i=0;$i<4;$i++)
if($change[$i]==$minv && $flag==1){
$flag=2;
for($j=3;$j>=0;$j--){//reset coin_count and player budget
$coin_count[$j]=0;
$player[$j]=$player_copy[$j];
}
for($j=3-($i%2)-1;$j>=0;$j--){
while(($price>0) && 1<=$player[$j]){
$price-=$coin_value[$j];
$player[$j]--;
$coin_count[$j]++;
}
}
}
//prints result
for($j=0;$j<4;$j++)
printf("%d x %d\n",$coin_count[$j],$coin_value[$j]);
printf("change: %d\n",$minv);
?>

PHP Two Tailed Z-Score to P (Probability) Calculator

I've been searching all over and cannot find anything about this.
I'm looking for a function that will convert a Z-Score to a Probability using a two tailed table, preferably in PHP.
(Like this one: http://www.sjsu.edu/faculty/gerstman/StatPrimer/z-two-tails.pdf)
My only other option is to make an array based on that table and comparing the Z-Score.. There must be a better way.
Edit:
Below is a slab of code found on the PHP.net statistics function page. These functions are really poorly documented.
The functions below will accurately calculate a one-tailed z-score into a probability.
function erf($x)
{
$pi = 3.1415927;
$a = (8*($pi - 3))/(3*$pi*(4 - $pi));
$x2 = $x * $x;
$ax2 = $a * $x2;
$num = (4/$pi) + $ax2;
$denom = 1 + $ax2;
$inner = (-$x2)*$num/$denom;
$erf2 = 1 - exp($inner);
return sqrt($erf2);
}
function cdf($n)
{
if($n < 0)
{
return (1 - erf($n / sqrt(2)))/2;
}
else
{
return (1 + erf($n / sqrt(2)))/2;
}
}
Found a solution:
function erf($x)
{
$pi = 3.1415927;
$a = (8*($pi - 3))/(3*$pi*(4 - $pi));
$x2 = $x * $x;
$ax2 = $a * $x2;
$num = (4/$pi) + $ax2;
$denom = 1 + $ax2;
$inner = (-$x2)*$num/$denom;
$erf2 = 1 - exp($inner);
return sqrt($erf2);
}
function cdf($n)
{
return (1 - erf($n / sqrt(2)))/2;
//I removed the $n < 0 test which inverses the +1/-1
}
function cdf_2tail($n)
{
return 2*cdf($n);
//After a little more digging around, the two tail test is simply 2 x the cdf.
}
I tested my results against: http://vassarstats.net/tabs.html#z and the z-score table.
It is correct to 0.1%

String similarity in PHP: levenshtein like function for long strings

The function levenshtein in PHP works on strings with maximum length 255. What are good alternatives to compute a similarity score of sentences in PHP.
Basically I have a database of sentences, and I want to find approximate duplicates.
similar_text function is not giving me expected results. What is the easiest way for me to detect similar sentences like below:
$ss="Jack is a very nice boy, isn't he?";
$pp="jack is a very nice boy is he";
$ss=strtolower($ss); // convert to lower case as we dont care about case
$pp=strtolower($pp);
$score=similar_text($ss, $pp);
echo "$score %\n"; // Outputs just 29 %
$score=levenshtein ( $ss, $pp );
echo "$score\n"; // Outputs '5', which indicates they are very similar. But, it does not work for more than 255 chars :(
The levenshtein algorithm has a time complexity of O(n*m), where n and m are the lengths of the two input strings. This is pretty expensive and computing such a distance for long strings will take a long time.
For whole sentences, you might want to use a diff algorithm instead, see for example: Highlight the difference between two strings in PHP
Having said this, PHP also provides the similar_text function which has an even worse complexity (O(max(n,m)**3)) but seems to work on longer strings.
I've found the Smith Waterman Gotoh to be the best algorithm for comparing sentences. More info in this answer. Here is the PHP code example:
class SmithWatermanGotoh
{
private $gapValue;
private $substitution;
/**
* Constructs a new Smith Waterman metric.
*
* #param gapValue
* a non-positive gap penalty
* #param substitution
* a substitution function
*/
public function __construct($gapValue=-0.5,
$substitution=null)
{
if($gapValue > 0.0) throw new Exception("gapValue must be <= 0");
//if(empty($substitution)) throw new Exception("substitution is required");
if (empty($substitution)) $this->substitution = new SmithWatermanMatchMismatch(1.0, -2.0);
else $this->substitution = $substitution;
$this->gapValue = $gapValue;
}
public function compare($a, $b)
{
if (empty($a) && empty($b)) {
return 1.0;
}
if (empty($a) || empty($b)) {
return 0.0;
}
$maxDistance = min(mb_strlen($a), mb_strlen($b))
* max($this->substitution->max(), $this->gapValue);
return $this->smithWatermanGotoh($a, $b) / $maxDistance;
}
private function smithWatermanGotoh($s, $t)
{
$v0 = [];
$v1 = [];
$t_len = mb_strlen($t);
$max = $v0[0] = max(0, $this->gapValue, $this->substitution->compare($s, 0, $t, 0));
for ($j = 1; $j < $t_len; $j++) {
$v0[$j] = max(0, $v0[$j - 1] + $this->gapValue,
$this->substitution->compare($s, 0, $t, $j));
$max = max($max, $v0[$j]);
}
// Find max
for ($i = 1; $i < mb_strlen($s); $i++) {
$v1[0] = max(0, $v0[0] + $this->gapValue, $this->substitution->compare($s, $i, $t, 0));
$max = max($max, $v1[0]);
for ($j = 1; $j < $t_len; $j++) {
$v1[$j] = max(0, $v0[$j] + $this->gapValue, $v1[$j - 1] + $this->gapValue,
$v0[$j - 1] + $this->substitution->compare($s, $i, $t, $j));
$max = max($max, $v1[$j]);
}
for ($j = 0; $j < $t_len; $j++) {
$v0[$j] = $v1[$j];
}
}
return $max;
}
}
class SmithWatermanMatchMismatch
{
private $matchValue;
private $mismatchValue;
/**
* Constructs a new match-mismatch substitution function. When two
* characters are equal a score of <code>matchValue</code> is assigned. In
* case of a mismatch a score of <code>mismatchValue</code>. The
* <code>matchValue</code> must be strictly greater then
* <code>mismatchValue</code>
*
* #param matchValue
* value when characters are equal
* #param mismatchValue
* value when characters are not equal
*/
public function __construct($matchValue, $mismatchValue) {
if($matchValue <= $mismatchValue) throw new Exception("matchValue must be > matchValue");
$this->matchValue = $matchValue;
$this->mismatchValue = $mismatchValue;
}
public function compare($a, $aIndex, $b, $bIndex) {
return ($a[$aIndex] === $b[$bIndex] ? $this->matchValue
: $this->mismatchValue);
}
public function max() {
return $this->matchValue;
}
public function min() {
return $this->mismatchValue;
}
}
$str1 = "Jack is a very nice boy, isn't he?";
$str2 = "jack is a very nice boy is he";
$o = new SmithWatermanGotoh();
echo $o->compare($str1, $str2);
You could try using similar_text.
It can get quite slow with 20,000+ characters (3-5 seconds) but your example you mention using only sentences, this will work just fine for that usage.
One thing to note is when comparing string of different sizes you will not get 100%. For example if you compare "he" with "head" you would only get a 50% match.

PHP inverse of a matrix

I saw this question,and pop up this idea.
Is there an efficient way to do this in PHP?
EDIT
Best with a demo?
You could use the pear package Math_Matrix for this.
This package claims to be able to do what you are looking for.
There is this open source PHP Library that is able to invert a Matrix.
All you need to do is
<?php
include_once ("Matrix.class.php");
$matrixA = new Matrix(array(array(0, 1), array(2, 6)));
echo $matrixA->getInverse()->getMathMl();
?>
Here tested code https://gist.github.com/unix1/7510208
Only identity_matrix() and invert() functions are enough
Yes there are several ways to accomplish this in php. There are a handful of available libraries. Alternatively, you could maintain your own class and customize as needed. Here is an excerpt from our inhouse library that is based on the mathematical method described in the link. There is a demonstration at the end of the class for further reference.
https://www.intmath.com/matrices-determinants/inverse-matrix-gauss-jordan-elimination.php
class MatrixLibrary
{
//Gauss-Jordan elimination method for matrix inverse
public function inverseMatrix(array $matrix)
{
//TODO $matrix validation
$matrixCount = count($matrix);
$identityMatrix = $this->identityMatrix($matrixCount);
$augmentedMatrix = $this->appendIdentityMatrixToMatrix($matrix, $identityMatrix);
$inverseMatrixWithIdentity = $this->createInverseMatrix($augmentedMatrix);
$inverseMatrix = $this->removeIdentityMatrix($inverseMatrixWithIdentity);
return $inverseMatrix;
}
private function createInverseMatrix(array $matrix)
{
$numberOfRows = count($matrix);
for($i=0; $i<$numberOfRows; $i++)
{
$matrix = $this->oneOperation($matrix, $i, $i);
for($j=0; $j<$numberOfRows; $j++)
{
if($i !== $j)
{
$matrix = $this->zeroOperation($matrix, $j, $i, $i);
}
}
}
$inverseMatrixWithIdentity = $matrix;
return $inverseMatrixWithIdentity;
}
private function oneOperation(array $matrix, $rowPosition, $zeroPosition)
{
if($matrix[$rowPosition][$zeroPosition] !== 1)
{
$numberOfCols = count($matrix[$rowPosition]);
if($matrix[$rowPosition][$zeroPosition] === 0)
{
$divisor = 0.0000000001;
$matrix[$rowPosition][$zeroPosition] = 0.0000000001;
}
else
{
$divisor = $matrix[$rowPosition][$zeroPosition];
}
for($i=0; $i<$numberOfCols; $i++)
{
$matrix[$rowPosition][$i] = $matrix[$rowPosition][$i] / $divisor;
}
}
return $matrix;
}
private function zeroOperation(array $matrix, $rowPosition, $zeroPosition, $subjectRow)
{
$numberOfCols = count($matrix[$rowPosition]);
if($matrix[$rowPosition][$zeroPosition] !== 0)
{
$numberToSubtract = $matrix[$rowPosition][$zeroPosition];
for($i=0; $i<$numberOfCols; $i++)
{
$matrix[$rowPosition][$i] = $matrix[$rowPosition][$i] - $numberToSubtract * $matrix[$subjectRow][$i];
}
}
return $matrix;
}
private function removeIdentityMatrix(array $matrix)
{
$inverseMatrix = array();
$matrixCount = count($matrix);
for($i=0; $i<$matrixCount; $i++)
{
$inverseMatrix[$i] = array_slice($matrix[$i], $matrixCount);
}
return $inverseMatrix;
}
private function appendIdentityMatrixToMatrix(array $matrix, array $identityMatrix)
{
//TODO $matrix & $identityMatrix compliance validation (same number of rows/columns, etc)
$augmentedMatrix = array();
for($i=0; $i<count($matrix); $i++)
{
$augmentedMatrix[$i] = array_merge($matrix[$i], $identityMatrix[$i]);
}
return $augmentedMatrix;
}
public function identityMatrix(int $size)
{
//TODO validate $size
$identityMatrix = array();
for($i=0; $i<$size; $i++)
{
for($j=0; $j<$size; $j++)
{
if($i == $j)
{
$identityMatrix[$i][$j] = 1;
}
else
{
$identityMatrix[$i][$j] = 0;
}
}
}
return $identityMatrix;
}
}
$matrix = array(
array(11, 3, 12),
array(8, 7, 10),
array(13, 14, 15),
);
$matrixLibrary = new MatrixLibrary();
$inverseMatrix = $matrixLibrary->inverseMatrix($matrix);
print_r($inverseMatrix);
/*
Array
(
[0] => Array
(
[0] => 0.33980582524272
[1] => -1.1941747572816
[2] => 0.52427184466019
)
[1] => Array
(
[0] => -0.097087378640777
[1] => -0.087378640776699
[2] => 0.13592233009709
)
[2] => Array
(
[0] => -0.20388349514563
[1] => 1.1165048543689
[2] => -0.51456310679612
)
)
*/
/**
* matrix_inverse
*
* Matrix Inverse
* Guass-Jordan Elimination Method
* Reduced Row Eshelon Form (RREF)
*
* In linear algebra an n-by-n (square) matrix A is called invertible (some
* authors use nonsingular or nondegenerate) if there exists an n-by-n matrix B
* such that AB = BA = In where In denotes the n-by-n identity matrix and the
* multiplication used is ordinary matrix multiplication. If this is the case,
* then the matrix B is uniquely determined by A and is called the inverse of A,
* denoted by A-1. It follows from the theory of matrices that if for finite
* square matrices A and B, then also non-square matrices (m-by-n matrices for
* which m ? n) do not have an inverse. However, in some cases such a matrix may
* have a left inverse or right inverse. If A is m-by-n and the rank of A is
* equal to n, then A has a left inverse: an n-by-m matrix B such that BA = I.
* If A has rank m, then it has a right inverse: an n-by-m matrix B such that
* AB = I.
*
* A square matrix that is not invertible is called singular or degenerate. A
* square matrix is singular if and only if its determinant is 0. Singular
* matrices are rare in the sense that if you pick a random square matrix over
* a continuous uniform distribution on its entries, it will almost surely not
* be singular.
*
* While the most common case is that of matrices over the real or complex
* numbers, all these definitions can be given for matrices over any commutative
* ring. However, in this case the condition for a square matrix to be
* invertible is that its determinant is invertible in the ring, which in
* general is a much stricter requirement than being nonzero. The conditions for
* existence of left-inverse resp. right-inverse are more complicated since a
* notion of rank does not exist over rings.
*/
public function matrix_inverse($m1)
{
$rows = $this->rows($m1);
$cols = $this->columns($m1);
if ($rows != $cols)
{
die("Matrim1 is not square. Can not be inverted.");
}
$m2 = $this->eye($rows);
for ($j = 0; $j < $cols; $j++)
{
$factor = $m1[$j][$j];
if ($this->debug)
{
fms_writeln('Divide Row [' . $j . '] by ' . $m1[$j][$j] . ' (to
give us a "1" in the desired position):');
}
$m1 = $this->rref_div($m1, $j, $factor);
$m2 = $this->rref_div($m2, $j, $factor);
if ($this->debug)
{
$this->disp2($m1, $m2);
}
for ($i = 0; $i < $rows; $i++)
{
if ($i != $j)
{
$factor = $m1[$i][$j];
if ($this->debug)
{
$this->writeln('Row[' . $i . '] - ' . number_format($factor, 4) . ' ×
Row[' . $j . '] (to give us 0 in the desired position):');
}
$m1 = $this->rref_sub($m1, $i, $factor, $j);
$m2 = $this->rref_sub($m2, $i, $factor, $j);
if ($this->debug)
{
$this->disp2($m1, $m2);
}
}
}
}
return $m2;
}

Categories