解析 1 TB 文本并有效计算每个单词出现的次数

2024-03-06

最近,我遇到一个面试问题,用任何语言创建一个算法,该算法应该执行以下操作

  1. 读取 1 TB 内容
  2. 对该内容中每个重复出现的单词进行计数
  3. 列出最常出现的 10 个单词

您能让我知道为此创建算法的最佳方法吗?

Edit:

好吧,假设内容是英文的。我们如何找到该内容中出现频率最高的前 10 个单词?我的另一个疑问是,如果他们故意提供唯一的数据,那么我们的缓冲区将因堆大小溢出而过期。我们也需要处理这个问题。


面试答案

这项任务很有趣,但又不太复杂,因此是开始良好技术讨论的好方法。我完成这项任务的计划是:

  1. 使用空格和标点符号作为分隔符,将输入数据拆分为单词
  2. 将找到的每个单词输入到Trie http://en.wikipedia.org/wiki/Trie结构,计数器在代表单词最后一个字母的节点中更新
  3. 遍历完全填充的树以查找计数最高的节点

在采访中......我会展示以下想法Trie http://en.wikipedia.org/wiki/Trie在木板或纸上画树。从空开始,然后基于包含至少一个重复出现的单词的单个句子构建树。说“猫能捉老鼠”。最后展示如何遍历树以找到最高计数。然后我会证明这棵树如何提供良好的内存使用、良好的单词查找速度(特别是在自然语言的情况下,许多单词彼此派生),并且适合并行处理。

在黑板上画画

Demo

下面的 C# 程序在 4 核 Xeon W3520 上在 75 秒内处理 2GB 文本,最多使用 8 个线程。性能就在身边每秒 430 万字低于最佳输入解析代码。随着特里结构 http://en.wikipedia.org/wiki/Trie为了存储单词,在处理自然语言输入时,内存不是问题。

Notes:

  • 测试文本获得自古腾堡计划 http://www.gutenberg.org
  • 输入解析代码假设换行并且不是最理想的
  • 标点符号和其他非单词的删除做得不是很好
  • 处理一个大文件而不是几个较小的线程将需要少量代码来开始读取文件内指定偏移量之间的线程。

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;

namespace WordCount
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Counting words...");
            DateTime start_at = DateTime.Now;
            TrieNode root = new TrieNode(null, '?');
            Dictionary<DataReader, Thread> readers = new Dictionary<DataReader, Thread>();

            if (args.Length == 0)
            {
                args = new string[] { "war-and-peace.txt", "ulysees.txt", "les-miserables.txt", "the-republic.txt",
                                      "war-and-peace.txt", "ulysees.txt", "les-miserables.txt", "the-republic.txt" };
            }

            if (args.Length > 0)
            {
                foreach (string path in args)
                {
                    DataReader new_reader = new DataReader(path, ref root);
                    Thread new_thread = new Thread(new_reader.ThreadRun);
                    readers.Add(new_reader, new_thread);
                    new_thread.Start();
                }
            }

            foreach (Thread t in readers.Values) t.Join();

            DateTime stop_at = DateTime.Now;
            Console.WriteLine("Input data processed in {0} secs", new TimeSpan(stop_at.Ticks - start_at.Ticks).TotalSeconds);
            Console.WriteLine();
            Console.WriteLine("Most commonly found words:");

            List<TrieNode> top10_nodes = new List<TrieNode> { root, root, root, root, root, root, root, root, root, root };
            int distinct_word_count = 0;
            int total_word_count = 0;
            root.GetTopCounts(ref top10_nodes, ref distinct_word_count, ref total_word_count);
            top10_nodes.Reverse();
            foreach (TrieNode node in top10_nodes)
            {
                Console.WriteLine("{0} - {1} times", node.ToString(), node.m_word_count);
            }

            Console.WriteLine();
            Console.WriteLine("{0} words counted", total_word_count);
            Console.WriteLine("{0} distinct words found", distinct_word_count);
            Console.WriteLine();
            Console.WriteLine("done.");
        }
    }

    #region Input data reader

    public class DataReader
    {
        static int LOOP_COUNT = 1;
        private TrieNode m_root;
        private string m_path;        

        public DataReader(string path, ref TrieNode root)
        {
            m_root = root;
            m_path = path;
        }

        public void ThreadRun()
        {
            for (int i = 0; i < LOOP_COUNT; i++) // fake large data set buy parsing smaller file multiple times
            {
                using (FileStream fstream = new FileStream(m_path, FileMode.Open, FileAccess.Read))
                {
                    using (StreamReader sreader = new StreamReader(fstream))
                    {
                        string line;
                        while ((line = sreader.ReadLine()) != null)
                        {
                            string[] chunks = line.Split(null);
                            foreach (string chunk in chunks)
                            {
                                m_root.AddWord(chunk.Trim());
                            }
                        }
                    }
                }
            }
        }
    }

    #endregion

    #region TRIE implementation

    public class TrieNode : IComparable<TrieNode>
    {
        private char m_char;
        public int m_word_count;
        private TrieNode m_parent = null;
        private ConcurrentDictionary<char, TrieNode> m_children = null;

        public TrieNode(TrieNode parent, char c)
        {
            m_char = c;
            m_word_count = 0;
            m_parent = parent;
            m_children = new ConcurrentDictionary<char, TrieNode>();            
        }

        public void AddWord(string word, int index = 0)
        {
            if (index < word.Length)
            {
                char key = word[index];
                if (char.IsLetter(key)) // should do that during parsing but we're just playing here! right?
                {
                    if (!m_children.ContainsKey(key))
                    {
                        m_children.TryAdd(key, new TrieNode(this, key));
                    }
                    m_children[key].AddWord(word, index + 1);
                }
                else
                {
                    // not a letter! retry with next char
                    AddWord(word, index + 1);
                }
            }
            else
            {
                if (m_parent != null) // empty words should never be counted
                {
                    lock (this)
                    {
                        m_word_count++;                        
                    }
                }
            }
        }

        public int GetCount(string word, int index = 0)
        {
            if (index < word.Length)
            {
                char key = word[index];
                if (!m_children.ContainsKey(key))
                {
                    return -1;
                }
                return m_children[key].GetCount(word, index + 1);
            }
            else
            {
                return m_word_count;
            }
        }

        public void GetTopCounts(ref List<TrieNode> most_counted, ref int distinct_word_count, ref int total_word_count)
        {
            if (m_word_count > 0)
            {
                distinct_word_count++;
                total_word_count += m_word_count;
            }
            if (m_word_count > most_counted[0].m_word_count)
            {
                most_counted[0] = this;
                most_counted.Sort();
            }
            foreach (char key in m_children.Keys)
            {
                m_children[key].GetTopCounts(ref most_counted, ref distinct_word_count, ref total_word_count);
            }
        }

        public override string ToString()
        {
            if (m_parent == null) return "";
            else return m_parent.ToString() + m_char;
        }

        public int CompareTo(TrieNode other)
        {
            return this.m_word_count.CompareTo(other.m_word_count);
        }
    }

    #endregion
}

这里是跨 8 个线程处理相同 20MB 文本 100 次的输出。

Counting words...
Input data processed in 75.2879952 secs

Most commonly found words:
the - 19364400 times
of - 10629600 times
and - 10057400 times
to - 8121200 times
a - 6673600 times
in - 5539000 times
he - 4113600 times
that - 3998000 times
was - 3715400 times
his - 3623200 times

323618000 words counted
60896 distinct words found
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

解析 1 TB 文本并有效计算每个单词出现的次数 的相关文章

  • ASP.NET MVC 中的经典 ASP (C#)

    我有一个应用程序想要 最终 转换为 ASP NET MVC 我想要进行全面的服务升级 到 ASP NET 但想要使用当前的 ASP 内容来运行当前的功能 这样我就可以在对新框架进行增量升级的同时升级小部分 该站点严重依赖于不太成熟的 VB6
  • asp.net 文本框文本模式数字,仅允许数字

    我只是想知道 ASP NET 中是否有一种方法只允许文本框中的数字textmode number 当我使用这个时
  • 迭代变量并查找特定类型实例的技术

    我想迭代进程中内存中的变量 通过插件动态加载 并查找特定类型的实例 以前我可以找到特定类型 或内存中的所有类型 我可以创建类型的实例 我可以获取作为不同类型的字段包含的实例 但我无论如何都不知道只是 搜索 特定类型的实例 一种方法是使用 W
  • 使用post方法将多个参数发送到asp.net core 3 mvc操作

    使用 http post 方法向 asp net mvc core 3 操作发送具有多个参数的 ajax 请求时存在问题 参数不绑定 在 dot net 框架 asp net web api 中存在类似的限制 但在 asp net mvc
  • C++:重写已弃用的虚拟方法时出现弃用警告

    我有一个纯虚拟类 它有一个纯虚拟方法 应该是const 但不幸的是不是 该接口位于库中 并且该类由单独项目中的其他几个类继承 我正在尝试使用这个方法const不会破坏兼容性 至少在一段时间内 但我找不到在非常量方法重载时产生警告的方法 以下
  • Clang 编译器 (x86):80 位长双精度

    我正在尝试在 x86 Windows 平台上使用本机 80 位长双精度 海湾合作委员会选项 mlong double 80 https gcc gnu org onlinedocs gcc x86 Options html似乎不适用于 cl
  • 如何使用recv()检测客户端是否仍然连接(并且没有挂起)?

    我写了一个多客户端服务器程序C on SuSE Linux 企业服务器 12 3 x86 64 我为每个客户端使用一个线程来接收数据 我的问题是 我使用一个终端来运行服务器 并使用其他几个终端来运行服务器telnet到我的服务器 作为客户端
  • 暂停下载线程

    我正在用 C 编写一个非常简单的批量下载程序 该程序读取要下载的 URL 的 txt 文件 我已经设置了一个全局线程和委托来更新 GUI 按下 开始 按钮即可创建并启动该线程 我想要做的是有一个 暂停 按钮 使我能够暂停下载 直到点击 恢复
  • 如何从 C# 控制器重定向到外部 url

    我使用 C 控制器作为网络服务 在其中我想将用户重定向到外部网址 我该怎么做 Tried System Web HttpContext Current Response Redirect 但没有成功 使用控制器的重定向 http msdn
  • 当前的 c++ 工作草案与当前标准有何不同

    通过搜索该标准的 PDF 版本 我最终找到了这个链接C 标准措辞草案 http www open std org jtc1 sc22 wg21 docs papers 2012 n3376 pdf从 2011 年开始 我意识到我可以购买最终
  • 将数据打印到文件

    我已经超载了 lt lt 运算符 使其写入文件并写入控制台 我已经为同一个函数创建了 8 个线程 并且我想输出 hello hi 如果我在无限循环中运行这个线程例程 文件中的o p是 hello hi hello hi hello hi e
  • 生产代码中的 LRU 实现

    我有一些 C 代码 需要使用 LRU 技术实现缓存替换 目前我知道两种实现LRU缓存替换的方法 每次访问缓存数据时使用时间戳 最后比较替换时的时间戳 使用缓存项的堆栈 如果最近访问过它们 则将它们移动到顶部 因此最后底部将包含 LRU 候选
  • g++ 对于看似不相关的变量“警告:迭代...调用未定义的行为”

    考虑以下代码strange cpp include
  • 当前的 x86 架构是否支持非临时加载(来自“正常”内存)?

    我知道有关此主题的多个问题 但是 我没有看到任何明确的答案或任何基准测量 因此 我创建了一个处理两个整数数组的简单程序 第一个数组a非常大 64 MB 第二个数组b很小 无法放入 L1 缓存 程序迭代a并将其元素添加到相应的元素中b在模块化
  • 为什么拆箱枚举会产生奇怪的结果?

    考虑以下 Object box 5 int int int box int 5 int nullableInt box as int nullableInt 5 StringComparison enum StringComparison
  • strcmp 给出分段错误[重复]

    这个问题在这里已经有答案了 这是我的代码给出分段错误 include
  • 使用 C# 从 DateTime 获取日期

    愚蠢的问题 给定日期时间中的日期 我知道它是星期二 例如我如何知道它的 tue 2 和 mon 1 等 Thanks 您正在寻找星期几 http msdn microsoft com en us library system datetim
  • 使用 CSharpCodeProvider 类编译 C# 7.3 的 C# 编译器版本是什么?

    我想使用 Microsoft CSharp CSharpCodeProvider 类来编译 C 7 3 代码 编译器版本在 IDictionary 中指定 在创建新的 CSharpCodeProvider 时将其作为输入 例如 Compil
  • 带重定向标准流的 C# + telnet 进程立即退出

    我正在尝试用 C 做一个 脚本化 telnet 项目 有点类似于Tcl期望 http expect nist gov 我需要为其启动 telnet 进程并重定向 和处理 其 stdin stdout 流 问题是 生成的 telnet 进程在
  • 错误:无效使用不完整类型“类 Move”/未定义对 Move::NONE 的引用

    拜托 我不知道为什么这个简单的代码被拒绝 它给了我 2 个编译错误 请帮帮我 I use 代码 块 20 03 我的编译器是GNU GCC 移动 hpp class Move public Move Move int int public

随机推荐

  • Flash 消息无法正常工作express/nodejs/ejs

    闪存消息似乎不起作用 我想我错过了一些非常明显的东西 但我已经研究了一个小时 但我仍然不知道为什么它不起作用 我的中间件 Session middleware app use session secret stuffedbagels res
  • 通过嵌套 tf.map_fn 反向传播梯度

    我想在每个向量上映射一个 TensorFlow 函数 该向量对应于具有维度的矩阵中每个像素的深度通道 批量大小 H W n 通道 换句话说 对于每个尺寸的图像H x W我在批次中拥有 我提取一些特征图F k 其数量为n channels 具
  • 无法在主线程上启动处理程序

    我正在开发 jar api 以从 Unity3D 读取 Google Fit 数据 我现在面临的问题是 当我想执行这段代码时 private void buildFitnessClient mClient new GoogleApiClie
  • 强制用户在首次使用 Devise 登录时重置密码

    预计到达时间最后更新为我当前的解决方案 我希望能够为高价值用户手动创建帐户 这意味着我们必须为他们生成密码并让他们在首次登录时更改密码 我找到了执行此操作的解决方案here https stackoverflow com questions
  • Cython setup.py 找不到已安装的 Visual C++ 构建工具

    我正在尝试使用此 setup py 文件构建我的 cython 代码 from distutils core import setup from Cython Build import cythonize import numpy as n
  • C# Linq 在嵌套数组对象中查找特定项

    我正在使用 asp net core webapi 和 azure cosmosdb 开发一个应用程序 我需要从对象列表中找到一个项目 我对 linq 没有经验 在下面的 json 中 我需要找到一个拥有 learnerId 123 的扇区
  • php xpath 与 text() 和 SimpleXMLElement->xpath 不符合 xpath 预期结果

    我正在尝试获取 td span 的所有文本节点 我正在尝试使用 xpath td span text 问题是它返回每个文本元素的所有文本节点 这里有两个 193 和 120 它返回 193120 两次 而不是单独元素中的 193 和 120
  • 获取 numpy 稀疏矩阵行的范数

    我有一个通过使用 Sklearn 的 TfidfVectorizer 对象获得的稀疏矩阵 vect TfidfVectorizer sublinear tf True max df 0 5 analyzer word vocabulary
  • 如何有效地编码/解码压缩的位置描述?

    我正在为日本象棋变体编写一个表库 为了索引表基数 我将每个国际象棋位置编码为整数 在编码步骤之一中 我对棋盘上棋子的位置进行编码 由于实际方法有点复杂 我就简单地解释一下这个问题 编码 在残局桌面中 我有 比方说 六个不同的棋子 我想将它们
  • 可空类型装箱/拆箱 - 为什么要这样实现?

    通过 C 从 CLR 中提取有关装箱 拆箱值类型的信息 关于装箱 如果可空实例不是null CLR 从可为 null 的实例中取出值并将其装箱 换句话说可空 值为5被装箱成盒装 Int32值为 5 关于拆箱 拆箱只是获取对装箱对象的拆箱部分
  • 关闭 Matplotlib 数据[重复]

    这个问题在这里已经有答案了 我正在使用 Matplotlib 和 MPLD3 创建可以在 html 页面中显示的图形 使用 django 目前 我的图表是根据从 csv 文件中提取的数据动态生成的 我经常在终端中收到此消息 运行时警告 已打
  • Android 唯一序列号

    我正在开发一个针对 Android 4 0 API 14 及更高版本的 Android 应用程序 我正在寻找每个设备唯一且永久存在的序列号 随设备一起死亡 恢复出厂设置后不会更改 我在网上找到了很多关于 Android 设备唯一标识符的结果
  • 使用 AppCompat 的 SearchView

    我在使用appcompat v7之前在Actionbar中实现了SearchView 但是当我想将 SearchView 与支持库 v7 一起使用时 它显示 null 异常 In style
  • 在C中编写位图文件头时出现问题

    我正在尝试使用 C 创建一个新的位图文件 这是 bmp 文件头的结构 define uint16 unsigned short define uint32 unsigned long define uint8 unsigned char t
  • 无法在 osx 优胜美地上制作枪图。未定义的符号

    我尝试在 os x yosemite 10 10 4 下制作 gnuplot 5 0 0 但出现错误 make Applications Xcode app Contents Developer usr bin make all recur
  • 了解 Cocoa 和 Objective-C 的引用计数

    我刚刚开始了解 Objective C 和 Cocoa 希望能够使用 iPhone SDK 我对 C 相当满意malloc and free概念 但 Cocoa 的引用计数方案让我相当困惑 有人告诉我 一旦你理解了它 它就会非常优雅 但我只
  • Rails:调用 .limit(5) 更改结果顺序

    我有一个搜索功能 基本上运行模型记录的有序列表 问题是每当我打电话时 search limit 5 结果的顺序与我调用时的顺序不同 search 这是我的一些方法 def self search server name pvp type i
  • 错误:您正在传递未定义的模块!请检查您传递给 i18next.use() 的对象

    由于上述问题 我的单元测试失败了 String ts import as i18n from i18next import initReactI18next from react i18next import BrowserLanguage
  • OSStatus NSOSStatusErrorDomain

    当我使用获取该属性时收到以下错误 AudioSessionGetProperty kAudioSessionProperty CurrentHardwareSampleRate size myAudioDescription mSample
  • 解析 1 TB 文本并有效计算每个单词出现的次数

    最近 我遇到一个面试问题 用任何语言创建一个算法 该算法应该执行以下操作 读取 1 TB 内容 对该内容中每个重复出现的单词进行计数 列出最常出现的 10 个单词 您能让我知道为此创建算法的最佳方法吗 Edit 好吧 假设内容是英文的 我们