更高效的解决方案
$dict = array_flip($order);
$positions = array_map(function ($elem) use ($dict) { return $dict[$elem['id']] ?? INF; }, $array);
array_multisort($positions, $array);
不要在每一次比较中重新计算位置
当数组很大或获取id代价较高时,使用usort()
可能会变得很糟糕,因为您需要在每次比较中重新计算id。请尝试使用预先计算的位置(见下面示例中的mediumsort
或fastsort
)使用array_multisort()
,这并不复杂。
此外,每次比较都在顺序数组中搜索id(如接受的答案中所述)并不能提高性能,因为您需要在每次比较中遍历它。请在一开始就计算它。
在下面的代码段中,您可以看到主要的三个排序函数:
slowsort
接受的答案。在每次比较中搜索位置。
mediumsort
通过预先计算位置改进了slowsort
fastsort
通过完全避免搜索,改进了mediumsort
。
请注意,这些处理元素的id不按照给定顺序提供回退值
INF
。如果您的顺序数组与原始数组的id一一匹配,则避免排序并只需将元素插入正确位置即可。我添加了一个名为
cheatsort
的函数,可以完美实现此功能。
您还可以通过权重对数组进行更一般的排序(请参见示例中的
weightedsort
)。确保仅计算一次权重,以获得良好的性能。
性能(针对长度为1000的数组):
fastsort about 1 ms
mediumsort about 3 ms
slowsort about 60 ms
提示:对于更大的数组,差异会变得更加明显。
排序函数比较
<?php
function slowsort(&$array, $order, $key = 'id')
{
usort($array, function ($a, $b) use ($order, $key) {
$pos_a = array_search($a[$key], $order);
$pos_b = array_search($b[$key], $order);
return $pos_a - $pos_b;
});
}
function mediumsort(&$array, $order, $key = 'id')
{
$positions = array_map(function ($elem) use ($order, $key) {
return array_search($elem[$key], $order);
}, $array);
array_multisort($positions, $array);
}
function fastsort(&$array, $order, $key = 'id')
{
$dict = array_flip($order);
$positions = array_map(function ($elem) use ($dict, $key) {
return $dict[$elem[$key]] ?? INF;
}, $array);
array_multisort($positions, $array);
}
function cheatsort(&$array, $order, $key = 'id')
{
$dict = array_flip($order);
$copy = $array;
foreach ($copy as $elem) {
$pos = $dict[$elem[$key]];
$array[$pos] = $elem;
}
}
function weightedsort(&$array, $weight_func)
{
$weights = array_map($weight_func, $array);
array_multisort($weights, $array);
}
function generate($size = 1000)
{
$order = array();
$array = array();
for ($i = 0; $i < $size; $i++) {
$id = random_int(0, PHP_INT_MAX);
$order[] = $id;
$array[] = array('id' => $id);
}
shuffle($order);
return [$array, $order];
}
function time_it($callable)
{
$then = microtime(true);
$callable();
$now = microtime(true);
return 1000 * ($now - $then);
}
function time_sort($sort_func)
{
echo "Timing $sort_func", PHP_EOL;
[$array, $order] = generate();
echo time_it(function () use ($sort_func, &$array, $order) {
$sort_func($array, $order);
}) . ' ms' . PHP_EOL;
}
time_sort('cheatsort');
time_sort('fastsort');
time_sort('mediumsort');
time_sort('slowsort');