例如,假设我想在文件中找到特定的单词或数字。内容按排序顺序(显然)。由于我想在文件上运行二进制搜索,将整个文件复制到一个数组然后运行二进制搜索似乎真的浪费时间...我已经有效地将它变成了线性时间算法,因为我'在我可以运行搜索之前,我必须花费O(n)时间复制该文件。
有更快的方法吗?是否有类似lseek的东西可以用行代替字节?
如果没有,我最好只做一次线性搜索(假设我只在整个程序期间运行一次搜索)?
答案 0 :(得分:6)
你不能直线寻求。一旦你想到它就很明显了。
但是你可以在文本文件上进行二元搜索。
你做的是:
答案 1 :(得分:4)
基于磁盘的二进制搜索至少在初期需要“块感知”,即知道是否读取了整个字符串中的单个字节,即I /成本是一样的。另一个认为需要注意的是与顺序读取操作相比,搜索操作的成本相对较高。
有几种方法可以使用这种关于磁盘I / O特性的意识:
答案 2 :(得分:2)
如果文件很小,比如几百千字节,那么将整个文件读入(或虚拟内存映射)到内存中几乎肯定会更快。这是因为执行多个I / O操作以进行搜索和传输的开销比仅读取整个文件要差得多,这是大多数程序所做的,并且大多数操作系统都认为这样做了。
除非所有的行都是相同的长度,或者具有非常可预测的长度,否则没有简单的方法来寻找#n行。但是,为了执行二进制搜索,我将在二进制搜索和读取中使用字节偏移,比如100个字节(如果这些字都小于100个字符长)在偏移之前和之后 - 总共200个字节。然后在中间之前和之后扫描换行符以提取单词。
答案 3 :(得分:1)
是的,你可以lseek,但如果每行的每个单词/数字的大小是固定的,如果不是这种情况会更有帮助,这更有可能,那么你必须按文件大小查找并寻求最近的单词开始仍然达到接近二进制搜索的典型O(log n)时间复杂度。
答案 4 :(得分:1)
没有“lseek”函数,因为文件命令没有“line”的概念这个概念存在于不同的抽象层中,而不是原始文件命令。
关于它是否更快,答案取决于许多因素,包括文件大小,磁盘驱动器速度和可用RAM量。如果它不是一个大文件,我的猜测是将整个文件加载到内存中会更快。
如果它是一个大文件,我会使用二进制搜索算法将其缩小到较小的范围(比如几兆字节),然后加载整个块。
答案 5 :(得分:0)
这里有很多性能权衡,在对典型数据进行测量之前,不可能知道什么是有意义的。
如果您要维护此代码,则需要简单。如果搜索很少或文件很小,请使用线性搜索。如果成本确实很重要,你将不得不做一些实验。
线性搜索后我要尝试的第二件事是mmap
文件并扫描它以获取换行符。这确实需要线性时间,但strchr
可能非常快。如果您可以保证文件以换行符结尾,则会有所帮助。一旦划分了行,就可以通过二进制搜索来保持较小的比较次数。
你应该考虑的另一个选择是Boyer-Moore字符串搜索。这是一个亚线性时间搜索,并且根据搜索模式的大小,它可能比对数二进制搜索更快。 Boyer-Moore特别适合长搜索字符串。
最后,如果您确定二进制搜索确实很好,但识别行是性能瓶颈,您可以预先计算每行的起始位置,并将这些预先计算的位置以二进制格式存储在辅助文件中。
我觉得只做一个预测感觉很舒服:几乎可以肯定的是,一次只能用readline()
或fgets()
来避免一行阅读,因为这个策略总是涉及调用malloc()
保持线的内容。在每一行上拨打malloc()
的费用可能会淹没搜索或比较的任何费用。
答案 6 :(得分:0)
如上所述,由于文件是文本文件,因此无法可靠地预测给定行在文件中开始的字节。 ersatz二进制搜索的想法非常好。但鉴于目前顺序I / O速度有多快以及随机I / O速度有多快,除非文件很大,否则它实际上不会为您节省很多钱。
正如您所提到的,如果您要阅读它,您可以随意线性搜索它。所以这样做,在阅读时使用修改过的Boyer-Moore搜索,你会做得很好。