Discuz教程网

PHP数组交集的优化代码分析

[复制链接]
authicon dly 发表于 2011-3-8 17:47:12 | 显示全部楼层 |阅读模式
假设我们正在运营一个手机相关的网站,用户可以通过指定若干参数(如操作系统,屏幕分辨率,摄像头像素等等)来筛选自己想要的手机。
不过由于手机的参数多,且不同的手机其参数差异大,所以参数表结构通常是纵表(一个参数是一行),而不是横表(一个参数是一列),此时使用若干参数来取结果,通常就是把每个单独参数来取结果,再一起取交集。
假定每个参数会包含一千个左右的唯一结果(id int),以此为前提来模拟生成一些数据:
代码如下:

  1. <?php
  2. $rand = function() {
  3. $result = array();
  4. for ($i = 0; $i < 1000; null) {
  5. $value = mt_rand(1, 10000);
  6. if (!isset($result[$value])) {
  7. $result[$value] = null;
  8. $i++;
  9. }
  10. }
  11. return array_keys($result);
  12. };
  13. $param_a = $rand();
  14. $param_b = $rand();
  15. ?>
复制代码

注意:如果测试数据集过小的话,结论可能会出现不一致,先来看看通过PHP内置方法array_intersect实现的性能:
复制代码 代码如下:

  1. <?php
  2. $time = microtime(true);
  3. $result = array_intersect($param_a, $param_b);
  4. $time = microtime(true) - $time;
  5. echo "array_intersect: {$time}\n";
  6. ?>
复制代码

再来看看通过自定义方法intersect实现的性能:
代码如下:

  1. <?php
  2. function intersect() {
  3. if (func_num_args() < 2) {
  4. trigger_error('param error', E_USER_ERROR);
  5. }
  6. $args = func_get_args();
  7. foreach ($args AS $arg) {
  8. if (!is_array($arg)) {
  9. trigger_error('param error', E_USER_ERROR);
  10. }
  11. }
  12. $intersect = function($a, $b) {
  13. $result = array();
  14. $length_a = count($a);
  15. $length_b = count($b);
  16. for ($i = 0, $j = 0; $i < $length_a && $j < $length_b; null) {
  17. if($a[$i] < $b[$j]) {
  18. $i++;
  19. } else if($a[$i] > $b[$j]) {
  20. $j++;
  21. } else {
  22. $result[] = $a[$i];
  23. $i++;
  24. $j++;
  25. }
  26. }
  27. return $result;
  28. };
  29. $result = array_shift($args);
  30. sort($result);
  31. foreach ($args as $arg) {
  32. sort($arg);
  33. $result = $intersect($result, $arg);
  34. }
  35. return $result;
  36. }
  37. $time = microtime(true);
  38. $result = intersect($param_a, $param_b);
  39. $time = microtime(true) - $time;
  40. echo "intersect: {$time}\n";
  41. ?>
复制代码

直觉上,我们肯定会认为内置函数快于自定义函数,但本例中结果恰恰相反:
array_intersect: 0.023918151855469
intersect: 0.0026049613952637
需要提醒大家的是,array_intersect和intersect在功能上并不完全等价,例子如下:
代码如下:


  1. $param_a = array(1, 2, 2);
  2. $param_b = array(1, 2, 3);
  3. var_dump(
  4. array_intersect($param_a, $param_b),
  5. intersect($param_a, $param_b)
  6. );
  7. array_intersect: 1, 2, 2
  8. intersect: 1, 2
复制代码


也就是说,如果在第一个数组参数中有重复元素的话,则array_intersect会返回所有满足条件的重复元素,而不是仅仅返回一个,有兴趣的读者可以变换一下参数顺序再看结果。
再唠叨一下,最初我写intersect方法时,大概写成下面这个样子:
代码如下:

  1. <?php
  2. function intersect() {
  3. if (func_num_args() < 2) {
  4. trigger_error('param error', E_USER_ERROR);
  5. }
  6. $args = func_get_args();
  7. foreach ($args AS $arg) {
  8. if (!is_array($arg)) {
  9. trigger_error('param error', E_USER_ERROR);
  10. }
  11. }
  12. $result = array();
  13. $data = array_count_values(
  14. call_user_func_array('array_merge', $args)
  15. );
  16. foreach ($data AS $value => $count) {
  17. if ($count > 1) {
  18. $result[] = $value;
  19. }
  20. }
  21. return $result;
  22. }
  23. ?>
复制代码

代码更简洁,不过有一个弊端,因为使用了array_merge,所以当数组中元素非常多的时候,占用的内存会比较大,反之如果数组中元素不是非常多,那么此方法也是可行的。(来源:火丁笔记)





上一篇:PHP单元测试利器 PHPUNIT深入用法
下一篇:用php在MongoDB中模拟Auto Increment
authicon ningbear 发表于 2011-5-8 13:59:55 | 显示全部楼层
看帖必回
authicon 21585151 发表于 2011-5-9 04:59:50 | 显示全部楼层
这个贴不错!!!
authicon №小乖 发表于 2011-5-9 18:59:50 | 显示全部楼层
支持!好东西,拿走了!
authicon soul2511 发表于 2011-5-13 13:59:31 | 显示全部楼层
不错,我喜欢
authicon lightning123 发表于 2011-5-21 07:59:48 | 显示全部楼层
不错,我喜欢
authicon melody0721 发表于 2011-5-21 12:00:04 | 显示全部楼层
哦哦,发财了啊,看到好东西啦
authicon 陶衣小可 发表于 2011-5-21 15:00:01 | 显示全部楼层
支持!好东西,拿走了!
authicon Pianissimo 发表于 2011-6-16 11:18:55 | 显示全部楼层
有意思~顶顶 ,继续顶顶。继续顶哦
authicon 82xiaochong911 发表于 2011-6-16 16:00:03 | 显示全部楼层
回贴下载呀
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1314学习网 ( 浙ICP备10214163号 )

GMT+8, 2025-5-4 04:30

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表