Recursively find combinations in PHP - php

I have the following structure:
A 3
B 2
C 2
D 1
E 0
While letters represent the actual value of the element, numbers represent the level of the element. I want to be able to output the following:
A B D E
A C D E
the code that I have right now is not doing the job properly and I need to find a recursive way to address the problem. Any help would be really appreciated.
<?php
// An array that holds the values
$values = array();
$values[] = "A";
$values[] = "B";
$values[] = "C";
$values[] = "D";
$values[] = "E";
// An array that holds the levels
$levels = array();
$levels[] = 3;
$levels[] = 2;
$levels[] = 2;
$levels[] = 1;
$levels[] = 0;
// We are asuming that 3 is the heighest level
$startingLevel = 3;
// this array will holds all combinations. each group is seperated by a |
$results = array();
for($i = 0; $i < count($values); $i++)
{
$thisValue = $values[$i];
$thisLevel = $levels[$i];
if($thisLevel == $startingLevel)
{
$results[] = $thisValue;
$j = 0;
$k = $i;
$limit = $thisLevel;
while($j < $thisLevel)
{
if($levels[$k] < $limit)
{
$results[] = $values[$k];
$limit = $levels[$k];
$j++;
}
$k++;
}
// separating groups by |
$results[] = "|";
}
}
// Show results
print_r($results);
?>

Most likely what you want is to generate all combinations with O((n^2-n)/2) and then compare it with the 2nd array and also what you want is to look at my example in Javascript. Array Waypoints hold your first Array. Array wayStr holds your solution. Then you need only to iterate through the solution and compare it with your 2nd array.
function getWayStr(curr) {
var nextAbove = -1;
for (var i = curr + 1; i < waypoints.length; ++i) {
if (nextAbove == -1) {
nextAbove = i;
} else {
wayStr.push(waypoints[i]);
wayStr.push(waypoints[curr]);
}
}
if (nextAbove != -1) {
wayStr.push(waypoints[nextAbove]);
getWayStr(nextAbove);
wayStr.push(waypoints[curr]);
}
}

Separate the different levels and use permutation:
lexicographic ordering

Related

Problem with php array function displaying only the first item in the array ignoring the rest

I am trying to make a function to display client's items in an array, however only the first item id is shown in the array. What's wrong with my code? I guess I'm doing something wrong with the array.
Example of sItem value: https://pastebin.pl/view/e492ffa1
$items = array();
$client_item = bin2hex($u->sItem);
$x = 0;
for ($i = 0; $i < 78; $i++) {
$item = hexdec(reverse(substr($client_item, $x, 8)));
if ($item != 0) {
$ii = $db->get_object("SELECT * FROM Clients.dbo.ITEM WHERE Num =" . $item);
if (is_object($ii)) {
$items[] = array("ItemID" => $ii->Num, "ItemSlot" => $i);
}
}
$x += 16;
}
The code above will show just the first item in the array. All others are not shown.
var items = [{"ItemID":310511133,"ItemSlot":0}]
I am not sure what I'm doing wrong. If I remove $x += 16; then it will simply add more entries in the array with the first item id. I want more entries but not with the same item id obviously. :)
var items = [{"ItemID":310511133,"ItemSlot":0},{"ItemID":310511133,"ItemSlot":1},{"ItemID":310511133,"ItemSlot":2},{"ItemID":310511133,"ItemSlot":3},{"ItemID":310511133,"ItemSlot":4}....
This is the reverse function:
function reverse($str)
{
$len = strlen($str);
$i = $len - 2;
$ret = NULL;
while (0 <= $i) {
$ret .= substr($str, $i, 2);
$i -= 2;
}
return $ret;
}
Database Query not returning any object SELECT * FROM Clients.dbo.ITEM WHERE Num = $item
can you confirm values exists in database
you can echo $item value and see manually in database are those values exists.

Switch elements of 2d array

I have a problem: I can't make my script working with penalties, but I made it work with bonuses.
Start: two-dimensional array of users, each has id, name, start position and modificator (if modificator < 0 its bonus; if modificator > 0 its penalty).
How modificators work: for example, I have a queue of users Test1, Test2, Test3. If I give user Test3 bonus 1, he will switch places with user Test 2 and final queue will be Test1, Test3, Test2. If I give him bous 2, than he will be first at queue.
Penalties works the other way around. If I give user Test1 penalty 1, he will be second in a queue. If I give him penalty 3, he will be moved to the end of the queue.
This is my working code for bonuses:
<?
$new = array(); $orders = array();
$new[111]['position'] = 1; $new[111]['user'] = 'Test1'; $orders[1] = 111; $new[111]['shift'] = 0;
$new[222]['position'] = 2; $new[222]['user'] = 'Test2'; $orders[2] = 222; $new[222]['shift'] = 0;
$new[333]['position'] = 3; $new[333]['user'] = 'Test3'; $orders[3] = 333; $new[333]['shift'] = 0;
$new[444]['position'] = 4; $new[444]['user'] = 'Test4'; $orders[4] = 444; $new[444]['shift'] = 0;
$new[555]['position'] = 5; $new[555]['user'] = 'Test5'; $orders[5] = 555; $new[555]['shift'] = 0;
$new[666]['position'] = 6; $new[666]['user'] = 'Test6'; $orders[6] = 666; $new[666]['shift'] = 0;
$new[777]['position'] = 7; $new[777]['user'] = 'Test7'; $orders[7] = 777; $new[777]['shift'] = 0;
$new[888]['position'] = 8; $new[888]['user'] = 'Test8'; $orders[8] = 888; $new[888]['shift'] = 0;
//shift function
function bonus($start, $bonus)
{
global $new, $orders;
//for bonuses, shift to start, $bonus is negative number
if ($bonus < 0)
{
for ($i = $start; $i > ($start - abs($bonus)); $i--)
{
$order_id = intval($orders[$i]);
$prev = intval($orders[$i - 1]);
if ($prev != 0)
{
$temp = $new[$order_id]['position'];
$new[$order_id]['position'] = $new[$prev]['position'];
$new[$prev]['position'] = $temp;
$orders[$i] = $prev;
$orders[$i - 1] = $order_id;
}
}
}
//end for bonuses
}
// Now I set modificator (bonus negative, penalty positive)
$new[555]['shift'] = -2;
// sort by bonuses
$i = 1;
foreach($new as $value)
{
$order_id = $orders[$i];
bonus($new[$order_id]['position'], $new[$order_id]['shift']);
$i++;
}
// sort 2d array with field position
usort($new, function($a, $b){
return $a['position'] <=> $b['position'];
});
//DATA OUTPUT
$i = 1;
foreach($new as $value)
{
echo $i . '. ' . $value['user'] . ' ; shift: ' . $value['shift'];
echo '<br>';
$i++;
}
?>
At this code I set bonus with: $new[555]['shift'] = -2;
So, penalty will be like: $new[444]['shift'] = 3;
Code for penalties I think must be something like this:
//for penalty, shift to the end, $bonus positive number
if ($bonus > 0)
{
$total_new = count($new);
for ($i = $start; $i < ($start + $bonus); $i++)
{
$order_id = intval($orders[$i]);
$next = intval($orders[$i + 1]);
if ($next < $total_new)
{
$temp = $new[$order_id]['position'];
$new[$order_id]['position'] = $new[$next]['position'];
$new[$next]['position'] = $temp;
$orders[$i] = $next;
$orders[$i + 1] = $order_id;
}
}
}
//end code for penalty
Here is something wrong with $orders. I tried many different types of code all day long, including array enumeration in asc/desc order, but it didn't work correcty...
P.S. I know about array_multisort, but I dont need to sort, I need to switch elements with each other.
Again: bonus 2 means that user must switch places with previous number 2 times. Penalty 3 means that user must switch places with next user 3 times.

PHP: Transform string to another string

I would like to Convert simple string to another format based on below logic
Example 1 : if string is 3,4-8-7,5 then I need the set as (3,8,7),(4,8,5).
Example 2: If string is "4-5,6-4" then required set will be (4,5,4),(4,6,4).
More Clear Requirements:
if string is 5-6,7,8-2,3-1. It need to be divided first like [5] AND [(6) OR (7) OR (8)] AND [(2) OR (3)] AND [1]. Result must be All possible combination: (5,6,2,1),(5,6,3,1),(5,7,2,1),(5,7,3,1),(5,8,2,1),(5,8,3,1).
The Logic behind to building the set are we need to consider ',' as OR condition and '-' as AND condition.
I am trying my best using For loop but unable to find solution
$intermediate = array();
$arry_A = explode('-', '3,4-8-7,5');
for ($i = 0; $i < count($arry_A); $i++) {
$arry_B = explode(',', $arry_A[$i]);
for ($j = 0; $j < count($arry_B); $j++) {
if (count($intermediate) > 0) {
for ($k = 0; $k < count($intermediate); $k++) {
$intermediate[$k] = $intermediate[$k] . ',' . $arry_B[$j];
}
} elseif (count($intermediate) === 0) {
$intermediate[0] = $arry_B[$j];
}
}
}
echo $intermediate, should give final result.
Cool little exercise!
I would do it with the following code, which I will split up for readability:
I used an array as output, since it's easier to check than a string.
First, we initialize the $string and create the output array $solutions. We will calculate the maximum of possible combinations from the beginning ($results) and fill the $solutions array with empty arrays which will be filled later with the actual combinations.
$string = '3,4-8-7,5';
$solutions = array();
$results = substr_count($string,',')*2;
for($i = 0; $i < $results; $i++) {
array_push($solutions,array());
}
We will need two helper functions: checkSolutions which makes sure, that the combination does not yet exist more than $limit times. And numberOfORAfterwards which will calculate the position of an OR pattern in the $string so we can calculate how often a combination is allowed in the single steps of the walkthrough.
function checkSolutions($array,$solutions,$limit) {
$count = 0;
foreach($solutions as $solution) {
if($solution === $array) $count++;
}
if($count < $limit) return true;
else return false;
}
function numberOfORAfterwards($part,$parts) {
foreach($parts as $currPart) {
if($currPart === $part) $count = 0;
if(isset($count)) if(!ctype_digit($currPart)) $count++;
}
return $count;
}
Now the main part: We are going to loop over the "parts" of the $string a part are the digits between AND operations.
If you need further explanation on this loop, just leave a comment.
$length = 0;
// split by all AND operations
$parts = explode('-',$string);
foreach($parts as $part) {
if(ctype_digit($part)) {
// case AND x AND
foreach($solutions as &$solution) {
array_push($solution,$part);
}
} else {
// case x OR x ...
$digits = explode(',',$part);
foreach($digits as $digit) {
for($i = 0; $i < $results/count($digits); $i++) {
foreach($solutions as &$solution) {
if(count($solution) == $length) {
$test = $solution;
array_push($test,$digit);
$limit = numberOfORAfterwards($part,$parts);
echo $digit.' '.$limit.'<br>';
if(checkSolutions($test,$solutions,$limit)) {
array_push($solution,$digit);
break;
}
}
}
}
}
}
$length++;
}
print_r($solutions);
Some tests:
String: 3,4-8-7,5
Combinations: (3,8,7)(3,8,5)(4,8,7)(4,8,7)
String: 5-6,7,8-2,3-1
Combinations: (5,6,2,1)(5,6,3,1)(5,7,2,1)(5,7,3,1)(5,8,2,1)(5,8,2,1)
String: 2,1-4-3,2-7,8-9
Combinations: (2,4,3,7,9)(2,4,3,8,9)(2,4,2,7,9)(1,4,3,7,9)(1,4,2,8,9)(1,4,2,8,9)
String: 1,5-3,2-1
Combinations: (1,3,1)(1,2,1)(5,3,1)(5,3,1)

Find specific value array multidimensional php

I'm new on PHP and I want to find the 0 and replace with the number that is missed, inside the inner array, on a multidimensional array. If the inner array has more than two 0's, it will be ignored and goes to the next.
$list = array("First"=>array(0,1,2,3,0,5,6,7,8,9),
"Second"=>array(0,1,2,3,4,5,6,7,8,9),
"Third"=>array(0,1,2,3,4,5,0,0,8,9),
"Fourth"=>array(0,1,2,3,4,5,6,7,8,0),
"Fifth"=>array(0,1,2,3,4,5,0,7,8,9),
"Sixth"=>array(0,0,0,3,4,5,6,0,0,0),
"Seventh"=>array(0,1,2,3,0,0,6,7,8,9),
"Eighth"=>array(0,1,2,3,4,5,0,7,8,9),
"Ninth"=>array(0,1,2,3,4,0,6,7,8,9),
"Tenth"=>array(0,0,2,3,4,5,6,7,8,9));
$countZero = 0;
foreach($list as $lvl) {
foreach($lvl as $ind => $val) {
if($countZero = array_count_values($lvl[$val] === 0))
$list[$ind][$val] = 45 - array_sum($ind);
echo $count;
}
}
I want all inner arrays, that have two 0's get only one, to have all numbers in sequence i.e.
"First"=>array(0,1,2,3,4,5,6,7,8,9);
Please, help me.
I tried this code below, trying to finde the 0's.
$counts = 0;
$newArr = array();
foreach($list as $lvl) {
if(is_array($lvl)) {
for($i = 0; $i < count($lvl) - 1; $i++) {
if(($lvl[$i] == 0) < 2){
$counts++;
$newArr[$i] = 45 - array_sum($lvl);
}
}
}
}
print_r($newArr);
This is a solution using array_walk:
array_walk($list,
function(&$numbers) {
$zeroIndex = 0;
foreach($numbers as $i => $number) {
if( $number === 0 ) {
if( $zeroIndex > 0 ) {
return;
}
$zeroIndex = $i;
}
}
$numbers[$zeroIndex] = $zeroIndex;
});
You don't need to count all the zeros. You just need to check if there are less than 3 zeros.
I'm saving the index (position) of zero ($zeroIndex = $i).
I'm assuming that the first number is always a zero ($zeroIndex = 0).
The index of the second zero is greater than zero. If I find a zero when the index of the last found zero is greater than zero (if( $zeroIndex > 0 )), this means that there are more than two zeros.
In fact,here is what I've done and worked.
$list = array(array(1,2,3,0,5,6,7,8,9),
array(1,2,3,4,5,6,7,8,9),
array(1,2,3,4,5,0,0,8,9),
array(1,2,3,4,5,6,7,8,0),
array(1,2,3,4,5,0,7,8,9),
array(0,0,3,4,5,6,0,0,0),
array(1,2,3,0,0,6,7,8,9),
array(1,2,3,4,5,0,7,8,9),
array(1,2,3,4,0,6,7,8,9));
for($l = 0; $l < count($list); $l++)
{
$total = 0;
$countZ = 0;
for($i=0; $i < 9; $i++)
{
if($list[$l][$i] == 0)
{
$countZ++;
$indexZero = $i;
}
$total += $list[$l][$i];
if($countZ > 1) {
break;
}
}
$list[$l][$indexZero] = 45 - $total;
}
print_r($list);
TY all.

How to find duplicate values in an array without using array_count_values

I am trying to find duplicated values/string in an array using for loop
<?php
$b=array('a','b','c','a','b');
$c=count($b);
$d=array();
for($i=0;$i<=($c-1);$i++)
{
for($j=1;$j<=($c-1);$j++)
{
if($b[$i]!=$b[$j])
{
$flag=1;
}
}
if($flag==1)
{
$d[$i]=$b[$i];
}
}
print_R($d);
?>
where is my mistake? I have used array $d to display non duplicate values.....
NOTE: I need to try this only with for loop - I know how to do it using array functions.
You should reverse your test, because there are almost always values, which are different from the one you're testing. And you must reset your $flag before the inner loop, otherwise it will always be true.
When you want to find unique values, you can just test against $d only. If the value is already in $d, skip it.
$c1 = count($b);
for ($i = 0; $i < $c1; $i++) {
$dup = 0;
$c2 = count($d);
for ($j = 0; $j < $c2; $j++) {
if ($b[$i] == $d[$j])
$dup = 1;
}
if (!$dup)
$d[] = $b[$i];
}
print_r($d);
If you want to find values, which don't have duplicates instead
for ($i = 0; $i < $c; $i++) {
$dup = 0;
for ($j = 0; $j < $c; $j++) {
if ($i != $j && $b[$i] == $b[$j])
$dup = 1;
}
if (!$dup)
$d[] = $b[$i];
}
function has_dupes($array){
$dupe = array();
foreach($array as $val){
if(++$dupe[$val] > 1)
return true;
}
return false;
}
could do something like this.. this would check for dupes, then u can print the uniques
Why are you making a simple task complex .. simply
$b = array('a','b','c','a','b');
var_dump(customCount($b));
Output
array (size=3)
'a' => int 2 //duplicate
'b' => int 2 //duplicate
'c' => int 1
Function Used
function customCount($array) {
$temp = array();
foreach ( $array as $v ) {
isset($temp[$v]) or $temp[$v] = 0;
$temp[$v] ++;
}
return $temp ;
}

Categories