PHP太慢了,有人能找到让它更快的方法吗?

Posted

技术标签:

【中文标题】PHP太慢了,有人能找到让它更快的方法吗?【英文标题】:PHP too slow, can anyone see a way to make it faster? 【发布时间】:2015-05-30 08:46:13 【问题描述】:

给定一个电话号码列表,确定它是否一致,即没有一个号码是另一个号码的前缀。假设电话目录列出了这些号码:

紧急 911 爱丽丝 97 625 999 鲍勃 91 12 54 26

在这种情况下,您无法拨打 Bob 的电话,因为一旦您拨打了 Bob 电话号码的前三位,中心就会将您的电话转至紧急电话。所以这个列表是不一致的。

输入

输入的第一行给出一个整数,1≤t≤40,测试用例的数量。每个测试用例都以电话号码 n 开头,单独一行,1≤n≤10000。然后跟随 n 行,每行有一个唯一的电话号码。电话号码是最多十位数字的序列。

输出

对于每个测试用例,如果列表一致则输出“YES”,否则输出“NO”。


该程序应该从标准输入读取,并写入标准输出。我们还可以假设输入将遵守规范。这是我的代码:

<?php

    fscanf(STDIN, "%d", $numOfTestCases);

    for ($i = 0; $i < $numOfTestCases; $i++)  //Loop for reading test cases
        fscanf(STDIN, "%d", $numOfPhoneNumbers);

        $phoneNumbers = array();
        $isConsistent = true;

        for ($j = 0; $j < $numOfPhoneNumbers; $j++)  //Loop for reading phone numbers of each test case
            fscanf(STDIN, "%d", $newNumber);

            if ($isConsistent != false)  //If list already inconsistent, we dont need to check further input
                if (empty($phoneNumbers))  // If the array of phone numbers is empty, we just add the new one
                    $phoneNumbers[$j] = $newNumber;
                 else 
                    foreach ($phoneNumbers as $k => $testNumber)  //Loop for checking if new number is consistent or not
                        $newNumLen = strlen($newNumber);
                        $testNumlen = strlen($testNumber);

                        $newBeginning = substr($newNumber, 0, $testNumlen);
                        $testBeginning = substr($testNumber, 0, $newNumLen);

                        if ($newNumber == $testBeginning || $testNumber == $newBeginning) 
                            $isConsistent = false;
                            break;
                        
                    

                    if ($isConsistent == true) $phoneNumbers[$j] = $newNumber;
                                   
             
        

        $newAnswer = ($isConsistent) ? "YES" : "NO";
        $ansString = ($i == 0) ? $newAnswer : $ansString."\n".$newAnswer;
    

    fwrite(STDOUT, $ansString);

    exit(0);
?>

我的问题是有一个测试程序正在运行它,它的超时时间为 4 秒。第二个测试文件总是超时。我无权访问测试程序或文件,但我认为文件很长,甚至可能有 40 个测试用例,每个用例都有 10000 个电话号码。

谁能看到我怎样才能让这段代码运行得更快?

这是一个示例运行:

Sample Input:

2
3
911
97625999
91125426
5
113
12340
123440
12345
98346

Sample Output:

NO
YES

【问题讨论】:

使用分析器。 xdebug 很棒。你可以看到大部分时间都花在了哪里 我会使用完全不同的方法:目前您基本上是将每个数字与其他数字进行比较,这会产生 O(n^2) 的复杂度。相反,您可以使用每个级别的数字构建已知数字的树。当您在树中输入一个新号码并在途中遇到一个已知号码时,请拒绝它。这导致了几乎线性的复杂性。 或许慢的部分是慢,阻塞,等待输入,STDIN读取 你应该使用树,而不是数组 [php] 和 [performance] 从来都不是有效的组合。 ;) 【参考方案1】:

如上所述,树是您的解决方案。 创建一棵树,其中每个节点代表一个数字中的一个数字。 每个数字的最后一位将标记为 isNumber=true,其余数字将标记为 isNumber=false。

向树添加数字时,遍历树的节点并检查是否遍历了 isNumber=true 的节点。如果是这样,返回 false/打印一些东西。

例如:见下图

添加数字 21:迭代数字。创建新节点=1,新节点=2(isNumber=true)

添加数字 911:迭代数字。创建新节点=9,新节点=1,新节点=1(isNumber=true) 添加数字 9125:迭代数字。创建新节点=9,新节点=1,新节点=2,新节点=5(isNumber=true)

添加数字 9112:迭代数字。创建新节点 = 9,新节点 = 1,新节点 = 1(错误:失败,因为 isNumber=true

希望对你有所帮助。

编辑:

好的,因为我感兴趣,所以我尝试实施该解决方案,并且非常接近您的解决方案。

我做了一个简单的脚本来创建一个testcase.txt,包含10000个数字(1≤n≤10000)。

我制作了一个简单的 unittest.php 脚本,在同一个文件上运行 40 次(1≤t≤40)。

<?php 

// create_testcase.php
$handle = fopen("testcase.txt",'w');

for($i=0 ; $i < 10000 ; $i++)
    $number = rand(1,9999999999);
    fwrite($handle,$number."\n");

fclose($handle);

// unittest.php
class Node
    private $digit;
    private $leaf = false;
    private $children = array();
    function __construct($digit,$leaf = false)
        $this->digit = $digit;
        $this->leaf = $leaf;
    

    function isLeaf()
        return $this->leaf;
    

    function hasChild($digit)
        return array_key_exists($digit,$this->children);

    

    function hasChildren()
        return count($this->children);
    

    function addChild($digit,$isLeaf)
        $this->children[$digit] = new Node($digit,$isLeaf);
        return $this->children[$digit];
    

    function getChild($digit)
        return $this->children[$digit];
    




for($i=0 ; $i < 40 ; $i++)

    $anchor = new Node(0,false);
    $isConsistent = true;
    $handle = fopen("testcase.txt",'r');

    while(($number = fgets($handle)) != false)
        $node = $anchor;
        $number = rtrim($number);
        $number_length = strlen($number);
        foreach(str_split($number) as $index => $digit)
            if($node->hasChild($digit))
                $node = $node->getChild($digit);
                if($node->isLeaf())
                    $isConsistent = false;
                    break;
                
            
            else
                (($index+1) == $number_length) ? ($isLeaf = true) : ($isLeaf = false);
                $node = $node->addChild($digit,$isLeaf);
               
        

        if($node->hasChildren())
            $isConsistent = false;      
        

        if(!$isConsistent)
            break;                  // don't continue to next number in test case
        
    
    if($isConsistent)
        print "Consist list\n";

    
    else
        print "Not Consist list\n";
    
    fclose($handle);


结果是,对于我的旧的仍在工作的 core2duo,2GB 内存: 真实0m26.123s 用户 0m26.064s 系统 0m0.048s

对于:40x10000 = 400,000 个数字。

对于单个测试用例,它需要: 真正的0m0.554s 用户 0m0.544s 系统 0m0.008s

【讨论】:

非常感谢,这是最好的解决方案。现在我只需要提出执行此操作的代码。 我尝试用上面的方法做一个解决方案,但是我仍然有一个代码太慢,测试程序4秒超时。我的新代码在这里:pastebin.com/wSzxCu05,你能发现我做错了什么吗? @YyYo 漂亮的答案。 :)

以上是关于PHP太慢了,有人能找到让它更快的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 Prolog 中更快地实现口头算术

loadNibNamed 方法太慢 - 如何让它更快?

我的电脑运行慢,怎么能让它快。求大神帮助

如何让 Flutter showModalBottomSheet 动画更快?

在 C++ 程序中使用 scanf() 比使用 cin 更快?

Virtual City Playground获得投资点值实在是太慢了,有哪位大虾有作弊修改的方法吗?