Automatically build as many nested loops as I need? [duplicate] - php

This question already has answers here:
Permutations - all possible sets of numbers
(12 answers)
Closed 4 years ago.
I would like to make a list of every possible combination of inserted numbers. I figured out this way:
for($x=1;$x<=3;$x++){
for($y=1;$y<=3;$y++){
for($e=1;$e<=3;$e++){
echo $x.$y.$e."</br>" ;
}
}
}
But the problem with these nested loops is that I have to put manually as many loops as I have digits in a number. Is there a way to make it automatically/programmatically?

You can use a recursive function to loop again when necessary.
For example, this function prints out the numbers 1-3 for a given number of digits (minimum allowed value is 1, any lower it will still output all single-digit numbers in that range):
function variations($digits, $prefix = '') {
for ($i = 1; $i <= 3; $i++) {
$variation = $prefix . $i;
if ($digits > 1) {
variations($digits - 1, $variation);
} else {
echo $variation . "<br />";
}
}
}
variations(1);
/*
1<br />2<br />3<br />
*/
variations(2);
/*
11<br />12<br />13<br />21<br />22<br />23<br />31<br />32<br />33<br />
*/
https://3v4l.org/Rj2u7 (outputs a linebreak instead of "<br />" for display purposes)
Here's a slight variation that returns the generated numbers in an array instead of echo'ing them directly, so you can use them for some other purpose if you'd like:
function variations($digits, $prefix = '') {
$result = [];
for ($i = 1; $i <= 3; $i++) {
$variation = $prefix . $i;
if ($digits > 1) {
$result = array_merge($result, variations($digits - 1, $variation));
} else {
$result[] = $variation;
}
}
return $result;
}
print_r(variations(1));
/*
Array
(
[0] => 1
[1] => 2
[2] => 3
)
*/
print_r(variations(2));
/*
Array
(
[0] => 11
[1] => 12
[2] => 13
[3] => 21
[4] => 22
[5] => 23
[6] => 31
[7] => 32
[8] => 33
)
*/
https://3v4l.org/LBrjA

Related

PHP array multidimensional cross all elements

I need to create a sql sentence from a dinamic structure. The structure come from a multidimensional array. It is more difficult explain that show it, so I show 3 examples:
Example1: If I have this array:
myarry Array
(
[5] => Array
(
[0] => 2
[1] => 5
)
[6] => Array
(
[0] => 11
)
)
I need to create a string like:
(2 AND 11) OR (5 AND 11)
Example2: If I have this array:
myarry Array
(
[5] => Array
(
[0] => 2
[1] => 5
)
[6] => Array
(
[0] => 11
[1] => 8
)
)
I need to create a string like:
(2 AND 11) OR (5 AND 11) OR (2 AND 8) OR (5 AND 8)
Example3: If I have this array:
myarry Array
(
[5] => Array
(
[0] => 2
[1] => 5
)
[6] => Array
(
[0] => 11
)
[7] => Array
(
[0] => 70
[1] => 71
[2] => 72
)
)
I need to create a string like:
(2 AND 11 AND 70) OR (2 AND 11 AND 71) OR (2 AND 11 AND 72) OR (5 AND 11 AND 70) OR (5 AND 11 AND 71) OR (5 AND 11 AND 72)
And so on...
The index on the array are not important.
I have tried already:
foreach ($myarry as $clave => $feature){
${"feat_$n"} = $feature;
$n++;
}
$quan= count($myarry);
foreach ($feat_0 as $feature1) {
for ($m = 1; $m < $quan; $m++){
$name = "feat_{$m}";
foreach ($$name as $feature2) {
echo "OR feature1: ".$feature1." AND feature2: ".$feature2."<br>";
}
}
}
And also:
foreach ($myarry as $clave => $feature){
${"feat_$n"} = $feature;
$n++;
}
$i = 0;
foreach ($feat_0 as $clave0 => $feature0) {
for ($m = 1; $m < $cantidad; $m++){
$name = "feat_{$m}";
foreach ($$name as $clave1 => $feature1) {
echo "-feature0: ".$feature0." - feature1: ".$feature1." - i: ".$i." - m: ".$m."<br>";
$i++;
if($m == 1)
$indice = $feature1;
else
$pena[$feature0][$indice][$i] = $feature1;
}
$i=0;
}
}
But I'm not even close to the solution :(
I hope the question is clear.
Any help will be welcome!
Here is the custom function from source with some modification,
First I created unique combinations of all the arrays elements like a set and then I mapped them to create the required string.
function custom_function($myarry)
{
if (count($myarry) == 0) {
return array();
}
$a = array_shift($myarry);
if (count($myarry) == 0) {
$c = array(array());
} else {
$c = custom_function($myarry); // recursive call
}
$r = array();
foreach ($a as $v) {
foreach ($c as $p) {
$r[] = array_merge(array($v), $p);
}
}
return $r;
}
$temp = custom_function($myarry);
$andArr = [];
array_walk($temp, function ($item, $key) use (&$andArr) {
$andArr[] = '(' . implode(" AND ", $item) . ') ';
});
$str = implode(' OR ', $andArr);
array_walk — Apply a user supplied function to every member of an array
array_shift — Shift an element off the beginning of an array
Demo.

Count instances in an array with PHP

I'm trying to get a count of how many instances of 'UnitSqFeet' are within a certain range.
For example how many instances are between 0 - 175, or 176 - 300.
Here is a part example of the array (it contains 46 in total).
Array (
[0] => Array ( [UnitNumber] => 1.03 [UnitSqFeet] => 60.75 )
[1] => Array ( [UnitNumber] => 1.04 [UnitSqFeet] => 160.39 )
[2] => Array ( [UnitNumber] => 1.05 [UnitSqFeet] => 231.55 )
[3] => Array ( [UnitNumber] => 1.06 [UnitSqFeet] => 280.24 )
)
The 'UnitSqFeet' is a string so I'm assuming it'll have to be converted somewhere in there.
I managed to get this working as below but it would only output this in the first cell of an html table and not the rest. After doing some research on here I understand it's because I was repeating the query for every cell and after the first it had already retrieved all the rows. Not sure if there's a solution whereby I can just reset the query?
$counter = 0;
while ($units = odbc_fetch_array($result)) {
$num = $units['UnitSqFeet'];
$float = (float)$num;
if ($float > 0 && $float < 175) {
count($float);
$counter++;
};
};
echo $counter;
Try as
$count = $count2 = 0;
$result = array();
foreach(array_column($arr,'UnitSqFeet') as $key => $value){
if(round($value) <= 170){
if(!isset($result['0-170'])) $result['0-170'] = $count++;
$result['0-170'] = $count++;
}else{
if(!isset($result['171-300'])) $result['171-300'] = $count2++;
$result['171-300'] = $count2++;
}
}
print_r($result);
Fiddle
Try it here: http://viper-7.com/ofozYB
<?php
$UnitSqFeet = array_column($array, 'UnitSqFeet');
$output = array_filter($UnitSqFeet, function ($var) {
return ( $var >= 70 && $var <= 250 ); // return if between 70 & 250
});
print_r($output);
echo "There is " . count($output) . " values in the selected range";

php dynamic number array add missing values defined by a range

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
)

PHP check if an array contains a specific value every tot elements

I have a long array like the following.
What I need to achieve is to check if every 6 elements in this array is found the value 1. If it is not found must be added to one of the 6 elements randomly ( by replacing any of the original values ). Also if the value is found more than 2 times every 6 elements, one of them needs to be replaced with another random number.
I have already created the code that is able to generate the following array randomly.
But I do not know how to check if every 6 elements the array contains the value 1, if not how to add it randomly, if found more than 1 time how to replace one of the values with another random number.
I know it s pretty hard to understand. I hope someone will be able to help.
In few words the final result should be:
Every 6 elements this array has to contain at least the value 1 for one time and no more than one time, located every 6 elements in a random position.
This is what I have done so far, this code generates the array below:
/*
We select the elements to add randomly in the final array from a DB table
*/
$elements = parent::$db->select('_matrix_elements', 'element_id', NULL, 'ORDER BY RAND()');
$count = count($elements) - 1; //count the total number of the elements -1 to start from zero
$columns = 6;
$lines = 8;
//generating the final array
for ($a = 1; $a <= $lines; $a++) {
for ($b = 1; $b <= $columns; $b++) {
$rand = rand(1,$count);
$line['position_'.$a.'_' . $b] = $elements[$rand]['element_id'];
}
}
Array
(
[position_1_1] => 14
[position_1_2] => 6
[position_1_3] => 5
[position_1_4] => 6
[position_1_5] => 9
[position_1_6] => 8
[position_2_1] => 11
[position_2_2] => 7
[position_2_3] => 6
[position_2_4] => 1
[position_2_5] => 7
[position_2_6] => 5
[position_3_1] => 14
[position_3_2] => 5
[position_3_3] => 4
[position_3_4] => 7
[position_3_5] => 4
[position_3_6] => 10
[position_4_1] => 6
[position_4_2] => 2
[position_4_3] => 2
[position_4_4] => 1
[position_4_5] => 7
[position_4_6] => 6
[position_5_1] => 3
[position_5_2] => 7
[position_5_3] => 8
[position_5_4] => 10
[position_5_5] => 3
[position_5_6] => 2
[position_6_1] => 8
[position_6_2] => 2
[position_6_3] => 10
[position_6_4] => 2
[position_6_5] => 10
[position_6_6] => 9
[position_7_1] => 6
[position_7_2] => 10
[position_7_3] => 4
[position_7_4] => 8
[position_7_5] => 1
[position_7_6] => 5
[position_8_1] => 2
[position_8_2] => 7
[position_8_3] => 4
[position_8_4] => 7
[position_8_5] => 9
[position_8_6] => 13
)
Split the list up into a two dimensional array with 6 elements in each 1D array
Count the number of times each element appears in the 6 element sub array
If there are no ones in the array, make a random element 1
If there are 2 or more ones in the array
Replace with a random number (other than one) until there is only one one
Add the final sub array to a 1 dimensional array
Repeat for all the sub arrays in the 2D array
$list_2d = array_chunk($list, 6);
$final_list = array();
foreach ($list_2d as $array) {
$count = array_count_values($array);
if (!array_key_exists(1, $count))
$array[mt_rand(0, 5)] = 1;
else if ($count[1] > 1)
for ($i = 1; $i <= $count[1] - 1; $i++)
$array[array_search(1, $array)] = mt_rand(2, 15);
//Use whatever random number you want here, as long as it's not 1
$final_list = array_merge($final_list, $array);
}
Better to get it right straight away, than to make another solution for fixing it. Try this two functions with your code. Also I tested it so check it out here http://phpfiddle.org/lite/code/i9e-gb3. Hope this is what you need.
function randomizer($columns, $rows, $elements)
{
$line = array();
for ($row = 1; $row <= $rows; $row++)
{
$one_count = array();
for ($col = 1; $col <= $columns; $col++)
{
$line['position_' . $row . '_' . $col] = randomID($elements);
if ($line['position_' . $row . '_' . $col] == 1)
{
//we have 1 - store key value
$one_count[] = 'position_' . $row . '_' . $col;
}
}
if (empty($one_count))
{
//no 1 in last row - we will replace one random
$rand_col = rand(1, $columns);
$line['position_' . $row . '_' . $rand_col] = 1;
}
elseif (count($one_count) > 1)
{
//more than one 1 in last row, we will leave only one
//first - pick one random to keep
$keep_key = array_rand($one_count);
unset($one_count[$keep_key]);
// second - replace others with non 1 values
foreach ($one_count as $repl_key)
{
//this time we won't take ID=1
$line[$repl_key] = randomID($elements, true);
}
}
}
return $line;
}
function randomID($elements, $not1=false)
{
$count = count($elements) - 1;
$rand = rand(0, $count);
$el_id = $elements[$rand]['element_id'];
if ($not1 === true && $el_id == 1)
{
return randomID($elements, true);
}
return $el_id;
}
Almost forgot, about this $rand = rand(0, $count); < this is the correct way. You have done $count = count($elements) - 1; and yet started rand from 1 :)

PHP Find All (somewhat) Unique Combinations of an Array

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;
}

Categories