I would like to split an array into three array that have similar sums - as close as possible
I have array
$arr = [1,2,4,7,1,6,2,8];
Desire output for example:
a = 8,2 // as sum is 10
b = 7,2,1 // as sum is 10
c = 6,4,1 // as sum is 10
Thanks
You can use the following algorithm:
Sort the input array from big to small
Create output array
for each element in the input - insert to the lowest sum in the output array.
Consider the following code:
$arr = [1,2,4,7,1,6,2,8];
sort($arr);
$arr = array_reverse($arr); // big to small
$out = array(array(),array(),array()); // output array
for($i = 0; $i < 8; $i++) {
$sums = array_map("array_sum" ,$out); // get all current sums of the array
$index = array_keys($sums, min($sums))[0]; // get the min sum
$out[$index][] = $arr[$i]; // add the element to the array with the lowest sum
}
echo print_r($out, true);
Now you will get:
array:
[0]: array:
[0] => 8
[1] => 2
[2] => 1
[1]: array:
[0] => 7
[1] => 2
[2] => 1
[2]: array:
[0] => 6
[1] => 4
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).
Let's say we have arrays like below.
$arr00 = [0,1,2,...,9]; // It includes 9 arrays. So the score should be 9.
$arr01 = [0,1,...,8]; // score = 8
...
$arr09 = [0]; // score = 0
ArrScore (definition): If an array include an array with all elements it
gets one point. So in this case $arr00's total score is 9. Because it
includes all other 9 arrays. And $arr09's score will be 0.
Actual Conditions
Our array elements could be random numbers. (not sequent orders ascending +1)
There could be thousands of arrays.
Our arrays are always flat. (no duplicated element in an array)
We are using php (any theoretical approach is also ok)
Think that you have a standard PC and you will order these arrays everyday once. (No need for the result of "which arr eats which ones". Just ArrScores.)
Goal is to order arrays by ArrScore. And we need ArrScores. What should be the approach? (Theoretical or practical)
If I understood right, this might help:
function compare($a,$b) {
if(count(array_intersect($a, $b)) == count($a)) return -1;
else return 1;
}
$arr0 = [0,2,4,7];
$arr1 = [7,0,2,9,4];
$arr2 = [4,2];
$arr = [$arr0,$arr1,$arr2];
usort($arr,"compare");
foreach($arr as $a) {
print_r($a);
}
prints:
Array ( [0] => 4 [1] => 2 ) Array ( [0] => 0 [1] => 2 [2] => 4 [3] => 7 ) Array ( [0] => 7 [1] => 0 [2] => 2 [3] => 9 [4] => 4 )
EDIT:
Compute the ArrayScore for each array:
$arr0 = [0,2,4,7];
$arr1 = [7,0,2,9,4];
$arr2 = [4,2];
$arr = [$arr0,$arr1,$arr2];
$arrayScores = [];
//initialize the Scores with 0
foreach($arr as $a){
$arrayScores[] = 0;
}
//run through all arrays
for($i=0;$i<count($arr);$i++){
//with $j=$i+1, every combination is only checked once
for($j=$i+1; $j<count($arr);$j++){
if(count(array_intersect($arr[$j], $arr[$i])) == count($arr[$j])) {
$arrayScores[$i]++;
}
if(count(array_intersect($arr[$i], $arr[$j])) == count($arr[$i])){
$arrayScores[$j]++;
}
}
}
This question is similar to a previous question of mine, but I formulated it wrong. I'm very sorry; here's the actual problem.
I have a file with thousands of numbers underneath each other. Let's simplify by using this:
4
7
1
9
3
3
8
6
2
6
5
1
What I need is to output a matrix (in the form of an array) with a variable number of matrix-rows. The numbers from the file have to be devided over the rows with the first number going to the first row, the second number to the second row, etc. Or, if you like, the 1st number, and every fourth number after that, go to column 1. The second number, and every fourth number after that, to column 2, etc. In the example below, the number of rows is 3:
array (
[0] => 4,9,8,6
[1] => 7,3,6,5
[2] => 1,3,2,1
)
And in this example, the number of rows is 4:
array (
[0] => 4,3,2
[1] => 7,3,6
[2] => 1,8,5
[3] => 9,6,1
)
The number of rows is variable.
Currently, with help of Oscar Jara, I now have this:
$path = "data.csv";
$array = explode("\n", file_get_contents($path));
$numbers = array();
foreach(array_chunk($array, 3) as $number){
$numbers[] = implode(",", $number);
}
But this outputs the numbers from the file over rows instead of columns:
array (
[0] => 4,7,1
[1] => 9,3,3
[2] => 8,6,2
[3] => 6,5,1
)
I get confused when transforming this code into dividing into columns. If you don't, then any help is appreciated.
Try this:
$path = "data.csv";
$data = file($path);
$numbers = Array();
$rowcount = 4;
foreach($data as $i=>$n) {
$numbers[$i % $rowcount][] = $n;
}
// OPTIONAL: Join rows together into comma-separated string
$numbers = array_map(function($a) {return implode(",",$a);},$numbers);
$verticallyChunked = array();
$numColumns = 4;
$i = 0;
// optionally pad the array to next largest multiple of the chunksize
// important if you output an html table and like valid well formed html
$totalRows = ceil(count($array) / $numColumns);
$array = array_pad($array, $totalRows * $numColumns, '');
foreach ($array as $val) {
$verticallyChunked[$i++ % $numColumns][] = $val;
}
Is it possible to divide a number into an array based on its value?
For example:
$val = 3;
// do something here to convert the number 3 into 1's
Array
(
[0] => 1
[1] => 1
[2] => 1
)
$array = array_fill(0, $val, 1);
array_fill(0, $val, 1);
will create an array
Array
(
[0] => 1
[1] => 1
[2] => 1
)
Do something like this:
$arr = Array();
for ($i=0;$i<$val;$i++) {
$arr[] = 1;
}
But with larger numbers you may need something different.
Another slightly shorter solution is to use range()
$val = 3;
$array = range(1, $val);
print_r($array);
// Output:
// Array
// (
// [0] => 1
// [1] => 2
// [2] => 3
// )
It's not possible for the value to be negative, or zero.
That's good, because all of these solutions (including loops) won't work with a zero or negative. However, range() will give you a different result (the 5 digit range of 1 to -3, for example).
i want to set my array key initial value to a certain number.
here is what i have:
$tickets=array();
array_push($tickets,"10","20","TBD")
for($i=3; $i<20; $i++)
i want my array initial value to start at 3 not 0.
any ideas
Set your first value manually with $tickets[3]=$value and PHP will start putting $tickets[] at the next index (4, then 5, etc).
If you're initializing $tickets right there why not use an array literal?
$tickets=array(3=>10, 4=>20, 5=>'TBD');
print_r($tickets);
prints
Array
(
[3] => 10
[4] => 20
[5] => TBD
)
edit and btw: This also works with variables in both places, the key and the value. Therefore
$x = 5;
$y = 'TBD';
$tickets=array(3=>10, 4=>20, $x=>$y);
print_r($tickets);
has the same output as well as
$tickets=array( /* initial index here */ 3=>10, 20, 'TDB');
print_r($tickets);
Set $start_key to 3, and use range() to create the set of keys. Use array_combine() to combine into the array set up how you want:
$tickets = array();
array_push($tickets,"10","20","TBD");
print_r($tickets);
// This is the zero-indexed array that occurs by default:
// Array
// (
// [0] => 10
// [1] => 20
// [2] => TBD
// )
$start_key = 3;
$tickets = array_combine(range($start_key,count($tickets)+($start_key-1)), $tickets);
print_r($tickets);
// Now you have an array whose keys start at 3:
// Array
// (
// [3] => 10
// [4] => 20
// [5] => TBD
// )
Does this work?
$tickets = array();
for ($i=3; $i<20; $i++) {
$tickets[$i] = 'TBD';
}