【问题标题】:Is there any way to read from files faster?有什么方法可以更快地读取文件吗?
【发布时间】:2020-12-28 04:53:37
【问题描述】:

我正在尝试创建一个程序来查看字符串的所有排列,然后打印出所有有效的单词。我可以获得所有排列,但检查一个单词是否在字典文本文件中大约需要 3 秒。当我尝试 7 个字母时,花了 47:19。有什么方法可以更快地从文件中读取?
任何帮助将不胜感激。

#include <iostream>
#include <fstream>
#include <string>
#include <vector>




bool in(std::string arr, char element)
{
    for (int i = 0; i < arr.size(); i++)
    {
        if (arr[i] == element)
        {
            return true;
        }
    }

    return false;
}


bool inDictionary(std::string str)
{
    std::ifstream dictionary;
    dictionary.open("words.txt");
    if (dictionary.is_open())
    {
        std::string word;
        while (std::getline(dictionary, word))
        {
            if (word == str)
            {
                return true;
            }
        }
    }
    return false;
}




std::string remainingCharacters(std::string orginal, std::string newString)
{
    std::string characters = "";
    for (int i = 0; i < orginal.size(); i++)
    {
        if (!in(newString, orginal[i]))
        {
            characters += orginal[i];
        }
    }
    





    return characters;
}


void combinations(std::string cur, std::string original, std::vector<std::string>& permutations)
{

    for (int i = 0; i < remainingCharacters(original, cur).size(); i++)
    {
        
        permutations.push_back(cur + remainingCharacters(original, cur)[i]);
        combinations(cur + remainingCharacters(original, cur)[i], original, permutations);
    }
}

int main()
{

    std::vector<std::string> permutations;
    combinations("", "wdsrock",  permutations);
    for (int i = 0; i < permutations.size(); i++)
    {
        if (inDictionary(permutations[i]))
        {
            std::cout << permutations[i] << ", ";
        }
    }
;
}
   

【问题讨论】:

  • 如果字典不是太大,您可以在查找inDictionary 之前提前将其全部读入内存,即缓存它。
  • 如果要查找3个字,需要调用inDictionary 3次,每次打开一个文件,从文件开头开始?如果您有一个包含 1000 个单词的列表要搜索怎么办?简而言之,您当前的方法存在很大缺陷。读取文件一次,将信息存储在某处,然后搜索特定的存储,无论是数组、哈希表等,不是更有意义吗?
  • 一个比让你的慢步更快更好的问题可能是问你如何减少调用慢步的频率。这是您能想到的唯一方法(遍历排列;在文件的每次迭代中循环)吗?我将提出一个挑战:尝试只通过一次文件即可完成您的目标,而无需致电combinations()
  • @JaMiT 谢谢,我在程序开始时将文本文件存储到无序映射中,并使用 std::unordered_map::find() 快速遍历文件。现在需要不到一秒钟的时间。最后一个问题,最好使用无序映射,还是应该使用数组?
  • 取决于您在做什么以及您拥有多少数据。想象一下,您的字典中有一千个项目,并且必须一直 if (arr[i] == element) 所有这些项目。呸呸呸。 unsorted_map 使用更智能的算法进行查找,它将您的输入转换为数字并将该数字用作数组索引。有时它必须做更多的工作,太多的输入映射到相同的数字,算法必须找出数组索引的许多项目中的哪一个是正确的,但通常主要成本是将输入转换为索引它只发生一次。

标签: c++


【解决方案1】:

有一些方法可以减少读取操作的数量和测试的排列数量:

  • 将字典存储在内存中
  • 将单词字母与所有可能的排列(字谜)相关联。
  • iterate over combination 而不是排列

所以

std::unordered_map<std::string, std::vector<std::string>> read_dictionary()
{
    std::ifstream dictionary;
    dictionary.open("words.txt");
    if (!dictionary.is_open()) { throw std::runtime_error("No dictionary"); }
    std::unordered_map<std::string, std::vector<std::string>> res;
    std::string word;
    while (std::getline(dictionary, word))
    {
        auto anagram = word;
        std::sort(anagram.begin(), anagram.end());

        res[anagram].push_back(word);
    }
    return res;
}

template <typename Iterator>
bool next_combination(const Iterator first, Iterator k, const Iterator last)
{
    /* Credits: Thomas Draper */
    if ((first == last) || (first == k) || (last == k))
        return false;
     Iterator itr1 = first;
     Iterator itr2 = last;
     ++itr1;
     if (last == itr1)
         return false;
     itr1 = last;
     --itr1;
     itr1 = k;
     --itr2;
     while (first != itr1)
     {
         if (*--itr1 < *itr2)
         {
             Iterator j = k;
             while (!(*itr1 < *j)) ++j;
             std::iter_swap(itr1,j);
             ++itr1;
             ++j;
             itr2 = k;
             std::rotate(itr1,j,last);
             while (last != j)
             {
                 ++j;
                 ++itr2;
             }
             std::rotate(k,itr2,last);
             return true;
        }
    }
    std::rotate(first,k,last);
    return false;
}

int main()
{
    const auto dictionary = read_dictionary();
    std::string letters = "dsrock";
    std::sort(letters.begin(), letters.end());

    for (std::size_t i = 1; i != letters.length() + 1; ++i) {
        do {
            auto it = dictionary.find(letters.substr(0, i));
            if (it != dictionary.end()) {
                for (const auto& word : it->second) {
                    std::cout << word << std::endl;
                }
            }
        } while (next_combination(letters.begin(), letters.begin() + i, letters.end()));
    }
}

Demo

【讨论】:

    【解决方案2】:
    1. 最好的方法是将文件映射到内存并读取它。 Boost 库提供 API 来读取内存映射文件
    #include <iostream>
    #include <boost/iostreams/device/mapped_file.hpp>
    using namespace std;
    int main()
    {
    boost::iostreams::mapped_file_params arg;
    arg.path = "yourfile.txt";
    arg.new_file_size = pow(1024, 2); // 1 MB
    boost::iostreams::mapped_file::mapmode::readonly;
    boost::iostreams::mapped_file mf;
    mf.open(arg);
    char* bytes = (char*) mf.const_data();
    cout << bytes << endl;
    mf.close();
    return 0;
    }
    
    1. 如果您不想使用内存映射文件,另一种方法是读取数据块。读取数据块比单独读取字符更快
    ifstream is(filename,readmode);
    if (is) {
    char* buffer = new char[length+1];
    is.read(buffer, length);
    buffer[length] = '\0';
    is.close();
    

    【讨论】:

    • 如果您在这些文件中有大量数据并且文件组织良好且易于搜索,则两者都是选项。否则。减缓。减缓。减缓。注意:对整数进行操作时避免使用powpow(1024, 2) 进入浮点空间,由于浮点不精确,可能无法转换回您期望的整数。应该用std::vector 替换char* buffer = new char[length+1]; 显着减少内存管理问题。
    猜你喜欢
    • 1970-01-01
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    • 2022-11-24
    • 2020-06-02
    • 1970-01-01
    • 2010-09-27
    • 2021-08-16
    相关资源
    最近更新 更多