I have numbers, from 0 to 8. I would like in result, all possible sets of those numbers, each set should use all numbers, each number can occur only once in a set.
I would like to see solution made in PHP that could print out result. Or, at least, I would like some refreshment in theory of combinatorics, as I have long forgotten it. What is the formula to calculate how many permutations will there be?
Example sets:
0-1-2-3-4-5-6-7-8
0-1-2-3-4-5-6-8-7
0-1-2-3-4-5-8-6-7
0-1-2-3-4-8-5-6-7
0-1-2-3-8-4-5-6-7
0-1-2-8-3-4-5-6-7
and so on...
You're looking for the permutations formula:
nPk = n!/(n-k)!
In your case, you have 9 entries and you want to choose all of them, that's 9P9 = 9! = 362880
You can find a PHP algorithm to permutate in recipe 4.26 of O'Reilly's "PHP Cookbook".
pc_permute(array(0, 1, 2, 3, 4, 5, 7, 8));
Copied in from O'Reilly:
function pc_permute($items, $perms = array( )) {
if (empty($items)) {
print join(' ', $perms) . "\n";
} else {
for ($i = count($items) - 1; $i >= 0; --$i) {
$newitems = $items;
$newperms = $perms;
list($foo) = array_splice($newitems, $i, 1);
array_unshift($newperms, $foo);
pc_permute($newitems, $newperms);
}
}
}
Since PHP 5.5 you can use Generators. Generators save a lot of memory and are way faster (more than half compared to pc_permute()). So if you have any chance of having PHP 5.5 installed, you definitely want Generators.
This snipped is ported from Python: https://stackoverflow.com/a/104436/3745311
function permutations(array $elements)
{
if (count($elements) <= 1) {
yield $elements;
} else {
foreach (permutations(array_slice($elements, 1)) as $permutation) {
foreach (range(0, count($elements) - 1) as $i) {
yield array_merge(
array_slice($permutation, 0, $i),
[$elements[0]],
array_slice($permutation, $i)
);
}
}
}
}
Sample usage:
$list = ['a', 'b', 'c'];
foreach (permutations($list) as $permutation) {
echo implode(',', $permutation) . PHP_EOL;
}
Output:
a,b,c
b,a,c
b,c,a
a,c,b
c,a,b
c,b,a
Since this question often comes up in Google Search results, here's a modified version of the accepted answer that returns all combinations in an array and passes them as a return value of the function.
function pc_permute($items, $perms = array( )) {
if (empty($items)) {
$return = array($perms);
} else {
$return = array();
for ($i = count($items) - 1; $i >= 0; --$i) {
$newitems = $items;
$newperms = $perms;
list($foo) = array_splice($newitems, $i, 1);
array_unshift($newperms, $foo);
$return = array_merge($return, pc_permute($newitems, $newperms));
}
}
return $return;
}
To use:
$value = array('1', '2', '3');
print_r(pc_permute($value));
I've something that You may like
function combination_number($k,$n){
$n = intval($n);
$k = intval($k);
if ($k > $n){
return 0;
} elseif ($n == $k) {
return 1;
} else {
if ($k >= $n - $k){
$l = $k+1;
for ($i = $l+1 ; $i <= $n ; $i++)
$l *= $i;
$m = 1;
for ($i = 2 ; $i <= $n-$k ; $i++)
$m *= $i;
} else {
$l = ($n-$k) + 1;
for ($i = $l+1 ; $i <= $n ; $i++)
$l *= $i;
$m = 1;
for ($i = 2 ; $i <= $k ; $i++)
$m *= $i;
}
}
return $l/$m;
}
function array_combination($le, $set){
$lk = combination_number($le, count($set));
$ret = array_fill(0, $lk, array_fill(0, $le, '') );
$temp = array();
for ($i = 0 ; $i < $le ; $i++)
$temp[$i] = $i;
$ret[0] = $temp;
for ($i = 1 ; $i < $lk ; $i++){
if ($temp[$le-1] != count($set)-1){
$temp[$le-1]++;
} else {
$od = -1;
for ($j = $le-2 ; $j >= 0 ; $j--)
if ($temp[$j]+1 != $temp[$j+1]){
$od = $j;
break;
}
if ($od == -1)
break;
$temp[$od]++;
for ($j = $od+1 ; $j < $le ; $j++)
$temp[$j] = $temp[$od]+$j-$od;
}
$ret[$i] = $temp;
}
for ($i = 0 ; $i < $lk ; $i++)
for ($j = 0 ; $j < $le ; $j++)
$ret[$i][$j] = $set[$ret[$i][$j]];
return $ret;
}
Here is how to use it:
To get the number of combinations:
combination_number(3,10); // returns number of combinations of ten-elements set.
To get all possible combinations:
$mySet = array("A","B","C","D","E","F");
array_combination(3, $mySet); // returns all possible combinations of 3 elements of six-elements set.
Hope You make use of that.
I've ported the Python itertools code listed here (using generators). The advantage over the solutions posted so far is that it allows you to specify r (permutation size).
function permutations($pool, $r = null) {
$n = count($pool);
if ($r == null) {
$r = $n;
}
if ($r > $n) {
return;
}
$indices = range(0, $n - 1);
$cycles = range($n, $n - $r + 1, -1); // count down
yield array_slice($pool, 0, $r);
if ($n <= 0) {
return;
}
while (true) {
$exit_early = false;
for ($i = $r;$i--;$i >= 0) {
$cycles[$i]-= 1;
if ($cycles[$i] == 0) {
// Push whatever is at index $i to the end, move everything back
if ($i < count($indices)) {
$removed = array_splice($indices, $i, 1);
array_push($indices, $removed[0]);
}
$cycles[$i] = $n - $i;
} else {
$j = $cycles[$i];
// Swap indices $i & -$j.
$i_val = $indices[$i];
$neg_j_val = $indices[count($indices) - $j];
$indices[$i] = $neg_j_val;
$indices[count($indices) - $j] = $i_val;
$result = [];
$counter = 0;
foreach ($indices as $indx) {
array_push($result, $pool[$indx]);
$counter++;
if ($counter == $r) break;
}
yield $result;
$exit_early = true;
break;
}
}
if (!$exit_early) {
break; // Outer while loop
}
}
}
It works for me, but no promises!
Example usage:
$result = iterator_to_array(permutations([1, 2, 3, 4], 3));
foreach ($result as $row) {
print implode(", ", $row) . "\n";
}
This is my version of class. This class builds and returns permutated array as result
class Permutation {
private $result;
public function getResult() {
return $this->result;
}
public function permute($source, $permutated=array()) {
if (empty($permutated)){
$this->result = array();
}
if (empty($source)){
$this->result[] = $permutated;
} else {
for($i=0; $i<count($source); $i++){
$new_permutated = $permutated;
$new_permutated[] = $source[$i];
$new_source = array_merge(array_slice($source,0,$i),array_slice($source,$i+1));
$this->permute($new_source, $new_permutated);
}
}
return $this;
}
}
$arr = array(1,2,3,4,5);
$p = new Permutation();
print_r($p->permute($arr)->getResult());
The last three lines to test my class.
This is a simple recursive function that prints all permutations (written in pseudocode)
function rec(n, k) {
if (k == n) {
for i = 0 to n-1
print(perm[i], ' ');
print('\n');
}
else {
for i = 0 to n-1 {
if (not used[i]) {
used[i] = true;
perm[k] = i;
rec(n, k+1);
used[i] = false;
}
}
}
}
And it is called like this:
rec(9, 0);
Lexicographical order. There is no recursion. Almost no limits for array length.
There is no sort. It's running rather fast. It's easy to understand.
Minus: it gives a notice, but you can add a condition to start compare with the second element or error_reporting(0).
$a = array(
1,
2,
3,
4,
5
);
$b = array_reverse($a);
print_r($a);
//here need "br"
while ($a != $b)
{
foreach(array_reverse($a, true) as $k => $v)
{
if ($v < $a[$k + 1])
{
foreach(array_reverse($a, true) as $ka => $val)
{
if ($val > $v) break;
}
$ch = $a[$k];
$a[$k] = $a[$ka];
$a[$ka] = $ch;
$c = array_slice($a, 0, $k + 1);
print_r($a = array_merge($c, array_reverse(array_slice($a, $k + 1))));
//here need "br"
break;
}
}
}
You're basically talking about permutations where both n and k are 9 so you'll have 9! different permutations; see this: http://en.wikipedia.org/wiki/Permutation.
Here is my proposal, hope a little bit clearer than accepted answer.
function permutate($elements, $perm = array(), &$permArray = array())
{
if(empty($elements))
{
array_push($permArray,$perm); return;
}
for($i=0;$i<=count($elements)-1;$i++)
{
array_push($perm,$elements[$i]);
$tmp = $elements; array_splice($tmp,$i,1);
permutate($tmp,$perm,$permArray);
array_pop($perm);
}
return $permArray;
}
and usage:
$p = permutate(array('a','b','c'));
foreach($p as $perm)
print join(",",$perm)."|\n";
//function call
print_r(combinations([1,2,3,4,5,6,7,8,9,10,11,12,13]));
/**
* #param $mainArray
* #param int $size - optional
* #param array $combinations - optional
* #return mixed
*/
function combinations($mainArray, $size = 3, $combinations = [])
{
if (empty($combinations)) {
$combinations = $mainArray;
}
if ($size == 1) {
return str_replace('-','',$combinations);;
}
$newCombination = array();
foreach ($mainArray as $key => $val){
foreach ($combinations as $char) {
if(in_array($val, explode('-', $char))){
continue;
}
$newCombination[] = $val . '-' . $char;
}
}
return combinations($mainArray, $size - 1, $newCombination);
}
//========================= Next solution ==================================
function sampling($chars, $size, $combinations = array()) {
# if it's the first iteration, the first set
# of combinations is the same as the set of characters
if (empty($combinations)) {
$combinations = $chars;
}
# we're done if we're at size 1
if ($size == 1) {
return $combinations;
}
# initialise array to put new values in
$new_combinations = array();
# loop through existing combinations and character set to create strings
foreach ($combinations as $combination) {
foreach ($chars as $char) {
$new_combinations[] = $combination .'-'. $char ;
}
}
# call same function again for the next iteration
return $this->sampling($chars, $size - 1, $new_combinations);
}
function array_has_dupes($array) {
return count($array) !== count(array_unique($array));
}
function total() {
// Generate ticket price
$arrfinal = array();
// combinations
$chars = array(1,2,3,4,5,6,7,8,9,10,11,12,13); // for 10 digits
$combinations = $this->sampling($chars, 3);
//print_r($combinations); //exit;
foreach($combinations as $key => $val)
{
$arr = explode('-', $val);//str_split($val);
if(!$this->array_has_dupes($arr)){
$arrfinal[] = str_replace('-', '', $val);
}
}
echo '<pre>'; print_r($arrfinal); echo '</pre>';
}
Simple solution using recursion
function filterElement($element){
if(is_array($element[0])){
return $element[0];
}
# base case
return $element;
}
function permutation($input, $path){
// base case 1
if(count($input) == 0){
return [$path];
}
$output = [];
foreach($input as $index => $num){ # 1, 2,3, 4
$copyPath = $path; # copy the path - []
$copyPath[] = $num; # append the number [1]
# remove the current number
$inputLocal = $input;
unset($inputLocal[$index]); # [2, 3, 4]
$permute = permutation($inputLocal, $copyPath); # call [2, 3, 4], [1]
# for all element find add to output
foreach($permute as $ele){
# filter ouput
$output[] = filterElement($ele);
}
}
return $output;
}
print_r(permutation([1,2,3,4], []));
output
Array
(
[0] => Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
[1] => Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 3
)
[2] => Array
(
[0] => 1
[1] => 3
[2] => 2
[3] => 4
)
[3] => Array
(
[0] => 1
[1] => 3
[2] => 4
[3] => 2
)
[4] => Array
(
[0] => 1
[1] => 4
[2] => 2
[3] => 3
)
[5] => Array
(
[0] => 1
[1] => 4
[2] => 3
[3] => 2
)
[6] => Array
(
[0] => 2
[1] => 1
[2] => 3
[3] => 4
)
[7] => Array
(
[0] => 2
[1] => 1
[2] => 4
[3] => 3
)
[8] => Array
(
[0] => 2
[1] => 3
[2] => 1
[3] => 4
)
[9] => Array
(
[0] => 2
[1] => 3
[2] => 4
[3] => 1
)
[10] => Array
(
[0] => 2
[1] => 4
[2] => 1
[3] => 3
)
[11] => Array
(
[0] => 2
[1] => 4
[2] => 3
[3] => 1
)
[12] => Array
(
[0] => 3
[1] => 1
[2] => 2
[3] => 4
)
[13] => Array
(
[0] => 3
[1] => 1
[2] => 4
[3] => 2
)
[14] => Array
(
[0] => 3
[1] => 2
[2] => 1
[3] => 4
)
[15] => Array
(
[0] => 3
[1] => 2
[2] => 4
[3] => 1
)
[16] => Array
(
[0] => 3
[1] => 4
[2] => 1
[3] => 2
)
[17] => Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
)
[18] => Array
(
[0] => 4
[1] => 1
[2] => 2
[3] => 3
)
[19] => Array
(
[0] => 4
[1] => 1
[2] => 3
[3] => 2
)
[20] => Array
(
[0] => 4
[1] => 2
[2] => 1
[3] => 3
)
[21] => Array
(
[0] => 4
[1] => 2
[2] => 3
[3] => 1
)
[22] => Array
(
[0] => 4
[1] => 3
[2] => 1
[3] => 2
)
[23] => Array
(
[0] => 4
[1] => 3
[2] => 2
[3] => 1
)
)
Related
What is the best way to split the following array to different combinations according to number of values?
[0] => 1
[1] => 5
[2] => 4
[3] => 1
[4] => Array
(
[0] => 1
[1] => 9
)
To
[0] => 1
[1] => 5
[2] => 4
[3] => 1
[4] => 1
AND
[0] => 1
[1] => 5
[2] => 4
[3] => 1
[4] => 9
function getCombinations($arr){
$ret = array();
$subs = array();
// extract sub arrays
$n = count($arr);
for($i=0;$i<$n;$i++){
if(is_array($arr[$i])){
$subs[] = array('target'=>$i, 'values'=>$arr[$i], 'size'=>count($arr[$i]), 'counter'=>0);
$arr[$i] = null;
}
}
$subs_n = count($subs);
if($subs_n==0) // no subarrays found, return input
return $arr;
$finished = false;
while(!$finished){
$combination = $arr; // this will copy the existing array
for($i=0;$i<$subs_n;$i++){
$combination[$subs[$i]['target']] = $subs[$i]['values'][$subs[$i]['counter']]; // write current combination into designated index
}
$ret[] = $combination; // add combination to output
$carry = true;
$i = 0;
while($carry){
$subs[$i]['counter']++; //next combination
if($subs[$i]['counter']>=$subs[$i]['size']){ // if end of combination, increment next level
$carry = true;
$subs[$i]['counter'] = 0;
}else{
$carry = false;
}
$i++;
if($i>=$subs_n && $carry){ // if last combination reached, all done
$finished = true;
$carry = false;
}
}
}
return $ret;
}
I'd go with something like this:
<?php
$arr = [
0 => 1,
1 => 5,
2 => 4,
3 => 1,
4 => [
0 => 1,
1 => 9,
]
];
$arr = [
0 => null,
1 => $arr,
];
function split_array($a) {
$ended = true;
$new_line = [];
$rem_line = [];
$last_line = array_pop($a);
foreach ($last_line as $k=>$v){
if ( is_array( $v ) ) {
$new_v = array_shift($v);
if ( count($v) > 0 ) {
$rem_v = $v;
$ended = false;
} else {
$rem_v = $new_v;
}
} else {
$new_v = $v;
$rem_v = $v;
}
$new_line[] = $new_v;
$rem_line[] = $rem_v;
}
$a[] = $new_line;
if ( ! $ended ) {
echo 'not ended';
$a[] = $rem_line;
$a = split_array($a);
}
return $a;
}
array_shift($arr);
echo '<pre>';
print_r( split_array($arr) );
echo '</pre>';
I have one array like this :
$array='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}'
I want first split to two array like this :
$array[0]={b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1}
And
$array[1]={b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}
I change every array with this code
foreach ($b as $k => $m) {
if ($k % 2 == 0) {
$even[]= $m;
}
else {
$odd[] = $m;
}
}
$ff=array_combine($even,$odd);
I want output change like this
Array( Array[0] => ([b_price] => 9500 [b_discount] => 10 [mainPrice] => 95000 [total] => 95000 [title] =>obj1)
Array[1] => ([b_price] => 1500 [b_discount] => 15 [mainPrice] => 15000 [total] => 22500 [title] => obj2))
Two approaches:
-- using explode and array_map functions:
$str = '{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
$result = array_map(function($v){
$r = [];
$arr = explode(',', trim($v, '{}'));
foreach ($arr as $k => $v) {
if (!($k % 2)) $r[$v] = $arr[$k+1];
}
return $r;
}, explode('},{', $str));
print_r($result);
-- using additional preg_match_all and array_combine functions:
$str = '{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
$result = array_map(function($v){
preg_match_all('/([^,]+),([^,]+),?/', trim($v, '{}'), $m);
return array_combine($m[1], $m[2]);
}, explode('},{', $str));
print_r($result);
The output:
Array
(
[0] => Array
(
[b_price] => 9500
[b_discount] => 10
[mainPrice] => 95000
[total] => 95000
[title] => obj1
)
[1] => Array
(
[b_price] => 1500
[b_discount] => 15
[mainPrice] => 15000
[total] => 22500
[title] => obj2
)
)
you should change your needle, in your array string,
i have changed it with semicolon,
$arrayString='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1};{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
echo $arrayString;
echo "<pre>"; print_r (explode(";",$arrayString));
$b=explode(";",$arrayString);
foreach ($b as $k => $m) {
if ($k % 2 == 0) {
$even[]= $m;
}
else {
$odd[] = $m;
}
}
$ff=array_combine($even,$odd);
So, I write this decision. Maybe it can be more clear, but it works.
$array='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
/*Making array from string*/
$tmp_array = explode("},{", $array);
/*Removing { symbols*/
$tmp_array[0] = substr($tmp_array[0],1);
$tmp_array[1] = substr($tmp_array[1],0,-1);
/*Making arrays from string [0] and [1]*/
$tmp_array[0] = explode(',',$tmp_array[0]);
$tmp_array[1] = explode(',',$tmp_array[1]);
$new_array = [];
/*Creating associative arrays*/
for($a = 0; $a < count($tmp_array); $a++) {
$new_as_array = [];
for($i = 0; $i <= count($tmp_array[0]); $i+=2) {
if($i + 1 <= count($tmp_array[0])) {
$new_as_array[$tmp_array[$a][$i]] = $tmp_array[$a][$i + 1];
}
}
$new_array[] = $new_as_array;
}
print_r($new_array);
I have numbers, from 0 to 8. I would like in result, all possible sets of those numbers, each set should use all numbers, each number can occur only once in a set.
I would like to see solution made in PHP that could print out result. Or, at least, I would like some refreshment in theory of combinatorics, as I have long forgotten it. What is the formula to calculate how many permutations will there be?
Example sets:
0-1-2-3-4-5-6-7-8
0-1-2-3-4-5-6-8-7
0-1-2-3-4-5-8-6-7
0-1-2-3-4-8-5-6-7
0-1-2-3-8-4-5-6-7
0-1-2-8-3-4-5-6-7
and so on...
You're looking for the permutations formula:
nPk = n!/(n-k)!
In your case, you have 9 entries and you want to choose all of them, that's 9P9 = 9! = 362880
You can find a PHP algorithm to permutate in recipe 4.26 of O'Reilly's "PHP Cookbook".
pc_permute(array(0, 1, 2, 3, 4, 5, 7, 8));
Copied in from O'Reilly:
function pc_permute($items, $perms = array( )) {
if (empty($items)) {
print join(' ', $perms) . "\n";
} else {
for ($i = count($items) - 1; $i >= 0; --$i) {
$newitems = $items;
$newperms = $perms;
list($foo) = array_splice($newitems, $i, 1);
array_unshift($newperms, $foo);
pc_permute($newitems, $newperms);
}
}
}
Since PHP 5.5 you can use Generators. Generators save a lot of memory and are way faster (more than half compared to pc_permute()). So if you have any chance of having PHP 5.5 installed, you definitely want Generators.
This snipped is ported from Python: https://stackoverflow.com/a/104436/3745311
function permutations(array $elements)
{
if (count($elements) <= 1) {
yield $elements;
} else {
foreach (permutations(array_slice($elements, 1)) as $permutation) {
foreach (range(0, count($elements) - 1) as $i) {
yield array_merge(
array_slice($permutation, 0, $i),
[$elements[0]],
array_slice($permutation, $i)
);
}
}
}
}
Sample usage:
$list = ['a', 'b', 'c'];
foreach (permutations($list) as $permutation) {
echo implode(',', $permutation) . PHP_EOL;
}
Output:
a,b,c
b,a,c
b,c,a
a,c,b
c,a,b
c,b,a
Since this question often comes up in Google Search results, here's a modified version of the accepted answer that returns all combinations in an array and passes them as a return value of the function.
function pc_permute($items, $perms = array( )) {
if (empty($items)) {
$return = array($perms);
} else {
$return = array();
for ($i = count($items) - 1; $i >= 0; --$i) {
$newitems = $items;
$newperms = $perms;
list($foo) = array_splice($newitems, $i, 1);
array_unshift($newperms, $foo);
$return = array_merge($return, pc_permute($newitems, $newperms));
}
}
return $return;
}
To use:
$value = array('1', '2', '3');
print_r(pc_permute($value));
I've something that You may like
function combination_number($k,$n){
$n = intval($n);
$k = intval($k);
if ($k > $n){
return 0;
} elseif ($n == $k) {
return 1;
} else {
if ($k >= $n - $k){
$l = $k+1;
for ($i = $l+1 ; $i <= $n ; $i++)
$l *= $i;
$m = 1;
for ($i = 2 ; $i <= $n-$k ; $i++)
$m *= $i;
} else {
$l = ($n-$k) + 1;
for ($i = $l+1 ; $i <= $n ; $i++)
$l *= $i;
$m = 1;
for ($i = 2 ; $i <= $k ; $i++)
$m *= $i;
}
}
return $l/$m;
}
function array_combination($le, $set){
$lk = combination_number($le, count($set));
$ret = array_fill(0, $lk, array_fill(0, $le, '') );
$temp = array();
for ($i = 0 ; $i < $le ; $i++)
$temp[$i] = $i;
$ret[0] = $temp;
for ($i = 1 ; $i < $lk ; $i++){
if ($temp[$le-1] != count($set)-1){
$temp[$le-1]++;
} else {
$od = -1;
for ($j = $le-2 ; $j >= 0 ; $j--)
if ($temp[$j]+1 != $temp[$j+1]){
$od = $j;
break;
}
if ($od == -1)
break;
$temp[$od]++;
for ($j = $od+1 ; $j < $le ; $j++)
$temp[$j] = $temp[$od]+$j-$od;
}
$ret[$i] = $temp;
}
for ($i = 0 ; $i < $lk ; $i++)
for ($j = 0 ; $j < $le ; $j++)
$ret[$i][$j] = $set[$ret[$i][$j]];
return $ret;
}
Here is how to use it:
To get the number of combinations:
combination_number(3,10); // returns number of combinations of ten-elements set.
To get all possible combinations:
$mySet = array("A","B","C","D","E","F");
array_combination(3, $mySet); // returns all possible combinations of 3 elements of six-elements set.
Hope You make use of that.
I've ported the Python itertools code listed here (using generators). The advantage over the solutions posted so far is that it allows you to specify r (permutation size).
function permutations($pool, $r = null) {
$n = count($pool);
if ($r == null) {
$r = $n;
}
if ($r > $n) {
return;
}
$indices = range(0, $n - 1);
$cycles = range($n, $n - $r + 1, -1); // count down
yield array_slice($pool, 0, $r);
if ($n <= 0) {
return;
}
while (true) {
$exit_early = false;
for ($i = $r;$i--;$i >= 0) {
$cycles[$i]-= 1;
if ($cycles[$i] == 0) {
// Push whatever is at index $i to the end, move everything back
if ($i < count($indices)) {
$removed = array_splice($indices, $i, 1);
array_push($indices, $removed[0]);
}
$cycles[$i] = $n - $i;
} else {
$j = $cycles[$i];
// Swap indices $i & -$j.
$i_val = $indices[$i];
$neg_j_val = $indices[count($indices) - $j];
$indices[$i] = $neg_j_val;
$indices[count($indices) - $j] = $i_val;
$result = [];
$counter = 0;
foreach ($indices as $indx) {
array_push($result, $pool[$indx]);
$counter++;
if ($counter == $r) break;
}
yield $result;
$exit_early = true;
break;
}
}
if (!$exit_early) {
break; // Outer while loop
}
}
}
It works for me, but no promises!
Example usage:
$result = iterator_to_array(permutations([1, 2, 3, 4], 3));
foreach ($result as $row) {
print implode(", ", $row) . "\n";
}
This is my version of class. This class builds and returns permutated array as result
class Permutation {
private $result;
public function getResult() {
return $this->result;
}
public function permute($source, $permutated=array()) {
if (empty($permutated)){
$this->result = array();
}
if (empty($source)){
$this->result[] = $permutated;
} else {
for($i=0; $i<count($source); $i++){
$new_permutated = $permutated;
$new_permutated[] = $source[$i];
$new_source = array_merge(array_slice($source,0,$i),array_slice($source,$i+1));
$this->permute($new_source, $new_permutated);
}
}
return $this;
}
}
$arr = array(1,2,3,4,5);
$p = new Permutation();
print_r($p->permute($arr)->getResult());
The last three lines to test my class.
This is a simple recursive function that prints all permutations (written in pseudocode)
function rec(n, k) {
if (k == n) {
for i = 0 to n-1
print(perm[i], ' ');
print('\n');
}
else {
for i = 0 to n-1 {
if (not used[i]) {
used[i] = true;
perm[k] = i;
rec(n, k+1);
used[i] = false;
}
}
}
}
And it is called like this:
rec(9, 0);
Lexicographical order. There is no recursion. Almost no limits for array length.
There is no sort. It's running rather fast. It's easy to understand.
Minus: it gives a notice, but you can add a condition to start compare with the second element or error_reporting(0).
$a = array(
1,
2,
3,
4,
5
);
$b = array_reverse($a);
print_r($a);
//here need "br"
while ($a != $b)
{
foreach(array_reverse($a, true) as $k => $v)
{
if ($v < $a[$k + 1])
{
foreach(array_reverse($a, true) as $ka => $val)
{
if ($val > $v) break;
}
$ch = $a[$k];
$a[$k] = $a[$ka];
$a[$ka] = $ch;
$c = array_slice($a, 0, $k + 1);
print_r($a = array_merge($c, array_reverse(array_slice($a, $k + 1))));
//here need "br"
break;
}
}
}
You're basically talking about permutations where both n and k are 9 so you'll have 9! different permutations; see this: http://en.wikipedia.org/wiki/Permutation.
Here is my proposal, hope a little bit clearer than accepted answer.
function permutate($elements, $perm = array(), &$permArray = array())
{
if(empty($elements))
{
array_push($permArray,$perm); return;
}
for($i=0;$i<=count($elements)-1;$i++)
{
array_push($perm,$elements[$i]);
$tmp = $elements; array_splice($tmp,$i,1);
permutate($tmp,$perm,$permArray);
array_pop($perm);
}
return $permArray;
}
and usage:
$p = permutate(array('a','b','c'));
foreach($p as $perm)
print join(",",$perm)."|\n";
//function call
print_r(combinations([1,2,3,4,5,6,7,8,9,10,11,12,13]));
/**
* #param $mainArray
* #param int $size - optional
* #param array $combinations - optional
* #return mixed
*/
function combinations($mainArray, $size = 3, $combinations = [])
{
if (empty($combinations)) {
$combinations = $mainArray;
}
if ($size == 1) {
return str_replace('-','',$combinations);;
}
$newCombination = array();
foreach ($mainArray as $key => $val){
foreach ($combinations as $char) {
if(in_array($val, explode('-', $char))){
continue;
}
$newCombination[] = $val . '-' . $char;
}
}
return combinations($mainArray, $size - 1, $newCombination);
}
//========================= Next solution ==================================
function sampling($chars, $size, $combinations = array()) {
# if it's the first iteration, the first set
# of combinations is the same as the set of characters
if (empty($combinations)) {
$combinations = $chars;
}
# we're done if we're at size 1
if ($size == 1) {
return $combinations;
}
# initialise array to put new values in
$new_combinations = array();
# loop through existing combinations and character set to create strings
foreach ($combinations as $combination) {
foreach ($chars as $char) {
$new_combinations[] = $combination .'-'. $char ;
}
}
# call same function again for the next iteration
return $this->sampling($chars, $size - 1, $new_combinations);
}
function array_has_dupes($array) {
return count($array) !== count(array_unique($array));
}
function total() {
// Generate ticket price
$arrfinal = array();
// combinations
$chars = array(1,2,3,4,5,6,7,8,9,10,11,12,13); // for 10 digits
$combinations = $this->sampling($chars, 3);
//print_r($combinations); //exit;
foreach($combinations as $key => $val)
{
$arr = explode('-', $val);//str_split($val);
if(!$this->array_has_dupes($arr)){
$arrfinal[] = str_replace('-', '', $val);
}
}
echo '<pre>'; print_r($arrfinal); echo '</pre>';
}
Simple solution using recursion
function filterElement($element){
if(is_array($element[0])){
return $element[0];
}
# base case
return $element;
}
function permutation($input, $path){
// base case 1
if(count($input) == 0){
return [$path];
}
$output = [];
foreach($input as $index => $num){ # 1, 2,3, 4
$copyPath = $path; # copy the path - []
$copyPath[] = $num; # append the number [1]
# remove the current number
$inputLocal = $input;
unset($inputLocal[$index]); # [2, 3, 4]
$permute = permutation($inputLocal, $copyPath); # call [2, 3, 4], [1]
# for all element find add to output
foreach($permute as $ele){
# filter ouput
$output[] = filterElement($ele);
}
}
return $output;
}
print_r(permutation([1,2,3,4], []));
output
Array
(
[0] => Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
[1] => Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 3
)
[2] => Array
(
[0] => 1
[1] => 3
[2] => 2
[3] => 4
)
[3] => Array
(
[0] => 1
[1] => 3
[2] => 4
[3] => 2
)
[4] => Array
(
[0] => 1
[1] => 4
[2] => 2
[3] => 3
)
[5] => Array
(
[0] => 1
[1] => 4
[2] => 3
[3] => 2
)
[6] => Array
(
[0] => 2
[1] => 1
[2] => 3
[3] => 4
)
[7] => Array
(
[0] => 2
[1] => 1
[2] => 4
[3] => 3
)
[8] => Array
(
[0] => 2
[1] => 3
[2] => 1
[3] => 4
)
[9] => Array
(
[0] => 2
[1] => 3
[2] => 4
[3] => 1
)
[10] => Array
(
[0] => 2
[1] => 4
[2] => 1
[3] => 3
)
[11] => Array
(
[0] => 2
[1] => 4
[2] => 3
[3] => 1
)
[12] => Array
(
[0] => 3
[1] => 1
[2] => 2
[3] => 4
)
[13] => Array
(
[0] => 3
[1] => 1
[2] => 4
[3] => 2
)
[14] => Array
(
[0] => 3
[1] => 2
[2] => 1
[3] => 4
)
[15] => Array
(
[0] => 3
[1] => 2
[2] => 4
[3] => 1
)
[16] => Array
(
[0] => 3
[1] => 4
[2] => 1
[3] => 2
)
[17] => Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
)
[18] => Array
(
[0] => 4
[1] => 1
[2] => 2
[3] => 3
)
[19] => Array
(
[0] => 4
[1] => 1
[2] => 3
[3] => 2
)
[20] => Array
(
[0] => 4
[1] => 2
[2] => 1
[3] => 3
)
[21] => Array
(
[0] => 4
[1] => 2
[2] => 3
[3] => 1
)
[22] => Array
(
[0] => 4
[1] => 3
[2] => 1
[3] => 2
)
[23] => Array
(
[0] => 4
[1] => 3
[2] => 2
[3] => 1
)
)
Let's say I have following array:
Array
(
[0] => Array
(
[0] => a
[1] => 1
)
[1] => Array
(
[0] => b
[1] => 8
)
[2] => Array
(
[0] => c
[1] => 16
)
[3] => Array
(
[0] => d
[1] => 21
)
....
)
Numbers in inner array are generated randomly from range (0, 100) and they don't repeat.
I would like to create a loop, which will iterate from 0 to 100 and check if loop iteration is equal to inner number of above array. Excepted result is array with 100 elements:
Array
(
[0] => const
[1] => a
[2] => const
[3] => const
[4] => const
[5] => const
[6] => const
[7] => const
[8] => b
[9] => const
[10] => const
.
.
[16] => c
[17] => const
.
.
[21] => d
[22] => const
[23] => const
.
.
)
What I need is something like:
for ($i=0; $i < 100; $i++) {
if($i === $name[$i][1]) {
$new_array[] = $name[$i][0];
} else {
$new_array[] = 'const';
}
}
But I can't get it working, thus I need some help.
I am not an English native speaker, so hopefully you understand what I would like to achieve. Thanks for any help.
you need a nested loop like:
for ($i=0; $i < 100; $i++):
$found = false;
foreach($name as $array):
if($array[1] === $i):
$found = true;
$new_array[] = $array[0];
endif;
endforeach;
if(!$found):
$new_array[] = 'const';
endif;
endfor;
The reason it doesn't work is because each time $i is incremented you're trying to make a match in $name[$i], and not checking all of the arrays in $name, the simplest solution I can think of (and to perform the least number of iterations) would be to do something like:
$new_array = array();
foreach ($name as $n) {
$new_array[$n[1]] = $n[0];
}
for ($i=0; $i<100; $i++) {
if (!isset($new_array[$i])) {
$new_array[$i] = 'const';
}
}
ksort($new_array);
So first of all, loop through your $name array, and set up your $new_array with the the key => value pair (eg. [1] => 'a', [8] => 'b'), then in the for loop just check if the key ($i) has already been set, and if not, set it with the value 'const'. Finally, sort the $new_array by its keys.
The number of iterations in this example is count($name) + 100, whereas a nested loop for example would be 100 * count($name).
use
for ($i=0; $i < 100; $i++) {
if($i === $name[$i][1]) {
$new_array[$i] = $name[$i][0];
}
else{
$new_array[$i] = 'const';
}
}
for ($i = 0; $i < count($name); ++$i) {
if ($name[$i][1] === $i) {
$name[$i] = $name[$i][0];
} else {
$name[$i] = 'const';
}
}
Why do u use Identical operator instead of Equal
for ($i=0; $i < 100; $i++) {
if($i == $name[$i][1]) {
$new_array[] = $name[$i][0];
} else {
$new_array[] = 'const';
}
}
I try to write a script and a problem. Can you let me know if you know how i can do this or ask someone if they know how can this be possibe.
Max numbers which can be selected 1 to 20 numbers. it can loop and select any number between 1-20
ignore these numbers e.g. 1,2,4,6,9,12 this will be array which can change.
each array line can have upto 4 numbers
Each array line needs to be unique
5.I need to have around 10 arrays unique
Max 2 numbers can match previous numbers see below.
How can i go about doing this. Any help would be great.
e.g.
Array(
[0] => Array
(
[0] => 3
[1] => 16
[2] => 22
[3] => 24
)
[1] => Array
(
[0] => 3
[1] => 16
[2] => 7
[3] => 13
)
[2] => Array
(
[0] => 20
[1] => 17
[2] => 10
[3] => 18
)
)
This not allow as some array match each other
Array(
[0] => Array
(
[0] => 3
[1] => 16
[2] => 22
[3] => 24
)
[1] => Array - cant have this as 3 of the numbers matchs the previous array.only two numbers can match.
(
[0] => 3
[1] => 16
[2] => 22
[3] => 13
)
[2] => Array
(
[0] => 20
[1] => 17
[2] => 10
[3] => 18
)
)
Thank you.
This seems to satisfy your conditions: http://codepad.viper-7.com/WHkQeD
<?php
$num_arrays = 10; $num_elements = 4;
$min = 1; $max = 20;
$exclude_numbers = array( 1, 4, 6); // Add numbers here to exclude
$answer = array();
for( $i = 0; $i < $num_arrays; $i++)
{
$answer[$i] = array();
for( $j = 0; $j < $num_elements; $j++)
{
do
{
$current = rand( $min, $max);
// If the previous array exists and there are more than two common elements when we add the $current element, continue
if( isset( $answer[$i-1]) && count( array_intersect( $answer[$i-1], array_merge( $answer[$i], array( $current))) > 2)
{
continue;
}
} while( in_array( $current, $exclude_numbers) || in_array( $current, $answer[$i]));
$answer[$i][$j] = $current;
}
}
var_dump( $answer);
Edit: Here is a complete solution that satisfies all of your criteria.
Demo
<?php
$num_arrays = 10; $num_elements = 4;
$min = 1; $max = 20;
$exclude_numbers = array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
$answer = array();
for( $i = 0; $i < $num_arrays; $i++)
{
$answer[$i] = array();
for( $j = 0; $j < $num_elements; $j++)
{
do
{
// Get a random element
$current = rand( $min, $max);
$new_array = array_merge( $answer[$i], array( $current));
// If the previous array has more than two common elements (because of the added $current), get a new $current
if( isset( $answer[$i-1]) && count( array_intersect( $answer[$i-1], $new_array)) > 2)
{
$answer[$i] = array_diff( $new_array, $answer[$i-1]);
$j = count( $answer[$i]) - 1;
continue;
}
} while( in_array( $current, $exclude_numbers) || in_array( $current, $answer[$i]));
$answer[$i][$j] = $current;
// If the array is complete, we need to check for unique arrays
if( count( $answer[$i]) == $num_elements)
{
$k = $i - 1;
while( $k >= 0)
{
if( count( array_diff( $answer[$k], $answer[$i])) == 0)
{
// This array is the same as a previous one, start over
$answer[$i] = array();
$j = -1;
break;
}
$k--;
}
// Optionally sort each array
sort( $answer[$i]);
}
}
}
var_dump( $answer);
somthing like:
function generate_list($max, $forbidden)
{
$list = array();
while(count($list) < 4)
{
$new = rand(1, $max);
if(in_array($new, $forbidden))
{
continue;
}
$list[] = $new;
$forbidden[] = $new;
}
return $list;
}
function count_max_same($new_list, $old_lists)
{
$max_same = 0;
foreach($old_lists as $current_list)
{
$max_same = max($max_same, count(array_intersect($new_list, $old_lists)));
}
return $max_same;
}
function generate_unique_lists($count_of_lists, $max, $forbidden, $max_same = 2, $max_tries = 1000)
{
$lists = array();
while($max_tries-- AND count($lists) < $count_of_lists)
{
$new_list = generate_list($max, $forbidden);
if(count_max_same($new_list, $lists) <= $max_same)
{
$lists[] = $new_list;
}
}
return $lists;
}