我想尽可能均匀地将数组拆分成行,遵守每行最小计数约束,以便行数尽可能接近最小值,并且不同行数之间的差异永远不会超过 1.
换句话说,每一行的元素个数不能低于$min或高于(2 * $min - 1)。
例如,我有一个 65 个元素的数组,最小行大小约束为 9。
$x = range(1, 65);
$min = 9;
我想将数组拆分成行,较长的行出现在较短的行之前。
[
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38],
[39, 40, 41, 42, 43, 44, 45, 46, 47],
[48, 49, 50, 51, 52, 53, 54, 55, 56],
[57, 58, 59, 60, 61, 62, 63, 64, 65],
]
经过数小时的试验,我找到了答案 :D
// i.e. count($x) = 65, min = 9
$total = (int)floor(count($x) / $min); // 65 : 9 = 7 (rounded down)
$sisa = count($x) % $min; // 65 mod 9 = 2 (leftover)
$result = [];
$endOffset = 0;
for ($i = 0; $i < $total; $i++){
if ($sisa > 0){ // 2 > 0 // 1 > 0
$add = (int)floor($sisa / $total) + 1; // (2 : 7) + 1 = 1 // (1 : 7) + 1 = 1
$length = $min + $add; // 9 + 1 = 10 // 9 + 1 = 10
$offset = $endOffset; // 0 // 10
$sisa = $sisa - $add; // 2 - 1 = 1 // 1 - 1 = 0
} else {
$offset = $endOffset; // 20 // 29 etc. // 56
$length = $min; // 9 // 9 etc. // 9
}
$arr = array_slice(
$x, $offset, $length
); // [0-9] // [10-19] // [20-28] // [29-37] etc. // [56-64]
$endOffset = $offset + $length; // 0 + 10 = 10 // 10 + 10 = 20 // 20 + 9 = 29 // 29 + 9 = 38 etc. // 56 + 9 = 65
$result[] = $arr;
}
return $result;
}
通过执行以下计算,使用
array_splice()
和array_chunk()
的方法可以替代迭代过程。
$count
)$maxRows
)$maxRows
,股息四舍五入 ($maxColumns
)$maxRows
代码:(演示)
$count = count($array); // 65
$maxRows = intdiv($count, $minColumns); // 7
$maxColumns = ceil($count / $maxRows); // 10
$longRowsCount = $count % $maxRows; // 2
var_export([
...array_chunk(
array_splice(
$array,
0,
($maxColumns * $longRowsCount) ?: $count
),
$maxColumns
),
...array_chunk($array, $maxColumns - 1)
]);
这两个分块尝试(其中任何一个都可能产生一个空数组)通过使用数组内部的扩展运算符(
...
)解包它们的行来合并在一起。如果愿意,可以调用 array_merge()
而不是解包到数组中。