使用MapReduce实现矩阵相乘

news/2025/3/18 10:23:14/文章来源:https://www.cnblogs.com/Mount256/p/18778410

目录
  • 实现代码
    • 实现思路
    • 阶段一:部分乘积计算
    • 阶段二:聚合求和
    • 输入格式要求
    • 运行流程
    • 注意事项
    • 关键概念图解
  • 具体例子详细跟踪程序的执行过程
    • 输入文件格式
    • 阶段一执行过程
      • Mapper处理流程
      • Reducer处理过程
      • 阶段一最终输出
    • 阶段二执行过程
      • Mapper处理(直接传递)
      • Reducer聚合求和
    • 最终输出结果
    • 可视化数据流向

实现代码

使用Hadoop的MapReduce实现矩阵相乘是可行的,但需要合理设计数据分发和处理逻辑。以下是基于两阶段MapReduce的实现思路:

实现思路

  1. 阶段一:计算每个k对应的部分乘积。
  2. 阶段二:聚合所有部分乘积,得到最终结果矩阵。

阶段一:部分乘积计算

Mapper

  • 处理矩阵A的元素,输出键为列号k,值为(A, 行号i, 值)。
  • 处理矩阵B的元素,输出键为行号k,值为(B, 列号j, 值)。

Reducer

  • 对每个k,收集所有A的行i的值和B的列j的值。
  • 计算笛卡尔积,输出键为(i,j),值为A[i][k] * B[k][j]。

示例代码

/*** 第一阶段Mapper:负责分发矩阵元素到Reducer*/
public class Phase1Mapper extends Mapper<Object, Text, Text, Text> {// 输入值格式示例:// 矩阵A元素:A,行号i,列号k,值 如 "A,2,3,5.0"// 矩阵B元素:B,行号k,列号j,值 如 "B,3,4,2.0"public void map(Object key, Text value, Context context)throws IOException, InterruptedException {// 将输入文本按逗号分割String[] tokens = value.toString().split(",");String matrix = tokens[0]; // 获取矩阵标识(A或B)if (matrix.equals("A")) {// 处理矩阵A的元素int i = Integer.parseInt(tokens[1]); // 行号iint k = Integer.parseInt(tokens[2]); // 列号k(即中间维度)double aVal = Double.parseDouble(tokens[3]); // 元素值// 输出键为k,值为"A,行号i,值"(通过k进行分组)// 示例:key="3", value="A,2,5.0"context.write(new Text(String.valueOf(k)), new Text("A," + i + "," + aVal));} else if (matrix.equals("B")) {// 处理矩阵B的元素int k = Integer.parseInt(tokens[1]); // 行号k(即中间维度)int j = Integer.parseInt(tokens[2]); // 列号jdouble bVal = Double.parseDouble(tokens[3]); // 元素值// 输出键为k,值为"B,列号j,值"// 示例:key="3", value="B,4,2.0"context.write(new Text(String.valueOf(k)), new Text("B," + j + "," + bVal));}}
}/*** 第一阶段Reducer:计算部分乘积*/
public class Phase1Reducer extends Reducer<Text, Text, Text, DoubleWritable> {public void reduce(Text key, Iterable<Text> values, Context context)throws IOException, InterruptedException {// 存储来自矩阵A和B的元素List<String> aEntries = new ArrayList<>(); // 格式:i,valueList<String> bEntries = new ArrayList<>(); // 格式:j,value// 分类处理来自不同矩阵的值for (Text val : values) {String[] parts = val.toString().split(",");if (parts[0].equals("A")) {// A矩阵元素:格式为 A,i,valueaEntries.add(parts[1] + "," + parts[2]); // 保存i和值} else {// B矩阵元素:格式为 B,j,valuebEntries.add(parts[1] + "," + parts[2]); // 保存j和值}}// 计算笛卡尔积(所有i和j的组合)for (String a : aEntries) {String[] aParts = a.split(",");int i = Integer.parseInt(aParts[0]); // 矩阵A的行号double aVal = Double.parseDouble(aParts[1]); // 矩阵A的值for (String b : bEntries) {String[] bParts = b.split(",");int j = Integer.parseInt(bParts[0]); // 矩阵B的列号double bVal = Double.parseDouble(bParts[1]); // 矩阵B的值// 计算乘积并输出到第二阶段double product = aVal * bVal;// 输出键为(i,j),值为乘积结果// 示例:key="2,4", value=10.0context.write(new Text(i + "," + j), new DoubleWritable(product));}}}
}

阶段二:聚合求和

Mapper:直接传递键值对。
Reducer:对相同(i,j)键的值求和。

示例代码

/*** 第二阶段Mapper(恒等映射):直接传递键值对* 输入格式:i,j    productValue*/
public class Phase2Mapper extends Mapper<Object, Text, Text, DoubleWritable> {public void map(Object key, Text value, Context context)throws IOException, InterruptedException {// 将输入行分割为键值对(格式:i,j\tproduct)String[] parts = value.toString().split("\t");String outputKey = parts[0]; // 保持键不变double outputValue = Double.parseDouble(parts[1]);// 直接传递键值对context.write(new Text(outputKey), new DoubleWritable(outputValue));}
}/*** 第二阶段Reducer:求和得到最终结果*/
public class Phase2Reducer extends Reducer<Text, DoubleWritable, Text, DoubleWritable> {public void reduce(Text key, Iterable<DoubleWritable> values, Context context)throws IOException, InterruptedException {double sum = 0.0;// 对相同(i,j)的所有部分乘积求和for (DoubleWritable val : values) {sum += val.get(); // 累加值}// 输出最终结果context.write(key, new DoubleWritable(sum));}
}

输入格式要求

// 矩阵A的每个元素需要按以下格式存储:
A,i,k,value  
// 例如:A,0,2,3.14 表示A[0][2] = 3.14// 矩阵B的每个元素需要按以下格式存储:
B,k,j,value  
// 例如:B,2,5,6.28 表示B[2][5] = 6.28// 文件示例:
A,0,0,1.0
A,0,1,2.0
B,0,0,3.0
B,1,0,4.0

运行流程

  1. 输入格式:矩阵A和B以特定格式存储,如A,i,k,valueB,k,j,value
  2. 执行阶段一:计算每个k对应的部分乘积。
  3. 执行阶段二:对部分乘积求和,得到最终结果。

注意事项

  • 性能问题:阶段一的笛卡尔积可能导致中间数据量爆炸,适用于小规模矩阵或稀疏矩阵。
  • 优化:可通过矩阵分块、Combiner优化或调整数据分发策略来减少数据传输。

关键概念图解

矩阵相乘原理:
C[i][j] = Σ(A[i][k] * B[k][j]) ,对k从1到n求和MapReduce数据流:
矩阵A元素 (i,k) → 通过k分发 → Reducer
矩阵B元素 (k,j) → 通过k分发 → Reducer
Reducer内计算所有i和j的组合乘积阶段一输出:
(0,0) → 1 * 3 + 2 * 4 = 11
(0,1) → 1 * 5 + 2 * 6 = 17
...(其他元素同理)阶段二输出:
(0,0) 11
(0,1) 17
...(最终结果矩阵)

通过上述方法,可以利用Hadoop的MapReduce实现矩阵相乘,虽然存在性能限制,但理论上是可行的。

具体例子详细跟踪程序的执行过程

让我们通过一个具体的2x2矩阵相乘示例,详细跟踪程序的执行过程。假设:

矩阵A:

[1 2]
[3 4]

矩阵B:

[5 6]
[7 8]

最终结果C = A×B 应该是:

[1×5+2×7=19, 1×6+2×8=22]
[3×5+4×7=43, 3×6+4×8=50]

输入文件格式

文件内容(假设存储为input/matrix.txt):

A,0,0,1.0
A,0,1,2.0
A,1,0,3.0
A,1,1,4.0
B,0,0,5.0
B,0,1,6.0
B,1,0,7.0
B,1,1,8.0

阶段一执行过程

Mapper处理流程

每个Mapper会处理一行输入:

输入行 输出键 输出值 说明
A,0,0,1.0 "0" "A,0,1.0" 矩阵A第0行第0列元素
A,0,1,2.0 "1" "A,0,2.0" 矩阵A第0行第1列元素
A,1,0,3.0 "0" "A,1,3.0" 矩阵A第1行第0列元素
A,1,1,4.0 "1" "A,1,4.0" 矩阵A第1行第1列元素
B,0,0,5.0 "0" "B,0,5.0" 矩阵B第0行第0列元素
B,0,1,6.0 "0" "B,1,6.0" 矩阵B第0行第1列元素
B,1,0,7.0 "1" "B,0,7.0" 矩阵B第1行第0列元素
B,1,1,8.0 "1" "B,1,8.0" 矩阵B第1行第1列元素

Reducer处理过程

按key分组后:

Reducer处理key="0"(k=0)时:

  • 收到的values列表:
    ["A,0,1.0", "A,1,3.0", "B,0,5.0", "B,1,6.0"]
    
  • 分类存储:
    aEntries = ["0,1.0", "1,3.0"]  // 矩阵A第0列元素(即k=0)
    bEntries = ["0,5.0", "1,6.0"]  // 矩阵B第0行元素(即k=0)
    
  • 计算笛卡尔积:
    (0,0) → 1.0*5.0 = 5.0
    (0,1) → 1.0*6.0 = 6.0
    (1,0) → 3.0*5.0 = 15.0
    (1,1) → 3.0*6.0 = 18.0
    

Reducer处理key="1"(k=1)时:

  • 收到的values列表:
    ["A,0,2.0", "A,1,4.0", "B,0,7.0", "B,1,8.0"]
    
  • 分类存储:
    aEntries = ["0,2.0", "1,4.0"]  // 矩阵A第1列元素(k=1)
    bEntries = ["0,7.0", "1,8.0"]  // 矩阵B第1行元素(k=1)
    
  • 计算笛卡尔积:
    (0,0) → 2.0*7.0 = 14.0
    (0,1) → 2.0*8.0 = 16.0
    (1,0) → 4.0*7.0 = 28.0
    (1,1) → 4.0*8.0 = 32.0
    

阶段一最终输出

0,0 → 5.0
0,1 → 6.0
1,0 → 15.0
1,1 → 18.0
0,0 → 14.0
0,1 → 16.0
1,0 → 28.0
1,1 → 32.0

阶段二执行过程

Mapper处理(直接传递)

输入来自阶段一的输出,Mapper不做处理,直接传递:

(0,0) → 5.0
(0,1) → 6.0
(1,0) → 15.0
...(其他值同理)

Reducer聚合求和

按(i,j)分组求和:

处理(0,0):

  • 值列表:[5.0, 14.0]
  • 求和:5.0 + 14.0 = 19.0

处理(0,1):

  • 值列表:[6.0, 16.0]
  • 求和:6.0 + 16.0 = 22.0

处理(1,0):

  • 值列表:[15.0, 28.0]
  • 求和:15.0 + 28.0 = 43.0

处理(1,1):

  • 值列表:[18.0, 32.0]
  • 求和:18.0 + 32.0 = 50.0

最终输出结果

0,0 → 19.0
0,1 → 22.0
1,0 → 43.0
1,1 → 50.0

可视化数据流向

矩阵A元素分布:
k=0 → A(0,0)=1.0, A(1,0)=3.0
k=1 → A(0,1)=2.0, A(1,1)=4.0矩阵B元素分布:
k=0 → B(0,0)=5.0, B(0,1)=6.0
k=1 → B(1,0)=7.0, B(1,1)=8.0阶段一Reducer处理:
k=0时:(i=0) * (j=0) → 1*5=5(i=0) * (j=1) → 1*6=6(i=1) * (j=0) → 3*5=15(i=1) * (j=1) → 3*6=18k=1时:(i=0) * (j=0) → 2*7=14(i=0) * (j=1) → 2*8=16(i=1) * (j=0) → 4*7=28(i=1) * (j=1) → 4*8=32阶段二聚合:
C[0][0] = 5 + 14 = 19
C[0][1] = 6 + 16 = 22
C[1][0] = 15 + 28 = 43
C[1][1] = 18 + 32 = 50

通过这个具体案例,可以清晰看到:

  1. 数据如何通过k进行分组
  2. Reducer如何计算部分乘积
  3. 两阶段MapReduce如何协作完成矩阵相乘

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/900821.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关闭 WSL 中正在运行的 Linux 发行版

你使用 WSL 在 Windows 内运行 Linux 吗?你想知道如何关闭在 WSL 中运行的 Linux 发行版吗? 你当然可以在 WSL 中运行的 Linux 系统中 执行 shutdown 命令:sudo shutdown now你还可以使用 wsl 命令关闭 Linux 系统。如果你有多个发行版在 WSL 中运行,这是一种极好的方法。 …

windows如何调出剪贴板所有复制过的内容?

前言 大家好,我是小徐啊。我们在开发Java应用的时候,经常是需要复制粘贴的。我们在windows上面开发的时候,默认都是复制后,就把之前的复制的内容替换了。这就导致我们的复制粘贴很不方便,其实,windows可以支持我们显示最近所有的复制内容的,具体怎么做呢?文末附快捷键方…

AI与.NET技术实操系列(七):使用Emgu CV进行计算机视觉操作

引言 计算机视觉(Computer Vision, CV)是人工智能领域中最为引人注目的分支之一。从自动驾驶汽车到医疗影像分析,从智能安防系统到虚拟现实体验,计算机视觉的应用无处不在,深刻地改变着我们的生活和工作方式。 对于.NET开发者而言,掌握计算机视觉技术不仅意味着能够开发出…

路由器安全研究:D-Link DIR-823G v1.02 B05 复现与利用思路

D-Link DIR-823G v1.02 B05存在命令注入漏洞,攻击者可以通过POST的方式往 /HNAP1发送精心构造的请求,执行任意的操作系统命令。前言 D-Link DIR-823G v1.02 B05存在命令注入漏洞,攻击者可以通过POST的方式往 /HNAP1发送精心构造的请求,执行任意的操作系统命令。 漏洞分析bi…

北斗卫星时钟源,安徽京准助力国产时间精准度

北斗卫星时钟源,安徽京准助力国产时间精准度北斗卫星时钟源,安徽京准助力国产时间精准度 北斗卫星时钟源,安徽京准助力国产时间精准度 京准电钟官微——ahjzsz 北斗卫星时钟源作为中国自主研发的全球卫星导航系统的重要组成部分,其时间精准度的提升依赖于技术创新和系统优化…

ASE60N30-ASEMI工业自动化专用ASE60N30

ASE60N30-ASEMI工业自动化专用ASE60N30编辑:LL ASE60N30-ASEMI工业自动化专用ASE60N30 型号:ASE60N30 品牌:ASEMI 封装:TO-247 最大漏源电流:60A 漏源击穿电压:300V 批号:最新 RDS(ON)Max:38mΩ 引脚数量:3 沟道类型:N沟道MOS管 封装尺寸:如图 特性:MOS管、N沟道…

1周上线,2个月交付!有巢数智如何用 NocoBase 颠覆建筑行业数智化效率

有巢数智利用 NocoBase 平台,在建筑行业实现了数智化转型。 他们在 1 周内上线,2 个月内交付项目,显著提升了项目交付效率。 了解他们如何通过 NocoBase 的插件化架构,快速构建和调整功能,以满足复杂的业务需求。原文链接:https://www.nocobase.com/cn/blog/rapid-develo…

GoT:基于思维链的语义-空间推理框架为视觉生成注入思维能力

文探讨GoT框架如何通过语义-空间思维链方法提升图像生成的精确性与一致性 计算机视觉领域正经历一次技术革新:一种不仅能将文本转换为图像,还能在生成过程中实施结构化推理的系统。这一系统即为GoT(Generative Thoughts of Thinking,生成式思维链)框架——一种将显式推理机…

极速突破,PolarDB MySQL 列存索引加速复杂查询,完成任务可领取200社区积分!

「技术解决方案【Cloud Up 挑战赛】」上线了! 在电商行业,商品筛选页是用户找理想商品的关键,但商品数量激增、用户需求多样使现有筛选页面面临挑战,庞大商品数据需实时处理致系统性能要求高,用户筛选排序要求复杂使查询请求变复杂,传统数据库查询方式存在响应慢、延迟高…

2025-03-18 实践目标:numpy pandas matplotlib

python环境:Python 3.13.2 参考网络视频:https://www.bilibili.com/video/BV1KM4y1b7sC?spm_id_from=333.788.player.switch&vd_source=3bdaecff10bd344788cc194461374709&p=2pip install jupyter pip install numpyjupyter#本地运行编辑器 (文本,代码,绘图) j…

配置 pip 镜像源

配置 pip 镜像源配置 pip 镜像源 要配置 pip 使用国内的镜像源以加速包的下载,可以按照以下步骤操作: 1. 临时使用镜像源 在安装包时,可以通过 -i 参数临时指定镜像源。例如: pip install 包名 -i https://pypi.tuna.tsinghua.edu.cn/simple常用的国内镜像源有:清华大学:…

通义灵码插件使用指南(JetBrains IDE / Windows)

目录‌环境要求‌ ‌安装步骤‌ ‌基础功能使用‌3.1 代码续写与补全 3.2 生成代码注释 3.3 编写单元测试 3.4 解释代码含义‌高级功能配置与使用‌4.1 模型选择与切换 4.2 代码优化 4.3 智能问答 4.4 异常排查‌常见问题解答‌ ‌支持与反馈‌1. 环境要求‌操作系统‌: Window…