【问题标题】:How can I optimise this C++?如何优化这个 C++?
【发布时间】:2013-07-02 21:16:12
【问题描述】:

我正在尝试通过解决一些旧的 Google Code Jam 问题来练习 C++。我发现一个相对简单的方法是反转字符串中的单词。可以在这里找到https://code.google.com/codejam/contest/351101/dashboard#s=p1

到目前为止,我有:

#include<iostream>
using namespace std;

int main(){
    int n = 0;
    cin >> n;


    string rev = "";
    string buf = "";

    string data = "";
    getline(cin, data);

    for(int _ = 0; _ < n; _++){
        getline(cin, data);

        rev = "";
        buf = "";
        for(char& c : data) {
            buf += c;
            if(c == ' '){
                rev = buf + rev;
                buf = "";
            }
        }

        cout << "Case #" << _ + 1 << ": " << buf << " " << rev << endl;
    }

    return 0;
}

这似乎运行得很快。当使用大约1.2E6 的测试文件运行time ./reverse &lt; in &gt; /dev/null 时,使用g++ -O3 编译需要大约3.5 秒。

所以作为基准,我在 python 中创建了一个解决方案

#!/usr/bin/env python
from sys import stdin, stdout
stdout.writelines(map(lambda n: "Case #%d: %s\n" % (n + 1, ' '.join(stdin.readline().split()[::-1])), xrange(int(stdin.readline()))))

但是,当我在 pypy 和 time pypy reverse.py &lt; in &gt; /dev/null 下运行它时,它只需要大约 1.95 秒。

理论上pypy 是用 C++ 编写的,C++ 不应该同样快或更快,如果是这样,如何优化此代码以更快?

【问题讨论】:

  • 你真的不应该使用“_”作为变量名,如果没有别的只是作为一种风格的东西,但以 _ 或 __ 开头的变量对于某些编译器通常具有特殊含义。
  • @PherricOxide 标识符以下划线开头,后跟大写字母,包含双下划线的标识符保留用于实现。这适用于所有编译器。
  • 谢谢你告诉我。我想我最初使用它是因为我认为我不需要它,我想这只是 python 编码的一种习惯,如果在 for 循环中有一个你不需要的变量,我发现大多数人都称之为“_ ”。无论如何改变它似乎对时间没有太大影响。
  • 如果你想要真的良好的性能,放弃 C++ io 的东西和字符串...
  • 普通老 C、char*、malloc...

标签: c++ optimization g++ pypy


【解决方案1】:

一个简单的非复制/非分配标记器是可恶的std::strtok

以下内容在我的测试中胜过您的 python 程序

#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <cstring>

int main()
{
    std::cout.sync_with_stdio(false); // we don't need C in the picture

    std::string line;
    getline(std::cin, line);
    int num_cases = stoi(line);

    std::vector<char*> words;
    for(int n = 0; getline(std::cin, line) && n < num_cases; ++n)
    {   
        words.clear();
        char* p = std::strtok(&line[0], " ");
        while (p) {
            words.push_back(p);
            p = std::strtok(nullptr, " ");
        }
        std::cout << "Case #" << n + 1 << ": ";
        reverse_copy(words.begin(), words.end(),
                     std::ostream_iterator<char*>(std::cout, " "));
        std::cout << '\n'; // never std::endl!
    }
}   

PS:您的 C++ 和 python 输出不完全匹配;这个程序匹配你的 C++ 输出

【讨论】:

  • 哇! 1.2 秒。真快!我不知道 std::strtok,但它确实为此表现良好。谢谢。另外,我认为我的 C++ 和 Python 代码做了同样的事情,它们到底有什么不同?
  • @luke python 没有在每行末尾打印额外的空格
【解决方案2】:

我认为当您连接字符串时,您的 C++ 代码会执行相当多的内存副本(std::string 的大多数实现都将整个字符串保持在内存中。)我认为以下代码在没有副本的情况下执行此操作,但我没有测试它。至于为什么python表现得这么好,我不完全确定。

#include<iostream>

int main()
{
    size_t numCases;
    std::cin >> numCases;
    std::cin.ignore();

    for( size_t currentCase = 1; currentCase <= numCases; ++currentCase )
    {
        std::cout << "Case #" << currentCase << ": ";

        std::string line;
        getline(std::cin, line);
        size_t wordEnd = line.length() - 1;
        size_t lastSpace = std::string::npos;
        for ( int pos = wordEnd - 1; pos >= 0; --pos )
        {
            if ( line[pos] == ' ' )
            {
                for ( int prt = pos + 1; prt <= wordEnd; ++prt )
                    std::cout << line[prt];
                std::cout << ' ';
                lastSpace = pos;
                wordEnd = pos - 1;
                --pos;
            }
        }
        for ( int prt = 0; prt < lastSpace; ++prt )
            std::cout << line[prt];

        std::cout << std::endl;
    }

    return 0;
}

【讨论】:

  • 在g++ -O3 下编译时,实际上在运行time ./reverse &lt; in &gt; /dev/null 时似乎更慢,因为它需要大约4.5 秒。
【解决方案3】:

您可以利用算法和迭代器库来更简单地完成这一切,而不是使用两个缓冲区和大量连接。我不确定它会快多少(虽然我猜是但是),但它也更紧凑。

#include<iostream>
#include<algorithm>
#include<iterator>
#include<sstream>
using namespace std;

int main(){
    int n = 0;
    cin >> n;
    string data = "";
    getline(cin, data);
    for(int _ = 0; _ < n; _++){
        getline(cin, data);
        stringstream ss(data);
        reverse(istream_iterator<string>(ss), istream_iterator<string>());
        cout << "Case #" << _ + 1 << ": " << ss.str() << endl;
    }
    return 0;
}

【讨论】:

  • 当试图编译你的代码时,我得到:`错误:'ss'没有在这个范围内声明。
  • #include 对此感到抱歉
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-22
  • 2017-04-10
  • 2014-09-09
  • 2013-04-03
  • 2011-02-28
相关资源
最近更新 更多