mapreduce面试问题
本章包含有关整数编码和压缩的许多详细信息。 由于这些主题并非直接与MapReduce有关,因此我对它们没有任何疑问。
4.4反向索引:修订的实现
说明反向索引检索算法。 您可以假设每个文档都适合内存。 假设还有大量的文档。 应该通过整数编码和压缩来优化哪一部分?
Input: text documents
key: document id
value: text document
输出:键/值对,其中
键:字
值:list [documentId,numberOfOccurences]列表元素必须按numberOfOccurences排序
映射器计算每个单词在文档中出现的次数。 由于整个文档都适合存储在内存中,因此我们可以将部分结果保存在地图中。
Intermediate key/values:
key: word, numberOfOccurences
value: documentId
自定义分区程序按单词对中间键/值进行分组。 自定义排序按单词对它们进行排序,按出现次数对它们进行排序。
Reducer使用initialize方法初始化所有发布的列表。 Reduce方法处理两种情况:
- 当前单词等于上一个单词–在发布列表中添加documentId和numberOfOccurences。
- 当前单词等于前一个单词–发出前一个单词并发布列表; 初始化发布列表。
reducer中的发布列表应该被压缩。
class MAPPER
method INITIALIZE
H = new hash map
method MAP(docid, doc d)
H = new hash map
for all term w in doc d do
H(w) = H(w) + 1
for all term w in H do
emit(pair(u, w), count 1)
method CLOSE
for all term w in H
emit(pair(w, H(w)), docid)
class REDUCER
variable previous_word = 0
variable PL = new list of postings
method REDUCE(pair (w, #occurrences), docid)
if w <> previous_word && previous_word <> 0 do
emit(w, PL)
PL = new list of postings
PL.add(pair(#occurrences, docid))
previous_word = w
method compare(key (w1, o1), key (w2, o2))
if w1 = w2
return keys are equal
return keys are different
class SORTING_COMPARATOR
method compare(key (w1, o1), key (w2, o2))
if w1 = w2 do
return compare(o1, o2)
return compare(w1, w2)
5种图算法
本章包含两种算法:图形中的最短路径和页面排名算法。 问题很简单。
5.2并行广度优先搜索
查找从一个节点origin
到所有其他节点的最短路径。 每个边缘都有一个权重。 输入键/值对已经被bean预处理成舒适的形式。
Input: graph
key: node id
value: distance to origin, list[adjacent node, edge length]
输出:键/值对,其中
键:节点ID
值:到原点的距离,列表[相邻节点,边长]
该算法需要多次迭代。 停止迭代不会更改任何“到原点的距离”。 最坏的情况是会有O(n)次迭代,其中n是图中的节点数。
映射器将原始图原样传递给下一个迭代。 另外,它为每个相邻节点生成键/值对。 如果路线将通过节点,则该值包含距原点的最小已知距离。
class MAPPER
method MAP(node, pair(dist, adjacencylist))
emit(node, pair(dist, adjacencylist))
for all (closenode, nodedist) in adjacencylist do
emit(closenode, pair(dist + nodedist, empty))
减速器找到距每个节点的最小已知距离。 它将距离与原始图形一起传递到下一个迭代。 每当与任何节点的最小已知距离发生变化时,它也会增加全局计数器。
class REDUCER
method REDUCE(node, list(dist, adjacencylist))
minimum = infinity
previous_iteration_solution = infinity
original_graph = empty
for all (dist, adjacencylist) in list do
if adjacencylist not empty do
original_graph = adjacencylist
previous_iteration_solution = dist
if minimum > dist
minimum = dist
if previous_iteration_solution <> minimum
increment global counter
emit(node, pair(minimum, original_graph))
如果全局计数器为0,则算法停止。 否则需要另一个迭代。
说明页面等级算法,假设alpha = 0。
网页排名P(n)
页面的n
计算链接到它的所有页面的表单页面行列。
P(n) = sum_m (P(m)/C(m))
该总和经过链接到页面n
所有页面m
。 C(m)
是页面m
的传出链接数。
页面等级算法以迭代方式运行。 映射器将每个页面的页面排名贡献传递给相邻页面。 Reducer更新每个节点的页面等级。 当页面排名不再移动时,该算法停止。
class MAPPER
method MAP(page, (page_rank, adjacency_list))
emit(page, (0, adjacency_list))
contribution = page_rank/adjacency_list.length
for all node in adjacency_list do
emit(node, (contribution, empty))
class REDUCER
method REDUCE(page, contributions[c1, c2, ..., cn])
rank = 0
adjacency_list = new list
for all c in contributions do
adjacency_list.addAll(c.adjacency_list)
rank = rank + c.contribution
emit(page, (rank, adjacency_list))
6种用于文本处理的EM算法
在本章中,我没有提出任何疑问。
练习题
本章包含MapReduce的动手练习。 其中一些需要多次迭代。
暖身
计算文本集合中每个单词的出现次数。
Input:
key: document id,
value: text document.
输出:
关键:字,
值:发生的次数。
Intermediate pairs:
key: word
value: integer - how many times was the word seen in the input.
class MAPPER
method MAP(docid a, doc d)
for all term w in doc d do
emit(w, 1)
class COMBINER
method COMBINE(word w, counts[c1, c2, ..., cn])
s = 0
for all c in counts[c1, c2, ..., cn] do
s = s + c
emit(word w, s)
class REDUCER
variable total_occurrences = 0
method REDUCE(word w, counts[c1, c2, ..., cn])
s = 0
for all c in counts[c1, c2, ..., cn] do
s = s + c
emit(word w, s)
替代解决方案将使用映射器内合并。
网上商店
网站用户日志包含用户ID和每个会话的时长。 该网站的注册用户数量很少。 计算每个用户的平均会话长度。
Input:
key: user id,
value: session length.
输出:
密钥:用户ID,
值:平均会话长度。
由于注册用户数量不多,我们可以使用映射器内合并。
class MAPPER
variable total_time = new hash map
variable sessions_number = new hash map
method MAP(user_id, session_length)
total_time(user_id) = total_time(user_id) + session_length
sessions_number(user_id) = sessions_number(user_id) + 1
method CLOSE
for all user_id in total_logged_in_time
tt = total_time(user_id)
sn = sessions_number(user_id)
emit(user_id, pair(tt, sn))
class REDUCER
method REDUCE(user_id, [pairs(time, sessions_number)])
total_time = 0
total_sessions = 0
for all pairs in [pairs(time, sessions_number)] do
total_time = total_time + time
total_sessions = total_sessions + sessions_number
emit(user_id, total_time/total_sessions)
Web商店日志包含用户ID和每次销售的已购买商品。 您需要实现“项目购买者也已购买”功能。 每当显示商品时,商店都会建议商品买家最常购买的五种商品。
Input:
key: user id,
value: brought item.
输出:
键:项目,
价值:五个最常见的“物品购买者也已购买”物品清单。
我们的解决方案有两个迭代。 第一次迭代将生成同一用户带来的所有项目的列表。 分组是由框架完成的,映射器和约简器均执行标识功能。
Input:
key: user id,
value: brought item.
输出:
密钥:用户ID,
价值:所有带来的物品清单。
class MAPPER
method MAP(user_id, item)
emit(user_id, item)
class REDUCER
method REDUCE(user_id, items[i1, i2, ..., in])
emit(user_id, items)
第二次迭代解决了列表项上的共现问题。 它使用条纹方法。 与标准解决方案的唯一区别是,我们仅发出了五个最常见的共现。
Input:
key: user id,
value: list of all brought items.
输出:
键:项目,
值:五个最常见的同时出现的列表。
class MAPPER
method MAP(user_id, items[i1, i2, ..., in])
for all item in items do
H = new hash map
for all item j in items do
H(j) = H(j) + 1
emit(item, H)
class REDUCER
method REDUCE(item, stripes[H1, H2, ..., Hn])
T = new hash map
for all H in stripes do
for all (key/value) in H do
T(key) = T(key) + value
emit(user_id, max_five(T))
网上商店日志包含每次销售的用户ID,时间戳,物品和带入件数。 商店正在寻找销售额同时上升或下降的商品。 查找最多20个这样的月数的夫妻。
Input:
key: user id,
value: timestamp, brought item, count.
输出:
键:项目,项目
价值:两个项目的销售额上升或下降的月数。 #:输出包含20个键/值对,最大值
我们的解决方案需要多次MapReduce迭代。 我们必须:
- 计算给定月份的商品销售额是上升还是下降,
- 创建当月销售额变化相同的商品清单,
- 在这些列表中找到并发次数,
- 选择同时出现次数最多的项目。
第一次迭代计算任何给定月份的销售变化。 我们必须提供映射器,分区器,自定义排序和归约器。 映射器为每个输入键/值生成一个中间键/值对。 密钥由已售商品和销售月份组成。 价值包含已售件数。
分区程序将具有相同项目的所有键/值对发送到同一reducer。 自定义排序按月对它们进行排序。 最后,reducer计算销售变化。
Input:
key: user id,
value: timestamp, item, count.
中间键/值:
键:项目,月份
值:计数。
输出:
键:月,上/下/等
值:项目。
class MAPPER
method MAP(user_id, (timestamp, item, count))
month = get_month(timestamp)
emit((item, month), count)
class PARTITIONING_COMPARATOR
method compare(key (item1, month1), key (item2, month2))
if item1 = item2
return keys are equal
return keys are different
class SORTING_COMPARATOR
method compare(key (item1, month1), key (item2, month2))
if item1 = item2 do
return compare(month1, month2)
return compare(item1, item2)
class REDUCER
method REDUCE((item, month), counts[c1, c2, ..., cn])
c = sum([c1, c2, ..., cn])
if last_item = item
if last_month + 1 = month
//emit correct up/down/equal flags
if last_count < count
emit((item, month), up)
if last_count > count
emit((item, month), down)
if last_count = count
emit((item, month), equal)
else
//no sales during some months
emit((item, last_month + 1), down)
emit((item, month), up)
else
// new item
emit((last_item, last_month + 1), down)
emit((item, month), up)
last_item = item
last_count = count
last_month = month
第二次迭代通过键将第一次迭代结果分组。 它生成当月销售额变化相同的物料清单。 框架可以完成所有工作。 映射器和缩减器均执行标识功能。
Input:
key: month, up/down/equal
value: item.
输出:
键:月,上/下/等
值:[项目]。 第三迭代执行标准的“成对共现”算法。
Input:
key: month, up/down/equal
value: [items].
中间键/值:
键:项目,项目
值:同时出现的部分次数。
输出:
键:项目,项目
价值:两个项目的销售额上升或下降的月数。 #:输出包含所有项目对
class MAPPER
method MAP((month, change), items[i1, i2, ..., in])
for each i in items do
for each j in items do
if i != j
emit((i, j), 1)
class COMBINER
method COMBINE((item1, item2), co-occurrences[c1, c2, ..., cn])
s = 0
for all c in co-occurrences[c1, c2, ..., cn] do
s = s + c
emit((item1, item2), s)
class REDUCER
method REDUCE((item, item), co-occurrences[c1, c2, ..., cn])
s = 0
for all c in co-occurrences[c1, c2, ..., cn] do
s = s + c
emit((item1, item2), s)
最后,我们必须选择20个具有最大值的键/值对。 每个映射器选择具有最大值的20个键/值对,并使用相同的键发出它们。 只有一个精简器可以选择最终的20个键/值对。
Input:
key: item, item
value: number of months when both items sales rose or decline.
中间键/值:
密钥:1
值:项目,项目,两个项目的销售额增加或减少的月数。 #:输出包含20个键/值对,每个映射器都有最大值
输出:
键:项目,项目
价值:两个项目的销售额上升或下降的月数。 #:输出包含20个键/值对,最大值
the code is very simple but long
刑事机构
本章所有练习的输入都使用相同的数据结构。
犯罪机构偷走了Facebook的友谊数据库,并希望分析新数据。 友谊以键/值对的形式存储,每个友谊对应于两个键/值对:
Friends:
key: first friend name
value: second friend name
键:第二个朋友的名字
值:第一个朋友的名字
该机构还拥有所有公民的犯罪记录:
Criminal record:
key: citizen name
value: when, where, accomplices, description
寻找处于危险中的年轻人。 如果一个人的朋友中有一半以上有犯罪记录,则该人被视为处于危险中的年轻人。
我们的解决方案有两个迭代。 第一次迭代将连接两个集合,并使用has_record / law_abiding标志标记每个“有价值的朋友”。
Output:
key: first friend
value: second friend, has_record/law_abiding
映射器用数据集名称标记每个键。 分区程序根据密钥中的名称对数据进行分组,而排序程序则将犯罪记录放在友谊之前。 我们可以使用本地汇总来删除同一个人的多个犯罪记录。
class MAPPER
method MAP(name, value)
if value is name do
emit(name, friendship, item)
else
emit(name, criminal, item)
class PARTITIONING_COMPARATOR
method compare(key (name1, dataset1), key (name2, dataset2))
if name1 = name2
return keys are equal
return keys are different
class SORTING_COMPARATOR
method compare(key (name1, dataset1), key (name2, dataset2))
if name1 = name2 AND dataset1 is criminal
return key1 is lower
if name1 = name2 AND dataset2 is criminal
return key2 is lower
return compare(name1, name2)
class REDUCER
variable previous_name
method REDUCE(pair(name, flag), items[i1, i2, ..., in])
if flag is criminal do
previous_name = name
has_record = criminal
return
if previous_name <> name do
has_record = law_abiding
else
has_record = criminal
previous_name = name
for all i in items do
emit(i.name, pair(name, has_record))
第二次迭代同时计算朋友总数和具有犯罪记录的朋友数量。 Reducer仅针对有风险的年轻人发出键/值对。 同样,此迭代可以使用某种本地聚合。
Intermediate key/value:
key: name
value: total friends, total friend criminals
输出:
键:名称
值:空 #只有处于危险中的年轻人
class MAPPER
method MAP(name, pair(name, has_record))
if has_record is law_abiding do
emit(name, pair(0, 1))
else
emit(name, pair(1, 1))
class REDUCER
method REDUCE(name, items[pair(total, criminals)])
total = 0
criminals = 0
for all i in items do
total = total + i.total
criminals = criminals + i.criminals
if criminals / total > 0.5 do
emit(name, empty)
查找帮派。 帮派成员是:
- 正好有5位成员,
- 每个成员都是其他成员的朋友,
- 每两名成员共同犯下至少3项罪行。
同样,我们需要三个迭代。 这个想法是首先清理所有无用边的图形,以便仅保留犯罪联系。 然后,我们将图分成较小的可管理子图。 我们将每个人之间的所有犯罪联系和边缘联系在一起:
Last iteration reducers input:
key: person
values: all his criminal contacts and relationships between them.
最终归约器采用每个键/值对中由值表示的较小图,并找到其中具有4个顶点的完整子图。 从其中的关键点添加一个人,您已经找到了具有5个顶点的完整子图。 约简器可以使用任何多项式算法。
第一次迭代使用对方法来清除图形。 我们省略了本地聚合和重复项的删除。 两者都将使算法更有效。
Intermediate key/values:
key: first friend, second friend, friendship/accomplice
value: 1
输出:
关键:第一位朋友,第二位朋友
值:空 #拥有共同犯罪记录的唯一朋友
class MAPPER
method MAP(name, value)
if value is name do
emit(triple(name, value, friendship), empty)
else
for all crime_accomplice in value.accomplices do
emit(triple(name, crime_accomplice, accomplice), 1)
class PARTITIONING_COMPARATOR
method compare(key (name1, accomplice1, flag1), key (name2, accomplice2, flag2))
if name1 = name2 AND accomplice1 = accomplice2
return keys are equal
return keys are different
class SORTING_COMPARATOR
method compare(key (name1, accomplice1, flag1), key (name2, accomplice2, flag2))
if name1 = name2 AND accomplice1 AND flag1 is friendship
return key1 is lower
if name1 = name2 AND accomplice1 AND flag2 is friendship
return key2 is lower
return compare(pair(name1, accomplice1), pair(name2, accomplice2))
class REDUCER
variable previous_name
variable previous_accomplice
method sameAsPrevious(name, accomplice)
if previous_name <> name
return false
if previous_accomplice <> accomplice
return false
return true
method REDUCE(triple(name, accomplice, flag), items[i1, i2, ..., in])
if sameAsPrevious(name, accomplice) do
if items.length > 2 do
emit(name, accomplice)
return
if flag is friendship do
previous_name = name
previous_accomplice = accomplice
第二次迭代将所有“第二学位”朋友的列表附加到边缘:
Input
key: first friend, second friend
value: empty
关键:第二个朋友
值:第一个朋友,第二个朋友
输出:
关键:第一位朋友,第二位朋友
值:第二个朋友的所有朋友
关键:第二个朋友,第一个朋友
值:第一个朋友的所有朋友
class MAPPER
method MAP((first friend, second friend), empty)
emit(first friend, (first friend, second friend))
emit(second friend, (first friend, second friend))
class REDUCER
method REDUCE(name, edges[e1, e2, ..., en])
friends = new Set
friends.add(name)
for all edge in edges do
friends.add(edge.v1, edge.v2)
for all edge in edges do
emit(edge, friends)
最后,mapper和shuffle以及sort阶段一起生成任何给定人员的所有朋友以及他们之间的关系的列表。
Input
key: friend 1, friend 2
value: all friends of friend 2
中间键/值:
键:朋友1
值:朋友2,朋友2的所有朋友
减速器输入(经过随机排序后):
关键人物
价值观:他所有的朋友和他们之间的关系。
输出:
关键:第一位朋友,第二位朋友,第三位朋友,第四位朋友,第五位朋友
值:帮派
class MAPPER
method MAP((friend , friend 2), all friends of second friend)
emit(friend 1, (friend 2, all friends of friend 2))
class REDUCER
method REDUCE(name, graph attached to it)
any polynomial algorithm will work
参考:我们的JCG合作伙伴 Maria Jurcovicova在This is Stuff博客上的MapReduce问答 。
翻译自: https://www.javacodegeeks.com/2012/05/mapreduce-questions-and-answers-part-2.html
mapreduce面试问题