【问题标题】:Efficiently sort a delimited string of numbers and rearrange related delimited strings to match the sort有效地对分隔的数字字符串进行排序,并重新排列相关的分隔字符串以匹配排序
【发布时间】:2012-05-22 10:37:57
【问题描述】:

我已经得到了三个字符串,这些字符串是由组合查询和文件读取的漫长过程生成的,每个字符串都相互关联。


示例:

$versions = "1 2 5 4 10 6 8 7 3 9";
$weights = "50.2 60.5 35 10 15.98 60 50 60.1 70 75";
$ids = "512 318 112 326 155 191 977 961 943 441";

我想按照版本号升序排列。

示例结果:

$versions = "1 2 3 4 5 6 7 8 9 10";
$weights = "50.2 60.5 70 10 35 60 60.1 50 75 15.98";
$ids = "512 318 943 326 112 191 961 977 441 155";

我的问题是:有没有比我现在做的更有效的方法?
请注意,这些字符串可能会变得很大,到目前为止我看到的最大的是大约 600 个不同的版本

我执行以下操作:

  • 分解字符串
  • 复制以版本号为键的数组
  • 按键排序数组
  • 内爆字符串返回

这是代码和a live example

$versions = "1 2 5 4 10 6 8 7 3 9";
$weights = "50.2 60.5 35 10 15.98 60 50 60.1 70 75";
$ids = "512 318 112 326 155 191 977 961 943 441";

$a_versions = explode(" ", $versions);
$a_weights = explode(" ", $weights);
$a_ids = explode(" ", $ids);

$s_versions = array();
$s_weights = array();
$s_ids = array();

//set keys to correspond to version number
foreach($a_versions as $key => $ver){
    $s_versions[$ver] = $a_versions[$key];
    $s_weights[$ver] = $a_weights[$key];
    $s_ids[$ver] = $a_ids[$key];
}

//sort according to keys
ksort($s_versions, SORT_NUMERIC);
ksort($s_weights, SORT_NUMERIC);
ksort($s_ids, SORT_NUMERIC);

//implode back
$versions = implode(" ", $s_versions); 
$weights = implode(" ", $s_weights); 
$ids = implode(" ", $s_ids); 

echo "
    <pre>
        $versions
        $weights
        $ids
    </pre>
";

/*==========
Results
    1 2 3 4 5 6 7 8 9 10
    50.2 60.5 70 10 35 60 60.1 50 75 15.98
    512 318 943 326 112 191 961 977 441 155
==========*/

性能提升#1:

用 array_combine 替换 foreach 循环可能会带来一点性能提升。 – 斯文斯

确实如此,根据a simple unit test 的说法,它快了大约 11-15%。

【问题讨论】:

  • array_combine 替换foreach 循环可能会带来一点性能提升。
  • @svens 确实做到了,它提高了大约 12% 的效率(平均 100 次执行)
  • @ShadowScripter 版本($versions 中的值)是否唯一?还是同一版本可以多次出现?
  • @Yoshi 好问题,$versions$ids 都是独一无二的。
  • @ShadowScripter 但它们总是整数(例如 1 - n)?

标签: php performance sorting delimiter


【解决方案1】:

这是我能找到的最好的:

$versions = array_flip(explode(' ', $versions));
$weights = explode(' ', $weights);
$ids = explode(' ', $ids);

ksort($versions, SORT_NUMERIC);
foreach ($versions as $version => $idx) {
  $result[0][] = $version;
  $result[1][] = $weights[$idx];
  $result[2][] = $ids[$idx];
}

return array(
  implode(' ', $result[0]),
  implode(' ', $result[1]),
  implode(' ', $result[2]),
);

见: http://codepad.viper-7.com/flcvnO 比较我测试的内容(包括你的初始代码和带有 array_combine 的代码)

【讨论】:

  • 非常令人印象深刻,而且简单!不知道array_flip 存在,很酷。也感谢您提供的测试。我可以问一下你为什么这样设计你的测试布局吗?
  • @ShadowScripter 这似乎是比较这些方法的最简单方法。此外,array_multisort 在较长的版本字符串上往往会变慢,所以我试图获得更好的测试数据。
【解决方案2】:

这是另一种方法。这是最简单的例子,没有键:

$versions = "1 2 5 4 10 6 8 7 3 9";
$weights = "50.2 60.5 35 10 15.98 60 50 60.1 70 75";
$ids = "512 318 112 326 155 191 977 961 943 441";

$a_versions = explode(" ", $versions);
$a_weights = explode(" ", $weights);
$a_ids =  explode(" ", $ids);

$version_weight_id = array_map(null, $a_versions, $a_weights, $a_ids);

print_r($version_weight_id);

如果您想要更多键控,则必须具有地图功能,如下所示:

function version_weight_id($v, $w, $id) {

    return array('version' => $v, 'weight' => $w, 'id' => $id);

}

$versions = "1 2 5 4 10 6 8 7 3 9";
$weights = "50.2 60.5 35 10 15.98 60 50 60.1 70 75";
$ids = "512 318 112 326 155 191 977 961 943 441";

$a_versions = explode(" ", $versions);
$a_weights = explode(" ", $weights);
$a_ids =  explode(" ", $ids);

$version_weight_id = array_map('version_weight_id', $a_versions, $a_weights, $a_ids);

array_multisort($version_weight_id, $a_versions);

print_r($version_weight_id);

编辑:

这是另一种不需要地图功能的方法:

$versions = "1 2 5 4 10 6 8 7 3 9";
$weights = "50.2 60.5 35 10 15.98 60 50 60.1 70 75";
$ids = "512 318 112 326 155 191 977 961 943 441";

$a_versions = explode(" ", $versions);
$a_weights = explode(" ", $weights);
$a_ids =  explode(" ", $ids);

$weights_ids = array_map(null, $a_weights, $a_ids);
$versions_weights_ids = array_combine($a_versions, $weights_ids);

print_r($versions_weights_ids);

关键是你要知道主键是版本,子数组0键是权重,子数组1键是id。

要打印出你会使用的结果:

foreach($versions_weights_ids as $version => $weight_id) {
echo "
    <pre>
        $version
        {$weight_id[0]}
        {$weight_id[1]}
    </pre> ";
}

如果您正在寻找 ajax 解决方案,我会考虑使用 JSON,并使用类似的东西:

$versions = "1 2 5 4 10 6 8 7 3 9";
$weights = "50.2 60.5 35 10 15.98 60 50 60.1 70 75";
$ids = "512 318 112 326 155 191 977 961 943 441";

$a_versions = explode(" ", $versions);
$a_weights = explode(" ", $weights);
$a_ids =  explode(" ", $ids);

$weights_ids = array_map(null, $a_weights, $a_ids);
$versions_weights_ids = array_combine($a_versions, $weights_ids);

echo json_encode($versions_weights_ids);

这样,您可以使用框架的 each 方法或针对特定版本(或多个版本)进行输出。

【讨论】:

  • 排序后需要将数组内爆回三个字符串,您将如何使用您的方法有效地做到这一点?除此之外,它是一个不错的解决方案!
  • @ShadowScripter - 如果您已经将它们作为字符串,为什么还需要将它们内爆?
  • 整个事情的重点是根据版本号对字符串进行排序。这稍后在 AJAX 提取中使用,我在 javascript 端进行另一个拆分/分解,但我希望在此之前对版本/权重/ID 进行排序。我认为这在我的帖子“示例”和“示例结果”部分中得到了清楚的证明。
  • 您的编辑确实让我可以根据需要调整输出。在做a simple unit test的时候,说明你的方法等于或者比原来的稍快。而 svens' 是最快的。
猜你喜欢
  • 2020-03-14
  • 2017-07-17
  • 2022-01-13
  • 2021-03-30
  • 2020-01-26
  • 2012-10-03
  • 2019-12-18
  • 1970-01-01
  • 2014-10-10
相关资源
最近更新 更多