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
Related
I want a hint on a efficient algorithm of solving the following:
(Okay, The question was not that, I just made this for simplicity).
Example: Out of N Advert boards there should be M advert of a company in K consecutive boards. and the Adverts will be used such a way that only MINIMUM number of Adverts will be used
if N=3,K=2,M=1. The answer will be one 010;
if N=6, K=3, M=2 the answer will be 6.
011011,
011101,
011110,
101101,
101110,
110110.
I took the approach of Creating all the combinations(with binary approach) with iterative and recursive method.After that I filtered it. It is working well except if N is big it will crash.(as expected). So Is there any efficient method of solving these?
I just took another approach but it is not going to well .. :(
Second approach
if($n%$k == 0){
$perm = $m*($n/$k);
}else{
$perm = $m*ceil($n/$k);
}
Then I will do the nCr... and now I'm lost
First Approach
<?php
$n = $input1;$k = $input2;$l=$input3; $total = 0;
$flag = false;$arrays = array();$z=0;$min=$n;$x=0;
$total_permutations = pow(2,$n);
for($i=0;$i<$total_permutations;$i++){
$binary = base_convert($i, 10, 2);
$binary = str_pad($binary, $n,"0", STR_PAD_LEFT);
for($j=0;$j<=($n-$k);$j++){
$flag = false;$x=0;
for($m=0;$m<$k;$m++){
$x += intval($binary{$j+$m});
}
if($x<$l){
$flag = true;
break;
}
}
if(!$flag){
$arrays[$z]=str_split($binary);
$arrays[$z] = array_map('intval', $arrays[$z]);
$z++;
echo $binary."<br />";
}
unset($binary);
}
$min = min(array_map('array_sum',$arrays));
echo "------------<br />";
foreach($arrays as $val){
$sum = array_sum($val);
if($sum == $min){
echo implode("",$val);echo "<br>";
$total++;
}
}
return $total;
}
?>
To elaborate on my comment, here is one potential solution (Though, I would be very disappointed if there wasn't a better one)
1) First, calculate the minimum number of 1's needed in a valid solution. (I believe this is simply max(M, floor(n/k)*m + max(0, n%k-(k-m))). The formula is derived from building a string one letter at a time, placing 0's whenever possible)
2) Now, assume N > K (otherwise the answer is trivial).
We define the sub-problem to be: "Given a prefix P of length K, and a budget B of 1's to place, how many ways are there to fill out N characters whilst still enforcing the M rule?"
For example, consider that our string is "101XXXXXX", with K = 3, M = 2, and B = 4. Here, N = 6, P = "101". We solve this sub-problem like so:
a) Check if the budget is big enough in O(N) time. Return 0 if it isn't
If N=0, return 1 trivially
b) Set total possible solutions to 0
c) Set the first unknown character to 1. Compute the new prefix P'
(by stripping off the first character, and adding a "1" to the end),
the new B' = B-1, the new N' = N-1, and solve the new sub-problem:
Add its result to our total
d) Set the unknown character to 0 instead: But only if the new prefix P'
(strip, add "0") has at least M 1's. If so, set B' = B-1, N' = N-1,
and solve the new sub-problem. Add its result to our total
e) Return the total
To solve the original problem, simply consider all possible prefixes P (all nCr(K,M) of them), and sum up the solutions to the derived sub-problems. By caching the results to sub-problems based on unique inputs P, N, and B, we reduce the amount of duplicate work drastically. Amortized analysis should show that the full solution runs in O(N*nCr(K,M)) time
I have a system of equations of grade 1 to resolve in PHP.
There are more equations than variables but there aren't less equations than variables.
The system would look like bellow. n equations, m variables, variables are x[i] where 'i' takes values from 1 to m. The system may have a solution or not.
m may be maximum 100 and n maximum ~5000 (thousands).
I will have to resolve like a few thousands of these systems of equations. Speed may be a problem but I'm looking for an algorithm written in PHP for now.
a[1][1] * x[1] + a[1][2] * x[2] + ... + a[1][m] * x[m] = number 1
a[2][1] * x[1] + a[2][2] * x[2] + ... + a[2][m] * x[m] = number 2
...
a[n][1] * x[1] + a[n][2] * x[2] + ... + a[n][m] * x[m] = number n
There is Cramer Rule which may do it. I could make 1 square matrix of coefficients, resolve the system with Cramer Rule (by calculating matrices' determinants) and than I should check the values in the unused equations.
I believe I could try Cramer by myself but I'm looking for a better solution.
This is a problem of Computational Science,
http://en.wikipedia.org/wiki/Computational_science#Numerical_simulations
I know there are some complex algorithms to solve my problem but I can't tell which one would do it and which is the best for my case. An algorithm would use me better than just the theory with the demonstration.
My question is, does anybody know a class, script, code of some sort written in PHP to resolve a system of linear equations of grade 1 ?
Alternatively I could try an API or a Web Service, best to be free, a paid one would do it too.
Thank you
I needed exactly this, but I couldn't find determinant function, so I made one myself. And the Cramer rule function too. Maybe it'll help someone.
/**
* $matrix must be 2-dimensional n x n array in following format
* $matrix = array(array(1,2,3),array(1,2,3),array(1,2,3))
*/
function determinant($matrix = array()) {
// dimension control - n x n
foreach ($matrix as $row) {
if (sizeof($matrix) != sizeof($row)) {
return false;
}
}
// count 1x1 and 2x2 manually - rest by recursive function
$dimension = sizeof($matrix);
if ($dimension == 1) {
return $matrix[0][0];
}
if ($dimension == 2) {
return ($matrix[0][0] * $matrix[1][1] - $matrix[0][1] * $matrix[1][0]);
}
// cycles for submatrixes calculations
$sum = 0;
for ($i = 0; $i < $dimension; $i++) {
// for each "$i", you will create a smaller matrix based on the original matrix
// by removing the first row and the "i"th column.
$smallMatrix = array();
for ($j = 0; $j < $dimension - 1; $j++) {
$smallMatrix[$j] = array();
for ($k = 0; $k < $dimension; $k++) {
if ($k < $i) $smallMatrix[$j][$k] = $matrix[$j + 1][$k];
if ($k > $i) $smallMatrix[$j][$k - 1] = $matrix[$j + 1][$k];
}
}
// after creating the smaller matrix, multiply the "i"th element in the first
// row by the determinant of the smaller matrix.
// odd position is plus, even is minus - the index from 0 so it's oppositely
if ($i % 2 == 0){
$sum += $matrix[0][$i] * determinant($smallMatrix);
} else {
$sum -= $matrix[0][$i] * determinant($smallMatrix);
}
}
return $sum;
}
/**
* left side of equations - parameters:
* $leftMatrix must be 2-dimensional n x n array in following format
* $leftMatrix = array(array(1,2,3),array(1,2,3),array(1,2,3))
* right side of equations - results:
* $rightMatrix must be in format
* $rightMatrix = array(1,2,3);
*/
function equationSystem($leftMatrix = array(), $rightMatrix = array()) {
// matrixes and dimension check
if (!is_array($leftMatrix) || !is_array($rightMatrix)) {
return false;
}
if (sizeof($leftMatrix) != sizeof($rightMatrix)) {
return false;
}
$M = determinant($leftMatrix);
if (!$M) {
return false;
}
$x = array();
foreach ($rightMatrix as $rk => $rv) {
$xMatrix = $leftMatrix;
foreach ($rightMatrix as $rMk => $rMv) {
$xMatrix[$rMk][$rk] = $rMv;
}
$x[$rk] = determinant($xMatrix) / $M;
}
return $x;
}
Wikipedia should have pseudocode for reducing the matrix representing your equations to reduced row echelon form. Once the matrix is in that form, you can walk through the rows to find a solution.
There's an unmaintained PEAR package which may save you the effort of writing the code.
Another question is whether you are looking mostly at "wide" systems (more variables than equations, which usually have many possible solutions) or "narrow" systems (more equations than variables, which usually have no solutions), since the best strategy depends on which case you are in — and narrow systems may benefit from using a linear regression technique such as least squares instead.
This package uses Gaussian Elimination. I found that it executes fast for larger matrices (i.e. more variables/equations).
There is a truly excellent package based on JAMA here: http://www.phpmath.com/build02/JAMA/docs/index.php
I've used it for simple linear right the way to highly complex Multiple Linear Regression (writing my own Backwards Stepwise MLR functions on top of that). Very comprehensive and will hopefully do what you need.
Speed could be considered an issue, for sure. But works a treat and matched SPSS when I cross referenced results on the BSMLR calculations.
In a browser game we have items that occur based on their probabilities.
P(i1) = 0.8
P(i2) = 0.45
P(i3) = 0.33
P(i4) = 0.01
How do we implement a function in php that returns a random item based on its probability chance?
edit
The items have a property called rarity which varies from 1 to 100 and represents the probability to occcur. The item that occurs is chosen from a set of all items of a certain type. (e.x the given example above represents all artifacts tier 1)
I don't know if its the best solution but when I had to solve this a while back this is what I found:
Function taken from this blog post:
// Given an array of values, and weights for those values (any positive int)
// it will select a value randomly as often as the given weight allows.
// for example:
// values(A, B, C, D)
// weights(30, 50, 100, 25)
// Given these values C should come out twice as often as B, and 4 times as often as D.
function weighted_random($values, $weights){
$count = count($values);
$i = 0;
$n = 0;
$num = mt_rand(0, array_sum($weights));
while($i < $count){
$n += $weights[$i];
if($n >= $num){
break;
}
$i++;
}
return $values[$i];
}
Example call:
$values = array('A','B','C');
$weights = array(1,50,100);
$weighted_value = weighted_random($values, $weights);
It's somewhat unwieldy as obviously the values and weights need to be supplied separately but this could probably be refactored to suit your needs.
Tried to understand how Bulk's function works, and here is how I understand based on Benjamin Kloster answer:
https://softwareengineering.stackexchange.com/questions/150616/return-random-list-item-by-its-weight
Generate a random number n in the range of 0 to sum(weights), in this case $num so lets say from this: weights(30, 50, 100, 25).
Sum is 205.
Now $num has to be 0-30 to get A,
30-80 to get B
80-180 to get C
and 180-205 to get D
While loop finds in which interval the $num falls.
Not a duplicate of-
optimal algorithm for finding unique divisors
I came across this problem. I am not able to find an optimal algorithm.
The problem is :
Given a list L of natural numbers(number can be really large) and a number N, what's the optimal algorithm to determine the number of divisors of N which doesn't not divide any of the numbers present in the list L. Numbers in the list can be repetitive ie, one number can occur more than once.
Observation:
Divisors of some divisor d of N are also divisors of N.
MY approach was :
Find the divisors of N.
Sort L in reverse order(largest element being 1st element).
foreach divisor d of N, I check whether it divides any element in the list or not.(stop when you come to check for an element less than d in the list, as the list is sorted)
If d divides some number in the list L, then I don't check for any divisor of d, that is, I skip this checking.
Ultimately, left divisors which were neither divided any number in the list nor skipped are counted. This count is the final answer.
But this algorithm is not optimal for this problem.
Any ideas for a better algorithm?
What you need to look into is : co-primes (or relatively primes)
In number theory, a branch of mathematics, two integers a and b are
said to be coprime (also spelled co-prime) or relatively prime if the
only positive integer that evenly divides both of them is 1.
So to "transcode" your problem :
You basically want to find the Number of coprimes of N from the L list.
When a and b are co-primes?
If two numbers are relatively prime then their greatest common divisor (GCD)
is 1
Example code (for GCD) in PHP :
<?php
$gcd = gmp_gcd("12", "21");
echo gmp_strval($gcd) . "\n";
?>
Simply put :
$count = 0
Foreach element e in list L : calculate the GCD(e,N)
Is their GCD=1? If yes, they are coprime (so N and e have no common divisors). Count it up. $count++
And that's all there is to it.
First, factorize n and represent it in the following way: p1:k1, p2:k2,..., pm:km such that p1,p2,... are all primes and n=p1^k1 * p2^k2 ....
Now, iterate over r1, r2, r3,..., rm such that r1<=k1, r2<=k2, ..., rm<=km and check if p1^r1*p2^r2...*pm^rm divides any number in L. If not increment count by 1.
Optimization: Pick a value for r1. See if p1^r1 divides any number in L. If yes, then pick a number for r2 and so on. If p1^r1 does not divide any number in L, then increment count by (k2+1)(k3+1)..*(km+1).
Example N=72, L=[4, 5, 9, 12, 15, 20]:
Writing N as a primal product: 2:3, 3:2 (2^3*3*2 = 72).
p1=2, p2=3, k1=3, k2=2
count=0
r1=0:
r2=0:
Divides 4
r1=0:
r2=1:
Divides 9
r1=0:
r2=2:
Divides 9
r1=1:
r2=0:
Divides 4
r1=1:
r2=1:
Divides 12
r1=1:
r2=2:
L not divisible by 18. Count+=1 = 1
r1=2:
r2=0:
Divides 4
r1=2:
r2=1:
Divides 12
r1=2:
r2=2:
L not divisible by 36. Count+=1 = 2
r1=3:
r2=0:
L not divisible by 8. Count+=(k2+1) +=(2+1) = 5
<?php
class Divisors {
public $factor = array();
public function __construct($num) {
$this->num = $num;
}
// count number of divisors of a number
public function countDivisors() {
if ($this->num == 1) return 1;
$this->_primefactors();
$array_primes = array_count_values($this->factor);
$divisors = 1;
foreach($array_primes as $power) {
$divisors *= ++$power;
}
return $divisors;
}
// prime factors decomposer
private function _primefactors() {
$this->factor = array();
$run = true;
while($run && #$this->factor[0] != $this->num) {
$run = $this->_getFactors();
}
}
// get all factors of the number
private function _getFactors() {
if($this->num == 1) {
return ;
}
$root = ceil(sqrt($this->num)) + 1;
$i = 2;
while($i <= $root) {
if($this->num % $i == 0) {
$this->factor[] = $i;
$this->num = $this->num / $i;
return true;
}
$i++;
}
$this->factor[] = $this->num;
return false;
}
} // our class ends here
$example = new Divisors(4567893421);
print $example->countDivisors();
?>
So I've got a list of weighted items, and I'd like to pick 4 non-duplicate items from this list.
Item Weight
Apple 5
Banana 7
Cherry 12
...
Orange 8
Pineapple 50
What is the most efficient way to do this? My initial foray was to just reroll for the subsequent picks if an already picked item came up... but for a small list this can result in a ton of rerolls.
Edit for clarification:
For the above example, and ignoring fruits D through N, there is a total weight of 82. So the chances of being picked first are:
A ~6%
B ~8.5%
C ~14.6%
O ~9.8%
P ~61%
Once an items is picked the probabilities would (should!) change.
In your comment you say that unique means:
I don't want to pick the same item twice.
.. and that the weights determine a likelihood of being picked.
All you need to do to make sure that you don't pick duplicates, is simply remove the last picked item from the list before you pick the next one. Yes, this will change your weights slightly, but that is the correct statistical change to make if you do want unique results.
In addition, I'm not sure how you are using the weights to determine the candidates, but I came up with this algorithm that should do this with a minimal number of loops (and without the need to fill an array according to weights, which could result in extremely large arrays, requires int weights, etc.)
I've used JavaScript here, simply so it's easy to see the output in a browser without a server. It should be trivial to port to PHP since it's not doing anything complicated.
Constants
var FRUITS = [
{name : "Apple", weight: 8 },
{name : "Orange", weight: 4 },
{name : "Banana", weight: 4 },
{name : "Nectarine", weight: 3 },
{name : "Kiwi", weight: 1 }
];
var PICKS = 3;
function getNewFruitsAvailable(fruits, removeFruit) {
var newFruits = [];
for (var idx in fruits) {
if (fruits[idx].name != removeFruit) {
newFruits.push(fruits[idx]);
}
}
return newFruits;
}
Script
var results = [];
var candidateFruits = FRUITS;
for (var i=0; i < PICKS; i++) {
// CALCULATE TOTAL WEIGHT OF AVAILABLE FRUITS
var totalweight = 0;
for (var idx in candidateFruits) {
totalweight += candidateFruits[idx].weight;
}
console.log("Total weight: " + totalweight);
var rand = Math.random();
console.log("Random: " + rand);
// ITERATE THROUGH FRUITS AND PICK THE ONE THAT MATCHES THE RANDOM
var weightinc = 0;
for (idx in candidateFruits) {
// INCREMENT THE WEIGHT BY THE NEXT FRUIT'S WEIGHT
var candidate = candidateFruits[idx];
weightinc += candidate.weight;
// IF rand IS BETWEEN LAST WEIGHT AND NEXT WEIGHT, PICK THIS FRUIT
if (rand < weightinc/totalweight) {
results.push(candidate.name);
console.log("Pick: " + candidate.name);
// GET NEXT SET OF FRUITS (REMOVING PICKED FRUIT)
candidateFruits = getNewFruitsAvailable(candidateFruits, candidate.name);
break;
}
}
console.log("CandidateFruits: " + candidateFruits.length);
};
Output
for (var i=0; i < results.length; i++) {
document.write(results[i] + "<br/>");
}
The basic strategy is to allocate each fruit a portion of the total range [0,1). In the first loop, you'd have this:
Apple — 8/20 = 0.0 up to 0.4
Orange — 4/20 = 0.4 up to 0.6
Banana — 4/20 = 0.6 up to 0.8
Nectarine — 3/20 = 0.8 up to 0.95
Kiwi — 8/20 = 0.95 up to 1.0
The script iterates over each item in the list, and progresses a weight counter. When it reaches the range that contains the first random, it picks that item, removes it from the list, then recalculates the ranges based on the new total weight and runs again.
Here I found the idea to following steps:
Build the sum of the weights --> SUM
Build a random number between 0 and SUM --> RAND_NUMBER
Iterate through the list and subtract each element weight from RAND_NUMBER. If RAND_NUMBER gets negative, you have your first element.
Remove the found element from the list and go back to step 1 until you have 4 elements.
Update
function array_rand2($ary,$n = 1)
{
// make sure we don't get in to an infinite loop
// check we have enough options to select from
$unique = count(array_unique(array_keys($ary)));
if ($n > $unique) $n = count($unique);
// First, explode the array and expand out all the weights
// this means something with a weight of 5 will appear in
// in the array 5 times
$_ary = array();
foreach ($ary as $item => $weight)
{
$_ary = array_merge($_ary, array_fill(0, $weight, $item));
}
// now look for $n unique entries
$matches = array();
while (count($matches) < $n)
{
$r = $_ary[array_rand($_ary)];
if (!in_array($r,$matches))
{
$matches[] = $r;
}
}
// and now grab those $n entries and return them
$result = array();
foreach ($matches as $match){
$result[] = $match;
}
return $result;
}
See if that does a better job.
Maybe instead of "rerolls" you could just increment the list element index you've randomly generated: list.elementAt(rand_index++ % size(list)) (something like that). I'd think you'd find the next random unique item pretty fast with logic like that.
I'm sure there are even better solutions, of course, there usually are.
Edit: Looks like Brad's already provided one.. :)