Get all the combinations a number can be split into - php

I have a fixed number (5) and a fixed length (2):
I want to find all the combinations that can sum 50 (this can vary of course).
For example, for 50, and a length of 2, I want to get:
[[1, 49], [2, 48], [3, 47], [4, 46], ...],
For 50 and a length of 4, I want to get:
[[1, 1, 1, 47], [1, 1, 2, 46], [1, 1, 3, 45], [1, 1, 4, 44], ...],
I have no clue how to do this, or whether there's a name in mathematics for this.
This is what I have (doesn't have to use generators):
function getCombinations(array $numbers, int $sum, int $setLength) : \Generator {
$numbersLowestFirst = $numbers;
asort($numbersLowestFirst);
$lowestNumber = 0;
foreach ($numbersLowestFirst as $number) {
// array might be associative and we want to preserve the keys
$lowestNumber = $number;
break;
}
$remainingAmount = $sum - $lowestNumber;
$remainingNumberofItems = $setLength - 1;
$possibleItemCombinations = array_pad([$remainingAmount], $remainingNumberofItems, $lowestNumber);
}

I first made it using JS then translated to PHP. See below for a JS demonstration. The idea is as #Barmar said in comments.
Note: There will be duplicates. If you care for that, you should sort each "triplet" and look for duplications. See JS solution for that below.
function getCombinations($sum, $n)
{
if ($n == 1) {
return [[$sum]];
}
$arr = [];
for ($i = 0; $i <= $sum / 2; $i++) {
$combos = getCombinations($sum - $i, $n - 1);
foreach ($combos as $combo) {
$combo[] = $i;
$arr[] = $combo;
}
}
return ($arr);
}
JS style, it's the same idea only more comfortable to test on browser.
function getCombinations(sum, n) {
if (n == 1) return [[sum]];
var arr = []
for (var i = 0; i <= sum / 2; i++) {
var combos = getCombinations(sum - i, n - 1)
combos.forEach(combo => {
arr.push([i, ...combo]);
})
}
// removing duplicates
arr = Object.values(arr.reduce(function(agg, triplet) {
triplet = triplet.sort();
agg["" + triplet] = triplet;
return agg;
}, {}))
return arr;
}
console.log(getCombinations(5, 3))

Related

Function to find closest value or closest sum value of elements to given number

I want to send an integer array and a number to function and function will give me closest element or element sum to my number.
for example: our funtion name is findclosestsum.
findClosestSum([2, 3, 7, 14, 15], 25) --> must give 3,7,15 because sum of this 3 element is exactly 25.
findClosestSum([2, 3, 7, 14, 15], 15) --> must give only 15
findClosestSum([2, 3, 7, 14, 15], 11) --> must give 3,7 because sum=10 and very closer to 11
here is my php code
function findClosestSum($array, $number) {
$result = [];
$minDiff = null;
$arrayCount = count($array);
for ($i = 0; $i < $arrayCount; $i++) {
for ($j = $i + 1; $j < $arrayCount; $j++) {
$sum = $array[$i] + $array[$j];
$diff = abs($number - $sum);
if ($sum == $number) {
return [$array[$i], $array[$j]];
} elseif ($minDiff === null || $diff < $minDiff) {
$minDiff = $diff;
$result = [$array[$i], $array[$j]];
}
}
}
return $result;
}
it returns 7 and 15 for findClosestSum([2, 3, 7, 14, 15], 25). where is my mistake?
For solving this problem in a more productive and precise result, you may need to implement this with graphs.

Iteration over sorted associative array

I have array with positive int values like [4, 1, 75, 52, 5, 24]. I need to find two values with minimal difference. Also, I need the original keys of those two. So, I sorted the array with asort() to keep the keys. Now when I iterate I have a problem - I can't use $key + 1 to point to next element and using next() and prev() makes it difficult to get the keys (once you use next or prev pointer is moved):
for ($i = 0; $i < count($sorted)-1; $i++) {
if (current($sorted) - next($sorted) < $min) {
//echo prev($sorted) - next($sorted) . '<br>';
}
}
What would you do?
(Feel free to alter array in any other form if that makes this easier - asort is not necessary)
If I need to explain one more time: I have a problem with keys. Finding the closest values is not a problem.
I completely revamped your snippet. You can take whatever you want from below snippet,
$array = [4, 1, 5, 52, 75, 52, 24];
function difference($arr)
{
$n = count($arr);
// Initialize difference
// as infinite
$diff = PHP_INT_MAX;
// Find the min diff by comparing
// difference of all possible
// pairs in given array
$two_values = [];
for ($i = 0; $i < $n - 1; $i++) {
for ($j = $i + 1; $j < $n; $j++) {
if (abs($arr[$i] - $arr[$j]) < $diff) {
$diff = abs($arr[$i] - $arr[$j]);
$two_values['values'] = [$arr[$i], $arr[$j]];
$two_values['keys'] = [$i, $j];
$two_values['diff'] = $diff;
}
}
}
// Return min diff
return $two_values;
}
print_r(difference($array));
Demo.
Please let me know if something is not getting.
I found my own way to do this using additional array with keys and array_multisort()
$numbers = [4, 1, 75, 1, 52, 5, 52, 24, 52];
$ar = [];
$mins = [];
$min = PHP_INT_MAX;
foreach ($numbers as $key => $number) {
$ar[] = ['key' => $key, 'number' => $number];
}
array_multisort(array_column($ar, 'number'), SORT_DESC, array_column($ar, 'key'), SORT_DESC, $ar );
foreach ($ar as $key => $value) {
if (!isset($ar[$key + 1])) break;
if ($value['number'] - $ar[$key + 1]['number'] <= $min) {
$min = $value['number'] - $ar[$key + 1]['number'];
$mins = [$ar[$key + 1], $ar[$key]];
}
}

Check if array can be sorted in ascending order in single swap

I have an array say $a=array(1, 2, 6, 4, 5, 3); which can be sorted in swap in ascending order e.g. swap 6 and 3. If this is possible then my function should return true, if not then false. For example array(10, 30, 20, 40, 50, 60, 70) return true and array(80, 10, 30, 20, 40, 50, 60, 70) return false because it will take more than one swap to sort the array in ascending order so far i have done this..
function checksort($arr) {
for ($i = count($arr) - 1; $i > 0; $i--) {
if ($arr[$i] < $arr[$i - 1]) {
$j = $i - 1;
while ($j > 0) {
$j--;
$temp = $arr[$i];
$arr[$i] = $arr[$j + 1];
$arr[$j + 1] = $temp;
break;
}
}
}
$sortedarray = $arr;
sort($arr);
if ($sortedarray == $arr) {
return true;
}
return false;
}
$a = array(1, 2, 6, 4, 5, 3);
echo checksort($a);
The result I am getting is false where as it should be true. I know the issue is because the array is getting rewritten at every true condition in for loop but at the same time $i gets -1 and it reaches to the point where it can not go further and check the entire array for the condition.
Your function will only swap items next to each other and not at the other ends of the array.
It may be simpler to just look at the differences in the original array and a sorted array using array_diff_assoc() which means that this will give the number of items out of order. If there are more than 2 then return false, otherwise true...
function checksort($arr){
$b = $arr;
sort($b);
return count(array_diff_assoc($arr, $b))<=2;
}

best way to count the bigger elements on the right and left side of an array

For example, in php
$arr = [9, 4, 3, 5, 2, 6];
then,
$output = [[0,0], [1,2], [2,2], [1,1], [4,1], [1,0]];
[0, 0] = the bigger elements of 9 is 0 on both side
[1, 2] = the bigger elements of 4 is 1 (9) on left and 2 (5, 6) on right side ... [ 9 > 4] - [ 5 > 4, 6 > 4 ]
[2, 2] = the bigger elements of 3 is 2 (9, 4) on the left and 2 (5, 6) on right side
[1, 1] = the bigger elements of 5 is 1 (9) on the left and 1 (6) on the right side
[4, 1] = the bigger elements of 2 (9, 4, 3, 5) is 4 on the left and 1 (6) on the right side
[1, 0] = the bigger elements of 6 is 1 (9) on the left and 0 (no elements after 6) on the right side
I want it in O(n log(n)), is it possible?
Looks like lots of answers already, you could do this with some of the array functions like some of the answers did, but since this is probably for your homework best to keep it simple. I keep track of whether it's left or right that should be incremented each iteration by using the $side variable.
$arr = [9, 4, 3, 5, 2, 6];
$results = [];
for ($x = 0; $x < count($arr); $x++) {
$results[$x] = [0,0];
$side = 0;
for ($y = 0; $y < count($arr); $y++) {
if ($arr[$y] > $arr[$x]) {
$results[$x][$side]++;
} elseif ($arr[$x] == $arr[$y]) {
$side = 1;
}
}
}
You need to loop through $arr to get each value, and then, in the loop, loop again through $arr to get the other values. Then, in the second loop, you build your output array by comparing both the value (to know if the number is, indeed, bigger) and the key (to know if it's on the left or the right).
$arr = array(9, 4, 3, 5, 2, 6);
$output = array();
foreach ($arr as $key=>$value) {
$out = array(0, 0);
foreach ($arr as $key2=>$value2) {
if ($key2 == $key) # If it's the same element
continue;
if ($value2 > $value) {
if ($key2 < $key)
$out[0]++;
else
$out[1]++;
}
}
$output[] = $out;
}
print_r($output);
See the output here.
Try this:
function fix_array($array) {
$return_array = array();
foreach ($array as $i => $value){
$left = array_slice($array, 0, $i);
$count_left = count(array_filter($left, function($var) use($value){
return $var > $value;
}));
$right = array_slice($array, $i + 1);
$count_right = count(array_filter($right, function($var) use($value){
return $var > $value;
}));
$return_array[] = [$count_left, $count_right];
}
return $return_array;
}
$arr = [9, 4, 3, 5, 2, 6];
$new_array = fix_array($arr);
print_r($new_array);
Simply compare it with left and right values. Try this:
$arr = [9, 4, 3, 5, 2, 6];
$total = count($arr);
$new_arr=array();
foreach ($arr as $key => $value) {
$left = 0;
$right = 0;
for ($i=0; $i < $total; $i++) {
if($key > $i && $arr[$i] > $arr[$key])
{
$left++;
}
elseif ($key < $i && $arr[$i] > $arr[$key]) {
$right++;
}
}
$new_arr[]=[$left,$right];
}
echo "<pre>";
print_r($new_arr);
Try the following code using array_walk()
<?php
$arr = [9, 4, 3, 5, 2, 6];
$finalArray =[];
array_walk($arr, function($value,$key) use(&$finalArray,&$arr) {
$param ['pre_val']=0;
$param ['post_val']=0;
$param ['current_index'] = $key;
$param ['current_value'] = $value;
$arr2 = $arr;
array_walk($arr2, function(&$value,$key) use(&$finalArray,&$param) {
if($key < $param['current_index']){
if($value > $param['current_value']){$param['pre_val'] ++;}
}else{
if($value > $param['current_value']){$param['post_val'] ++;}
}
$finalArray[$param['current_index']][0] = $param['pre_val'];
$finalArray[$param['current_index']][1] = $param['post_val'];
});
});
print_r($finalArray);

How can I rotate a 2d array in php by 90 degrees

I want to rotate a matrix 90 degrees clockwise. This amounts to making the first column in the input the first row of the output, the second column of the input the 2nd row of the output, and the 3rd column of the input the 3rd row of the output. Note that the bottom of the column=the beginning of the row, because of the 90 degree rotation.
For example:
$matrix= [[1, 2, 3]
[4, 5, 6],
[7, 8, 9]];
rotate90degrees($matrix)= [[7, 4, 1],
[8, 5, 2],
[9, 6, 3]]
What I know is I have first transpose the matrix and then swap the columns to rotate the matrix by 90 degrees. How can this be applied to php?
I showed you how to transpose an array in answer to a previous question, to rotate 90 degrees, use that transpose logic, and then reverse the order of the values in each row in turn:
$matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
array_unshift($matrix, null);
$matrix = call_user_func_array('array_map', $matrix);
$matrix = array_map('array_reverse', $matrix);
var_dump($matrix);
Demo
Another reliable option:
function rotateMatrix90( $matrix )
{
$matrix = array_values( $matrix );
$matrix90 = array();
// make each new row = reversed old column
foreach( array_keys( $matrix[0] ) as $column ){
$matrix90[] = array_reverse( array_column( $matrix, $column ) );
}
return $matrix90;
}
Less clever than #mark-baker's by far. Maybe more clear.
php doesn't have concepts like "transpose" for a matrix without adding some sort of linear algebra library.
you can do it natively by eaching through the matrix and swapping some indexes
<?php
function rotate90($mat) {
$height = count($mat);
$width = count($mat[0]);
$mat90 = array();
for ($i = 0; $i < $width; $i++) {
for ($j = 0; $j < $height; $j++) {
$mat90[$height - $i - 1][$j] = $mat[$height - $j - 1][$i];
}
}
return $mat90;
}
$mat = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
print_r($mat);
//123
//456
//789
print_r(rotate90($mat));
//741
//852
//963
$mat = [[1, 2, 3], [4, 5, 6], [7, 8, 9], ["a", "b", "c"]];
print_r($mat);
//123
//456
//789
//abc
print_r(rotate90($mat));
//a741
//b852
//c963
You can concisely transpose the matrix data in a one-liner. The ... spread operator unpacks the input array's subarrays into sets of columnar data. Call array_reverse() on the columns of data sent to the custom callback function's scope.
Code: (Demo)
var_export(array_map(fn() => array_reverse(func_get_args()), ...$matrix));
Or:
var_export(array_map(fn(...$col) => array_reverse($col), ...$matrix));
Output (from either):
[[7,4,1],
[8,5,2],
[9,6,3]]
The below code is using additional space.
<?php
function rotate90degrees()
{
$matrix = [[1,2,3],
[4,5,6],
[7,8,9]];
$k = 0;
$len = count($matrix[0]);
$tmp = [];
// Create a new matrix with [[0,0,0],[0,0,0],[0,0,0]]
while($k < $len) {
$row = [];
$l = 0;
while($l < $len) {
$row[] = 0;
$l++;
}
$tmp[] = $row;
$k++;
}
// Rotate through the given matrix and fill out the above created new matrix
for($i=0; $i<$len; $i++) {
for($j=$len-1; $j>=0; $j--) {
$tmp[$i][$j] = $matrix[$j][$i];
}
$tmp[$i] = array_reverse($tmp[$i]);
}
return $matrix;
}
Below function does not use additional space.
<?php
function rotate90degreesWithoutAdditionalSpace()
{
$matrix = [[1,2,3],
[4,5,6],
[7,8,9]];
$len = count($matrix);
// Swap the rows columns.
for($i=0; $i<$len; $i++) {
for($j=$i; $j<$len; $j++) {
$tmp = $matrix[$i][$j];
$matrix[$i][$j] = $matrix[$j][$i];
$matrix[$j][$i] = $tmp;
}
}
// Swap the elements from both ends of each row until the centered element in the row.
for($i=0; $i<$len; $i++) {
for($j=0; $j<floor($len/2); $j++) {
$tmp = $matrix[$i][$j];
$matrix[$i][$j] = $matrix[$i][$len-1-$j];
$matrix[$i][$len-1-$j] = $tmp;
}
}
return $matrix;
}
you can rotate matrix cw using this code:
function rotateCW($arr){
return array_map(function($row, $i) use ($arr){
return array_reverse(array_column($arr, $i));
}, $arr[0], array_keys($arr[0]));
}
and rotate it CCW using this code:
function rotateCCW($arr){
return array_map(function($row, $i) use ($arr){
return array_column($arr, count($arr[0]) - 1 -$i);
}, $arr[0], array_keys($arr[0]));
}
function rotate90($a) {
$cnt = count($a);
$b = $a;
for ($i = 0; $i < $cnt; $i++) {
for ($j = 0; $j < $cnt; $j++) {
$b[$j][$cnt-1-$i] = $a[$i][$j];
}
}
return $b;
}

Categories