I've been looking at PHP array permutation / combination questions all day.. and still can't figure it out :/
If I have an array like:
20 //key being 0
20 //key being 1
22 //key being 2
24 //key being 3
I need combinations like:
20, 20, 22 //keys being 0 1 2
20, 20, 24 //keys being 0 1 3
20, 22, 24 //keys being 0 2 3
20, 22, 24 //keys being 1 2 3
The code I currently have gives me:
20, 22, 24
because it doesn't want to repeat 20... but that's what I need!
Here is the code I have. it is directly from Php recursion to get all possibilities of strings
function getCombinations($base,$n){
$baselen = count($base);
if($baselen == 0){
return;
}
if($n == 1){
$return = array();
foreach($base as $b){
$return[] = array($b);
}
return $return;
}else{
//get one level lower combinations
$oneLevelLower = getCombinations($base,$n-1);
//for every one level lower combinations add one element to them that the last element of a combination is preceeded by the element which follows it in base array if there is none, does not add
$newCombs = array();
foreach($oneLevelLower as $oll){
$lastEl = $oll[$n-2];
$found = false;
foreach($base as $key => $b){
if($b == $lastEl){
$found = true;
continue;
//last element found
}
if($found == true){
//add to combinations with last element
if($key < $baselen){
$tmp = $oll;
$newCombination = array_slice($tmp,0);
$newCombination[]=$b;
$newCombs[] = array_slice($newCombination,0);
}
}
}
}
}
return $newCombs;
}
I've been playing around with the ($b == $lastEl) line, with no luck
===============
Questions I've already looked at, and are not the same OR that created an out of memory error!:
How can I get all permutations in PHP without sequential duplicates?
Permutations - all possible sets of numbers
Combinations, Dispositions and Permutations in PHP
PHP array combinations
Get all permutations of a PHP array?
PHP: How to get all possible combinations of 1D array?
Select only unique array values from this array
Get all permutations of a PHP array?
PHP: How to get all possible combinations of 1D array?
Select only unique array values from this array
How can I get all permutations in PHP without sequential duplicates?
Algorithm to return all combinations of k elements from n
Find combination(s) sum of element(s) in array whose sum equal to a given number
Combinations, Dispositions and Permutations in PHP
PHP array combinations
Php recursion to get all possibilities of strings
How to return permutations of an array in PHP?
Permutations - all possible sets of numbers
Subset-sum problem in PHP with MySQL
Find unique combinations of values from arrays filtering out any duplicate pairs
Finding all the unique permutations of a string without generating duplicates
Generate all unique permutations
Subset sum for exactly k integers?
I've tried some of these algorithms with an array of 12 items, and end up running out of memory. However the algorithm that I'm currently using doesn't give me an out of memory error.... BUT.. I need those duplicates!
If you don't mind using a couple of global variables, you could do this in PHP (translated from a version in JavaScript):
<?PHP
$result = array();
$combination = array();
function combinations(array $myArray, $choose) {
global $result, $combination;
$n = count($myArray);
function inner ($start, $choose_, $arr, $n) {
global $result, $combination;
if ($choose_ == 0) array_push($result,$combination);
else for ($i = $start; $i <= $n - $choose_; ++$i) {
array_push($combination, $arr[$i]);
inner($i + 1, $choose_ - 1, $arr, $n);
array_pop($combination);
}
}
inner(0, $choose, $myArray, $n);
return $result;
}
print_r(combinations(array(20,20,22,24), 3));
?>
OUTPUT:
Array ( [0] => Array ( [0] => 20
[1] => 20
[2] => 22 )
[1] => Array ( [0] => 20
[1] => 20
[2] => 24 )
[2] => Array ( [0] => 20
[1] => 22
[2] => 24 )
[3] => Array ( [0] => 20
[1] => 22
[2] => 24 ) )
The pear package Math_Combinatorics makes this kind of problem fairly easy. It takes relatively little code, it's simple and straightforward, and it's pretty easy to read.
$ cat code/php/test.php
<?php
$input = array(20, 20, 22, 24);
require_once 'Math/Combinatorics.php';
$c = new Math_Combinatorics;
$combinations = $c->combinations($input, 3);
for ($i = 0; $i < count($combinations); $i++) {
$vals = array_values($combinations[$i]);
$s = implode($vals, ", ");
print $s . "\n";
}
?>
$ php code/php/test.php
20, 20, 22
20, 20, 24
20, 22, 24
20, 22, 24
If I had to package this as a function, I'd do something like this.
function combinations($arr, $num_at_a_time)
{
include_once 'Math/Combinatorics.php';
if (count($arr) < $num_at_a_time) {
$arr_count = count($arr);
trigger_error(
"Cannot take $arr_count elements $num_at_a_time "
."at a time.", E_USER_ERROR
);
}
$c = new Math_Combinatorics;
$combinations = $c->combinations($arr, $num_at_a_time);
$return = array();
for ($i = 0; $i < count($combinations); $i++) {
$values = array_values($combinations[$i]);
$return[$i] = $values;
}
return $return;
}
That will return an array of arrays. To get the text . . .
<?php
include_once('combinations.php');
$input = array(20, 20, 22, 24);
$output = combinations($input, 3);
foreach ($output as $row) {
print implode($row, ", ").PHP_EOL;
}
?>
20, 20, 22
20, 20, 24
20, 22, 24
20, 22, 24
Why not just use binary? At least then its simple and very easy to understand what each line of code does like this? Here's a function i wrote for myself in a project which i think is pretty neat!
function search_get_combos($array){
$bits = count($array); //bits of binary number equal to number of words in query;
//Convert decimal number to binary with set number of bits, and split into array
$dec = 1;
$binary = str_split(str_pad(decbin($dec), $bits, '0', STR_PAD_LEFT));
while($dec < pow(2, $bits)) {
//Each 'word' is linked to a bit of the binary number.
//Whenever the bit is '1' its added to the current term.
$curterm = "";
$i = 0;
while($i < ($bits)){
if($binary[$i] == 1) {
$curterm[] = $array[$i]." ";
}
$i++;
}
$terms[] = $curterm;
//Count up by 1
$dec++;
$binary = str_split(str_pad(decbin($dec), $bits, '0', STR_PAD_LEFT));
}
return $terms;
}
For your example, this outputs:
Array
(
[0] => Array
(
[0] => 24
)
[1] => Array
(
[0] => 22
)
[2] => Array
(
[0] => 22
[1] => 24
)
[3] => Array
(
[0] => 20
)
[4] => Array
(
[0] => 20
[1] => 24
)
[5] => Array
(
[0] => 20
[1] => 22
)
[6] => Array
(
[0] => 20
[1] => 22
[2] => 24
)
[7] => Array
(
[0] => 20
)
[8] => Array
(
[0] => 20
[1] => 24
)
[9] => Array
(
[0] => 20
[1] => 22
)
[10] => Array
(
[0] => 20
[1] => 22
[2] => 24
)
[11] => Array
(
[0] => 20
[1] => 20
)
[12] => Array
(
[0] => 20
[1] => 20
[2] => 24
)
[13] => Array
(
[0] => 20
[1] => 20
[2] => 22
)
[14] => Array
(
[0] => 20
[1] => 20
[2] => 22
[3] => 24
)
)
Had the same problem and found a different and bitwise, faster solution:
function bitprint($u) {
$s = array();
for ($n=0; $u; $n++, $u >>= 1){
if ($u&1){
$s [] = $n;
}
}
return $s;
}
function bitcount($u) {
for ($n=0; $u; $n++, $u = $u&($u-1));
return $n;
}
function comb($c,$n) {
$s = array();
for ($u=0; $u<1<<$n; $u++){
if (bitcount($u) == $c){
$s [] = bitprint($u);
}
}
return $s;
}
This one generates all size m combinations of the integers from 0 to n-1, so for example
m = 2, n = 3 and calling comb(2, 3) will produce:
0 1
0 2
1 2
It gives you index positions, so it's easy to point to array elements by index.
Edit: Fails with input comb(30, 5). Have no idea why, anyone any idea?
Cleaned up Adi Bradfield's sugestion using strrev and for/foreach loops, and only get unique results.
function search_get_combos($array = array()) {
sort($array);
$terms = array();
for ($dec = 1; $dec < pow(2, count($array)); $dec++) {
$curterm = array();
foreach (str_split(strrev(decbin($dec))) as $i => $bit) {
if ($bit) {
$curterm[] = $array[$i];
}
}
if (!in_array($curterm, $terms)) {
$terms[] = $curterm;
}
}
return $terms;
}
The Idea is simple. Suppose you know how to permute, then if you save these permutations in a set it becomes a combinations. Set by definition takes care of the duplicate values. The Php euqivalent of Set or HashSet is SplObjectStorage and ArrayList is Array. It should not be hard to rewrite. I have an implementation in Java:
public static HashSet<ArrayList<Integer>> permuteWithoutDuplicate(ArrayList<Integer> input){
if(input.size()==1){
HashSet<ArrayList<Integer>> b=new HashSet<ArrayList<Integer>>();
b.add(input);
return b;
}
HashSet<ArrayList<Integer>>ret= new HashSet<ArrayList<Integer>>();
int len=input.size();
for(int i=0;i<len;i++){
Integer a = input.remove(i);
HashSet<ArrayList<Integer>>temp=permuteWithoutDuplicate(new ArrayList<Integer>(input));
for(ArrayList<Integer> t:temp)
t.add(a);
ret.addAll(temp);
input.add(i, a);
}
return ret;
}
Related
I am trying to split an array of items into multiple equal parts with a maximum of 6 items per array
for example:
5 items in original array --> result: 1 array with 5 items
12 items in original array --> result: 2 arrays with 6 items
7 items in original array --> result: 2 arrays with 3 and 4 items
13 items in original array --> result: 3 arrays with 5,4,4 items
I have absolutely no idea how to get started on this
I guess this is what you are looking for. Not exactly beautiful, but working:
<?php
$size = 13;
$step = 6;
$input = array_keys(array_fill(0, $size, null));
$count = ceil($size / $step);
$chunk = floor($size / $count);
$bonus = $size % $count;
for ($i = 0; $i < $count; $i++) {
$output[] =
$i == 0 ?
array_slice($input, $i * $chunk, $chunk + $bonus) :
array_slice($input, $i * $chunk + $bonus, $chunk);
}
print_r($output);
Here $size is the size of your array and $step is the size of the chunks cut from that array. You can play around with those values.
An example output with the settings above would be:
Array
(
[0] => Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
)
[1] => Array
(
[0] => 5
[1] => 6
[2] => 7
[3] => 8
)
[2] => Array
(
[0] => 9
[1] => 10
[2] => 11
[3] => 12
)
)
Ok, I did this with a more dynamic programming way, where in we compute the distribution for smaller subproblems and go from 6 to 1, to see if current $j in the code fits in any of the previous distribution.
<?php
$arr = [];
$size = rand(1,150);
$range = range(1,$size);
$dist = [];
$dist[] = [];
for($i=1;$i<=$size;++$i){
if($i <= 6) $dist[] = [$i];
else{
for($j=6;$j>=1;--$j){
if(abs($j - $dist[$i-$j][0]) <= 1){
$dist[] = array_merge($dist[$i-$j],[$j]);
break;
}
}
}
}
// echo $size,PHP_EOL;
// print_r($dist[$size]); print the distribution if you want.
$result = [];
$curr_index = 0;
foreach($dist[$size] as $chunk_size){
$result[] = array_slice($range,$curr_index,$chunk_size);
$curr_index += $chunk_size;
}
echo $size,PHP_EOL;
print_r($result);
Demo: https://3v4l.org/gCWB2 (Note that the output there is different for each version of PHP as a different random number of an array size is generated each time).
Update: You can further optimize the above code for this heavy line $dist[] = array_merge($dist[$i-$j],[$j]);, but this I leave as an exercise to you(hint: store only the smallest starting one with it's count).
I have a unordered array of numbers:
$ar1 = [101,4,320,1,2,3,45,46];
and i want to extract longest sequence of numbers in that array and put them in second array?
I have tried with usort and I get this:
Array(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 45
[5] => 46
[6] => 101
[7] => 320
[8] => 321
[9] => 323)
but how to extract just longest sequence (witch in this case is 1,2,3,4) and put in another array?
UPDATE:
Here is a code:
function sub($a,$b)
{
if ($a == $b)
{
return 0;
}
return ($a<$b)? -1:1;
}
$ar1 = ["101","4","320","321","323","1","3","2","45","46"];
$ar2 = [];
usort($ar1,"sub");
print_r($ar1);
so i want to populate $ar2 with longest sequence in array...
I got it:
$ar1 = [101,4,320,1,2,3,45,46,47];
sort($ar1);
$first = $ar1[0];//assign first (lowest) number
$i=0;
$ar2 = array(); //this array will contain all arrays of consecutive numbers
foreach($ar1 as $v){
if($v-$first == 1){
$ar2[$i][] = $v;
}else{
$i++;
$ar2[$i] = array($v); //difference > 1, we set another array
}
$first = $v;
}
//now we look for the longest array
$max = 0;
$longest = array();
foreach($ar2 as $k=>$ar){
$c = count($ar);
if($c < $max){
unset($ar2[$k]); //if it is not the longest, we discard it
}else{
$max = $c;
$longest = $ar;
}
}
echo"<pre>";
print_r($longest);
echo"</pre>";
Yields:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
Copy, paste, run, enjoy!
Hi i am trying to create a sub array from an array.i.e; think I have an array such as given below
$array = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}
which I explode and assign it to a variable $i..
and run the for loop as shown below..
for ( $i=0;$i<count($array);$i++) {
$a = array();
$b = $array[$i];
for($j=0;$j<count($array);$j++){
if($b != $array[$j]){
$a[] = $array[$j];
}
}
the output I want is when
$i = 1
the array should be
{2,3,4,5,6,7,8,9,10,11}
and when
$i = 2
the array should be
{3,4,5,6,7,8,9,10,11,12}
similarly when
$i=19
the array should be
{1,2,3,4,5,6,7,8,9,10}
so how can I do it.
Assuming $i is supposed to be an offset and not the actual value in the array, you can do
$fullArray = range(1, 19);
$i = 19;
$valuesToReturn = 10;
$subset = iterator_to_array(
new LimitIterator(
new InfiniteIterator(
new ArrayIterator($fullArray)
),
$i,
$valuesToReturn
)
);
print_r($subset);
This will give your desired output, e.g.
$i = 1 will give 2 to 11
$i = 2 will give 3 to 12
…
$i = 10 will give 11 to 1
$i = 11 will give 12 to 2
…
$i = 19 will give 1 to 10
$i = 20 will give the same as $i = 1 again
and so on.
$array = range(1, 19);
$i = 19;
$result = array();
$after = array_slice($array, $i, 10);
$before = array_slice($array, 0, 10 - count($after));
$result = array_merge($after, $before);
var_dump(json_encode($result));
P.S. please note 0 element has 1 value and so on...
for ($i = 0; $i < count($array); $i++) {
if ($i + 10 < count($array))
$a = array_slice($array, $i, 10);
else
$a = array_merge(array_slice($array, $i), array_slice($array, 0, 10-(count($array)-$i)));
// do something with $a before it is over-written on the next iteration
}
This test:
<?php
$array = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);
for ($i = 0; $i < count($array); $i++) {
if ($i + 10 < count($array))
$a = array_slice($array, $i, 10);
else
$a = array_merge(array_slice($array, $i), array_slice($array, 0, 10-(count($array)-$i)));
echo "<h2>$i</h2>\n<pre>".print_r($a,true)."</pre><br />\n";
}
Resulted in this:
0
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
[6] => 7
[7] => 8
[8] => 9
[9] => 10
)
...
9
Array
(
[0] => 10
[1] => 11
[2] => 12
[3] => 13
[4] => 14
[5] => 15
[6] => 16
[7] => 17
[8] => 18
[9] => 19
)
10
Array
(
[0] => 11
[1] => 12
[2] => 13
[3] => 14
[4] => 15
[5] => 16
[6] => 17
[7] => 18
[8] => 19
[9] => 1
)
...
18
Array
(
[0] => 19
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
[6] => 6
[7] => 7
[8] => 8
[9] => 9
)
This works fine from my end
<?php
$array = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);
$size = sizeof($array); // Defining the array size
$str = 17; // This is the reference value from which you have to extract the values
$key = array_search($str, $array);
$key = $key+1; // in order to skip the given reference value
$start = $key%$size;
$end = $start+9;
for($i=$start; $i<=$end; $i++) {
$j = ($i%$size);
$result[] = $array[$j];
}
echo '<pre>'; print_r($result);
?>
It looks like all you need is a slice of a certain size from the array, slice that wraps around the array's end and continues from the beginning. It treats the array like a circular list.
You can achieve this in many ways, one of the simplest (in terms of lines of code) is to extend the original array by appending a copy of it at its end and use the PHP function array_slice() to extract the slice you need:
function getWrappedSlice(array $array, $start, $count = 10)
{
return array_slice(array_merge($array, $array), $start, $count);
}
Of course, you have to be sure that $start is between 0 and count($array) - 1 (including), otherwise the value returned by the function won't be what you expect.
Round-robin on an array can be achieved by doing a "rotate" operation inside each iteration:
for ($i = 0; $i < count($array); ++$i) {
// rotate the array (left)
array_push($array, array_shift($array));
// use $array
}
During the loop, the first element of the array is placed at the back. At the end of the loop, the array is restored to its original value.
I have looked and googled many times I found a few posts that are simular but I can not find the answer Im looking for so I hope you good people can help me.
I have a function that returns a simple number array. The array number values are dynamic and will change most frequently.
e.g.
array(12,19,23)
What I would like to do is take each number value in the array, compare it to a set range and return all the lower value numbers up to and including the value number in the array.
So if I do this:
$array = range(
(11,15),
(16,21),
(22,26)
);
The Desired output would be:
array(11,12,16,17,18,19,22,23)
But instead I get back all the numbers in all the ranges.
array(11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26)
What would be a simple solution to resolve this?
Try this code
$range = array(
array(11,15),
array(16,21),
array(22,26),
);
$array = array(12,19,23);
$result = array();
foreach($range as $key=>$value)
{
//$range1 =$range[$key];
$min = $range[$key][0];
$max = $range[$key][1];
for($i = $min;$i<=$max;$i++)
{
if($i <= $array[$key])
{
array_push($result,$i);
}
}
}
echo "<pre>";print_r($result);
Iterate over each element, find the the start and end values you need to include, and append them to the output array:
$a = array(12,19,23);
$b = array(
range(11,15),
range(16,21),
range(22,26)
);
$c = array();
foreach ($a as $k => $cap) {
$start = $b[$k][0];
$finish = min($b[$k][count($b[$k])-1], $cap);
for ($i = $start; $i <= $finish; $i++) {
$c[] = $i;
}
}
print_r($c);
prints
Array
(
[0] => 11
[1] => 12
[2] => 16
[3] => 17
[4] => 18
[5] => 19
[6] => 22
[7] => 23
)
My solution is probably not the most efficient, but here goes:
$numbers = array(12,19,23);
$ranges = array(
array(11,15),
array(16,21),
array(22,26)
);
$output = array();
// Loop through each of the numbers and ranges:
foreach($numbers as $num) {
foreach($ranges as $r) {
if ($num >= $r[0] && $num <= $r[1]) {
// This is the correct range
// Array merge to append elements
$output = array_merge($output, range($r[0], $num));
break;
}
}
}
// Sort the numbers if you wish
sort($output, \SORT_NUMERIC);
print_r($output);
Produces:
Array
(
[0] => 11
[1] => 12
[2] => 16
[3] => 17
[4] => 18
[5] => 19
[6] => 22
[7] => 23
)
I have an arbitrary number of nested arrays in php. For example:
Array
(
[0] => Array
(
[0] => 36
[0] => 2
[0] => 9
)
[1] => Array
(
[0] => 95
[1] => 21
[2] => 102
[3] => 38
)
[2] => Array
(
[0] => 3
[1] => 5
)
)
I want to find the most efficient way to combine all possible combinations of each of these nested arrays. I'd like to end up with something that looks like this...
Array
(
[0] => "36,95,3"
[1] => "36,95,5"
[2] => "36,21,3"
[3] => "36,21,5"
etc...
)
The order the results are combined in is not important. That is to say, there is no difference between "3,95,36", "36,95,3", and "95,36,3". I would like to omit these redundant combinations.
Any suggestions on how to go about this would be much appreciated.
Thanks in advance,
<?php
$a = array("01", "02");
$b = array("white", "green");
$c = array("one", "two", "three");
$aG = array($a, $b, $c);
$codes = array();
$pos = 0;
generateCodes($aG);
function generateCodes($arr) {
global $codes, $pos;
if(count($arr)) {
for($i=0; $i<count($arr[0]); $i++) {
$tmp = $arr;
$codes[$pos] = $arr[0][$i];
$tarr = array_shift($tmp);
$pos++;
generateCodes($tmp);
}
} else {
echo join(", ", $codes)."<br/>";
}
$pos--;
}
?>
General permutation solution for any number of groups (in Groovy to keep it simple).
list = [["A", "B", "W"], ["C", "D"], ["X", "Y"]]
r = []
void p(List<List<String>> l, int n) {
for(t in l[n]) {
r[n] = t
if (n < l.size() - 1) {
p(l, n + 1)
} else {
println(r)
}
}
}
p(list, 0);