PHP搜索数组速度

3

我很抱歉要写这么长的帖子,但是我想清楚地说明我的问题。感谢任何帮助。

我有一个名为$data的数组,格式如下:

enter image description here

... ...

给定一个任意的开始日期,我需要搜索数组以查找匹配的日期:

//$holidays is an array with dates of public holidays which are not considered business days
$search_results = array();
$minDate = -10;
$maxDate = 100;
$start_date = "2015-02-25";

echo "Before loop: " . xdebug_time_index() . "<br>";

for ($i=$minDate; $i<=$maxDate; $i++) {
    if (in_array_r(getBusinessDay(new DateTime($start_date), $holidays, $i), $data)){
        $a_date = getBusinessDay(new DateTime($start_date), $holidays, $i);
        $a_key = array_search($a_date, array_column($data, "date"));
        $search_results[]=array($i, $data[$a_key]["data"]);
    }
}

echo "After loop: " . xdebug_time_index() . "<br>";

var_dump($search_results);

然而,这段代码片段在页面加载时可能会运行10-15次,每次执行需要很长时间(至少在更大的数组上需要6秒钟):

enter image description here

请问您能帮我理解是哪一部分代码导致了这个延迟以及如何可能加快这个过程吗? 感谢您提前的帮助。
以下是代码片段中使用的函数:
function getBusinessDay($startdate, $holidays, $days) {

    $calculator = new BusinessDaysCalculator($startdate, $holidays, [BusinessDaysCalculator::SATURDAY, BusinessDaysCalculator::SUNDAY]);
    $calculator->addBusinessDays($days);
    $result = $calculator->getDate()->format('Y-m-d');
    unset($calculator);

    return $result;
}

function in_array_r($needle, $haystack, $strict = false) {
    foreach ($haystack as $item) {
        if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && in_array_r($needle, $item, $strict))) {
            return true;
        }
    }
    return false;
}

这是一款计算器,它会返回下一个工作日(周末和$holidays数组中的任何日期都会被跳过):

class BusinessDaysCalculator {

    const MONDAY    = 1;
    const TUESDAY   = 2;
    const WEDNESDAY = 3;
    const THURSDAY  = 4;
    const FRIDAY    = 5;
    const SATURDAY  = 6;
    const SUNDAY    = 7;

    /**
     * @param DateTime   $startDate       Date to start calculations from
     * @param DateTime[] $holidays        Array of holidays, holidays are no considered business days.
     * @param int[]      $nonBusinessDays Array of days of the week which are not business days.
     */
    public function __construct(DateTime $startDate, array $holidays, array $nonBusinessDays) {
        $this->date = $startDate;
        $this->holidays = $holidays;
        $this->nonBusinessDays = $nonBusinessDays;
    }

    public function addBusinessDays($howManyDays) {
        $i = 0;
        while ($i < abs($howManyDays)) {
            if ($howManyDays < 0) {
                $this->date->modify("-1 day");
            } else {
                $this->date->modify("+1 day");
            }
            if ($this->isBusinessDay($this->date)) {
                $i++;
            }
        }
    }

    public function getDate() {
        return $this->date;
    }

    private function isBusinessDay(DateTime $date) {
        if (in_array((int)$date->format('N'), $this->nonBusinessDays)) {
            return false; //Date is a nonBusinessDay.
        }
        foreach ($this->holidays as $day) {
            if ($date->format('Y-m-d') == $day->format('Y-m-d')) {
                return false; //Date is a holiday.
            }
        }
        return true; //Date is a business day.
    }
}

更新 1: 我已经更新了 $data 数组的结构为

enter image description here

循环如下:

for ($i=$minDate; $i <= $maxDate; $i++) {
        $day = getBusinessDay(new DateTime($start_date), $holidays, $i);
        if (array_key_exists($day, $data)) {
            $search_results[]=array($i, $data[$day]);
        }
}

时间只有稍微改善:

enter image description here

是array_key_exists导致了延迟吗?

更新2: 这是$holidays数组(它是静态的,总是相同的):

enter image description here


分析 PHP 脚本。你可以在 *nix 上使用 KCacheGrind,在 Windows 上使用 WinCacheGrind 来读取分析文件。 - Sverri M. Olsen
$i++ 改为 ++$i,这样可以稍微减少时间。 - H Aßdøµ
你尝试过在不进行数组搜索的情况下计时程序吗?即 BusinessDaysCalculator 的开销有多大? - Ryan Vincent
你是对的 @RyanVincent。BusinessDaysCalculator 大约需要 0.0110 的时间来找到下一个工作日,并且每次都要遍历假期数组。正在尝试找到更有效的方法。 - r1pster
你是否需要每次都创建“BusinessDaysCalculator”?是否可以创建一次并重复使用它?或者只有在日期更改时才重新创建它?此外,在“isBusinessDay”函数中,将“in_array”查找更改为“key”访问可能是值得的。这只是我的猜测。 - Ryan Vincent
显示剩余3条评论
1个回答

2

根据您的评论,日期列包含唯一值。 由于您只是在日期列上进行过滤,因此更有效的方法是通过日期列对数组进行索引,因此您应该按照以下方式重新构造数据:

$data = array(
    '2015-02-19' => 1.35625,
    '2015-02-20' => 1.4015,
    '2015-02-23' => 0.9095,
    '2015-02-24' => 1.0635,
    '2015-02-25' => 1.08775,
    '2015-02-26' => 0.947,
    /* ... */
)

in_array需要循环整个数组以找到与日期对应的元素,这对于大型数组来说可能会很慢。使用这种结构,您可以通过访问$data[$date]立即获取数据。


感谢@benoit的回答。时间有所改善,但只是稍微快了一点(半秒)。您认为array_key_exists(请参见原始帖子中的更新)会再次减慢它吗? - r1pster
在我的电脑上,循环不超过0.1秒,而且这个时间与数组大小无关(我已经尝试了一百万个元素)。 您能发布$holidays变量的样本数据吗?我的测试是使用空数组完成的。 - Benoit Esnard
感谢您的帮助。请查看原始帖子中$holidays数组的更新2。您认为我应该将日期作为字符串而不是对象吗? - r1pster
如果我将$holidays的值设置为100个DateTime对象的数组,则循环不再需要0.1秒,而是需要3秒。使用字符串而不是对象会有所帮助。 - Benoit Esnard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接