二维数组创建方式比较
暑假跟着地质队去跑山了,到现在还没结束,今天休息的时候突然刷到了一篇关于C++二维数组创建方面的文章,我觉得还是非常不错滴,就将其中提到的新方法和我已经使用过的三种方法进行了比较,发现该方法提高了二维数组的分配、访存和运算速度,我用了四种方法:1、常规动态二维数组声明、2、新方法动态二维数组声明、3、Vector容器创建方式1、4、Vector容器创建方式2。
测试数组大小都是20000*20000,这里只展示了其中的5*5的切片,我会先讲各种方法的原理,最后展示各方法的运行时长。
四个函数的功能都是计算直角三角形斜边长。
目录
1、常规动态二维数组声明
2、新方法动态二维数组声明
3、Vector容器声明方法一
4、Vector容器创建方法二
5、绘图解释
6、对比结果
7、程序代码
1、常规动态二维数组声明
这个方式就是最简单也是最常用的方式,先说明一个一维的指针数组,然后每个指针在指向一个数组的地址,就行了,访存则是先找到A[i][j]所对应的指针A[i],再找A[i][j]就行,更通俗的理解就是,这种逐行分配的方法会使得上一行和下一行的地址是不连续的,寻址效率会降低,这里给出代码:
void originalDynamic(int n){clock_t start = clock();double** a = new double* [n];double** b = new double* [n];double** c = new double* [n];for (int i = 0; i < n; i++) {a[i] = new double[n]();b[i] = new double[n]();c[i] = new double[n]();}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {a[i][j] = 3.0;b[i][j] = 4.0;c[i][j] = 0.0;}}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {c[i][j] = sqrt(powl(a[i][j],2) + powl(b[i][j],2));}}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {cout << c[i][j] << '\t';}cout << endl;}clock_t end = clock();cout << " Original time:" << (end - start)/CLOCKS_PER_SEC << endl;
}
2、新方法动态二维数组声明
出现这个方法的原因是这样的,方法一的地址实际上是不连续的,也就是说访存过程会浪费额外时间,通过将数组地址连续化,就可以节省访存时间,进而提高访存效率。
先贴代码:
void newDynamic(int n){clock_t start = clock();double** a = new double* [n];double** b = new double* [n];double** c = new double* [n];a[0] = new double[n * n];b[0] = new double[n * n];c[0] = new double[n * n];for (int i = 1; i < n; i++) {a[i] = a[0] + i * n;b[i] = b[0] + i * n;c[i] = c[0] + i * n;}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {a[i][j] = 3.0;b[i][j] = 4.0;c[i][j] = 0.0;}}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {c[i][j] = sqrt(powl(a[i][j], 2) + powl(b[i][j], 2));}}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {cout << c[i][j] << '\t';}cout << endl;}clock_t end = clock();cout << "New time:" << (end - start)/CLOCKS_PER_SEC << endl;
}
只需要看6-13行就行,可以看到再第一行的首地址申请空间的时候就把整个二维数组的空间全部申请了,再把其余隔行都依次对应到首地址之后的地址,这样就使得整个数组的地址连续化,从而提高了访存效率。咋说呢,我觉得这个有点像C++中一维数组变二维数组,看代码的逻辑上也很像:
//一维数组做二维数组使用:
double *array1=new double[n*n];
//二维数组的下标对应方式就是:
array[i][j]=array1[i*row+j];
上述的这个方式我之前用CUDA做东西的时候用的很多,今天这个二维数组声明方法实际上就是上述的一维数组做二维数组方法的变形。
3、Vector容器声明方法一
个人觉得这个方法是Vector数组声明方法中最简单的,也是最高效的一种,这里就不讲什么原理了,会使用就好:
void usingVectorway1(int n){clock_t start = clock();vector<vector<double>>a(n, vector<double>(n, 3.0));vector<vector<double>>b(n, vector<double>(n, 4.0));vector<vector<double>>c(n, vector<double>(n, 0.0));for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {c[i][j] = sqrt(powl(a[i][j], 2) + powl(b[i][j], 2));}}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {cout << c[i][j] << '\t';}cout << endl;}clock_t end = clock();cout << "vector Way1 time:" << (end - start)/CLOCKS_PER_SEC<< endl;}
直接按照3-5行的声明方式声明,容器类就会给你创建好。
4、Vector容器创建方法二
这个方法是一种笨办法,最慢也最不推荐使用,但是还是可以作为一个反例来给出:
但这个方法是最好理解的:先创建一个大小为n的vector数组,然后每个都resize成一个大小为n的一维数组,再遍历,赋值,特别麻烦,和第一种有着异曲同工之妙,但是也是最慢的。
void usingVectorway2(int n){clock_t start = clock();vector<vector<double>> a(n);vector<vector<double>> b(n);vector<vector<double>> c(n);for (int i = 0; i < n; i++) {a[i].resize(n);b[i].resize(n);c[i].resize(n);}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {a[i][j]=3.0;b[i][j]=4.0;c[i][j]=1.0;
}}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {c[i][j] = sqrt(powl(a[i][j], 2) + powl(b[i][j], 2));}}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {cout << c[i][j] << '\t';}cout << endl;}clock_t end = clock();cout << "Vector Way2 time:" << (end - start)/CLOCKS_PER_SEC << endl;
}
5、绘图解释
第一种和第四种方法原理是这样的:

第二种方法:

这下就很清楚了。
6、对比结果
最后我们来看看这四种方法的运行结果,主要是看他们的运行时长:

可以看出,新方法(方法二)的速度最快,而方法四的速度最慢,这说明我们改变声明和访存方式之后,分配、访存、计算速度是有所提升的,新方法是行之有效的。
7、程序代码
这里贴上所有程序代码,不写注释了,上面各函数都讲了:
#include<iostream>
#include<vector>
#include<ctime>
using namespace std;
void originalDynamic(int n);
void newDynamic(int n);
void usingVectorway1(int n);
void usingVectorway2(int n);
int main() {const int n = 20000;originalDynamic(n);newDynamic(n);usingVectorway1(n);usingVectorway2(n);
}void originalDynamic(int n){clock_t start = clock();double** a = new double* [n];double** b = new double* [n];double** c = new double* [n];for (int i = 0; i < n; i++) {a[i] = new double[n]();b[i] = new double[n]();c[i] = new double[n]();}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {a[i][j] = 3.0;b[i][j] = 4.0;c[i][j] = 0.0;}}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {c[i][j] = sqrt(powl(a[i][j],2) + powl(b[i][j],2));}}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {cout << c[i][j] << '\t';}cout << endl;}clock_t end = clock();cout << " Original time:" << (end - start)/CLOCKS_PER_SEC << endl;
}void newDynamic(int n){clock_t start = clock();double** a = new double* [n];double** b = new double* [n];double** c = new double* [n];a[0] = new double[n * n];b[0] = new double[n * n];c[0] = new double[n * n];for (int i = 1; i < n; i++) {a[i] = a[0] + i * n;b[i] = b[0] + i * n;c[i] = c[0] + i * n;}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {a[i][j] = 3.0;b[i][j] = 4.0;c[i][j] = 0.0;}}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {c[i][j] = sqrt(powl(a[i][j], 2) + powl(b[i][j], 2));}}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {cout << c[i][j] << '\t';}cout << endl;}clock_t end = clock();cout << "New time:" << (end - start)/CLOCKS_PER_SEC << endl;
}void usingVectorway1(int n){clock_t start = clock();vector<vector<double>>a(n, vector<double>(n, 3.0));vector<vector<double>>b(n, vector<double>(n, 4.0));vector<vector<double>>c(n, vector<double>(n, 0.0));for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {c[i][j] = sqrt(powl(a[i][j], 2) + powl(b[i][j], 2));}}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {cout << c[i][j] << '\t';}cout << endl;}clock_t end = clock();cout << "vector Way1 time:" << (end - start)/CLOCKS_PER_SEC<< endl;}void usingVectorway2(int n){clock_t start = clock();vector<vector<double>> a(n);vector<vector<double>> b(n);vector<vector<double>> c(n);for (int i = 0; i < n; i++) {a[i].resize(n);b[i].resize(n);c[i].resize(n);}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {a[i][j]=3.0;b[i][j]=4.0;c[i][j]=1.0;
}}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {c[i][j] = sqrt(powl(a[i][j], 2) + powl(b[i][j], 2));}}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {cout << c[i][j] << '\t';}cout << endl;}clock_t end = clock();cout << "Vector Way2 time:" << (end - start)/CLOCKS_PER_SEC << endl;
}
ok,学到一种新方法还是很高兴的。
相关文章:

二维数组创建方式比较
暑假跟着地质队去跑山了,到现在还没结束,今天休息的时候突然刷到了一篇关于C二维数组创建方面的文章,我觉得还是非常不错滴,就将其中提到的新方法和我已经使用过的三种方法进行了比较,发现该方法提高了二维数组的分配、…...

安达发|富士康科技集团利用自动排程APS软件打造智慧工厂
富士康科技集团作为全球领先的3C产品研发制造企业,近年来积极布局智能制造领域,通过引入先进的自动化排程系统(APS),成功打造了智慧工厂,提高了生产质量与效率,降低了生产成本。 富士康集团自2019年下半年提出在观澜厂区建立数字可…...

云计算在大数据分析中的应用与优势
文章目录 云计算在大数据分析中的应用云计算在大数据分析中的优势云计算在大数据分析中的示例未来发展和拓展结论 🎉欢迎来到AIGC人工智能专栏~云计算在大数据分析中的应用与优势 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT陈寒的博客&…...

linux————ELK(日志收集系统集群)
目录 一、为什么要使用ELK 二、ELK作用 二、组件 一、elasticsearch 特点 二、logstash 工作过程 INPUT(输入) FILETER(过滤) OUTPUTS(输出) 三、kibana 三、架构类型 ELK ELKK ELFK ELFKK EFK 四、构建ELk集群…...

Leetcode213 打劫家舍2
思路:既然头尾不能同时取,那就分别算只取头或者只取尾,不考虑特殊情况的话是一个简单的动态规划 class Solution:def rob(self, nums: list[int]) -> int:if len(nums) < 3:return max(nums)max_sum [nums[0], max(nums[1], nums[0])…...

Redis全局命令
"那篝火在银河尽头~" Redis-cli命令启动 现如今,我们已经启动了Redis服务,下⾯将介绍如何使⽤redis-cli连接、操作Redis服务。客户端与服务端交互的方式有两种: ● 第⼀种是交互式⽅式: 后续所有的操作都是通过交互式的⽅式实现,…...

Xml转json
利用fastjson转换,pom文件依赖: <dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version> </dependency> <dependency><groupId>com.alibaba</groupId><artifa…...

Spring框架知识点汇总
01.Spring框架的基本理解 关键字:核心思想IOC/AOP,作用(解耦,简化),简单描述框架组成; Spring框架是一款轻量级的开发框架,核心思想是IOC(反转控制)和AOP&a…...

JavaScript Web APIs - 06 正则表达式
Web APIs - 06 文章目录 Web APIs - 06正则表达式正则基本使用元字符边界符量词范围字符类 替换和修饰符正则插件change 事件判断是否有类 目标:能够利用正则表达式完成小兔鲜注册页面的表单验证,具备常见的表单验证能力 正则表达式综合案例阶段案例 正…...

Python入门教程 | Python3 字符串
字符串是 Python 中最常用的数据类型。我们可以使用引号( ’ 或 " )来创建字符串。 创建字符串很简单,只要为变量分配一个值即可。例如: var1 Hello World! var2 "Tarzan"Python 访问字符串中的值 Python 不支持单字符类型ÿ…...

Playwright for Python:安装及初步使用
文章目录 一、Playwright介绍1.1 简单介绍1.2 支持的平台1.3 支持语言1.4 官方文档(python) 二、开始2.1 安装要求2.2 安装2.3 脚本录制2.4 代码示例 一、Playwright介绍 1.1 简单介绍 Playwright是微软推出来的一款自动化测试工具,是专门为…...

Ubuntu 20.04.5 怎么安装微信
这是我的ubutun版本号 在这个系统装桌面版微信很多功能不健全。搜索了很多方法,这个算是不错的一个法子。 1.添加仓库 首次使用时,你需要运行如下一条命令将移植仓库添加到系统中。 wget -O- https://deepin-wine.i-m.dev/setup.sh | sh 2.应用安装 …...

HummerRisk V1.4.0发布
大家好,HummerRisk 1.4.0和大家见面了,在这个版本中我们变更了多云检测的底层逻辑,增加了每次检测的project概念,更好的去支持检测历史和检索需要,增加阿里云最佳实践中资源监控检测规则,增加资源态势中的细…...

C语言每日一练----Day(12)
本专栏为c语言练习专栏,适合刚刚学完c语言的初学者。本专栏每天会不定时更新,通过每天练习,进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字:最大连续1的个数 完全数计算 💓博主csdn个人主页࿱…...

【Tkinter系列11/15】小部件 (Text)
24. 小部件Text 文本小部件是一种更通用的方法 处理比小部件多行文本。文本小部件几乎是一个完整的文本 窗口中的编辑器:Label 您可以将文本与不同的字体、颜色和 背景。 您可以用文本穿插嵌入的图像。一 图像被视为单个字符。请参见第 24.3 节 “文本小部件图像”…...

通过「内网穿透」技术,实现出差期间远程访问企业局域网中的象过河ERP系统
文章目录 概述1.查看象过河服务端端口2.内网穿透3. 异地公网连接4. 固定公网地址4.1 保留一个固定TCP地址4.2 配置固定TCP地址 5. 使用固定地址连接 概述 ERP系统对于企业来说重要性不言而喻,不管是财务、生产、销售还是采购,都需要用到ERP系统来协助。…...

ChatGPT和大型语言模型(LLM)是什么关系?
参考:https://zhuanlan.zhihu.com/p/615203178 # ChatGPT和大型语言模型(LLM)是什么关系? 参考:https://zhuanlan.zhihu.com/p/622518771 # 什么是LLM大语言模型?Large Language Model,从量变到质变 https://zhuanla…...

list(介绍与实现)
目录 1. list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modififiers 1.2.6 list的迭代器失效 2. list的模拟实现 2.1 模拟实现list 2.2 list的反向迭代器 1.…...

Centos7 使用docker安装oracle数据库(超详细)
在linux中采用解压安装包的方式安装oracle非常麻烦,并且稍微不注意就会出现问题,因此采用docker来安装,下面为详细的步骤: 若不知道是否安装docker可查看这篇文章:docker安装 1、拉取oracle镜像 docker pull registr…...

昨天面试的时候被提问到的问题集合(答案)
1、vue的双向绑定原理是什么?里面的关键点在哪里? Vue的双向绑定原理是基于Object.defineProperty或者Proxy来实现的,其关键点在于数据劫持,即对数据的读取和修改进行拦截,在数据发生变化时自动更新视图 2、实现水平垂…...

PYTHON用户流失数据挖掘:建立逻辑回归、XGBOOST、随机森林、决策树、支持向量机、朴素贝叶斯和KMEANS聚类用户画像...
原文链接:http://tecdat.cn/?p24346 在今天产品高度同质化的品牌营销阶段,企业与企业之间的竞争集中地体现在对客户的争夺上(点击文末“阅读原文”获取完整代码数据)。 “用户就是上帝”促使众多的企业不惜代价去争夺尽可能多的客…...

详解IP协议
在介绍IP协议之前,先抛出一个概念:IP地址的作用——定位主机,具有将数据从主机A跨网络传输到主机B的能力,有了TCP提供的策略,例如滑动窗口、拥塞控制等,IP去执行它,所以我们通常叫TCP/IP协议&am…...

Stream流式编程用例
Stream流式编程用例: filter, map, flatmap, limit, skip, sort, distinct, collect, reduce, summary statistics public class StreamTest {public static void main(String[] args) {//filterStream<Integer> stream Stream.of(1, 2, 3, 4, 5);Stream&l…...

【Pytorch笔记】1. tensor的创建
参考视频: 深度之眼官方账号:01-02-张量简介与创建 torch.tensor() b torch.tensor(data, dtypeNone, deviceNone, requires_gradFalse, pin_memoryFalse)data:创建的tensor的数据来源,可以是list或numpy dtype:数据…...

Maven 基础之安装和命令行使用
Maven 的安装和命令行使用 1. 下载安装 下载解压 maven 压缩包(http://maven.apache.org/) 配置环境变量 前提:需要安装 java 。 在命令行执行如下命令: mvn --version如出现类似如下结果,则证明 maven 安装正确…...

运动耳机需要具备哪些功能、挂耳式运动蓝牙耳机推荐
作为运动爱好者,长时间的运动很容易枯燥,所以我会选择佩戴耳机来缓解运动的枯燥感,一款好的运动耳机可以让运动变得更加激情,还可以更好的享受运动的乐趣。 但现在的运动耳机产品实在是五花八门,到底什么样的运动蓝牙耳…...

【MCU】SD NAND芯片之国产新选择
文章目录 前言传统SD卡和可贴片SD卡传统SD卡可贴片SD卡 实际使用总结 前言 随着目前时代的快速发展,即使是使用MCU的项目上也经常有大数据存储的需求。可以看到经常有小伙伴这样提问: 大家好,请问有没有SD卡芯片,可以直接焊接到P…...

java 多线程
01.多线程类java.lang.Thread 这里继承Thread类的方法是比较常用的一种,如果说你只是想起一条线程。没有什么其它特殊的要求,那么可以使用Thread.(笔者推荐使用Runable,后头会说明为什么)。下面来看一个简单的实例&…...

ConsoleApplication17_2项目免杀(Fiber+VEH Hook Load)
加载方式FiberVEH Hook Load Fiber是纤程免杀,VEH是异常报错,hook使用detours来hook VirtualAlloc和sleep,通过异常报错调用实现主动hook 纤程Fiber的概念:纤程是比线程的更小的一个运行单位。可以把一个线程拆分成多个纤程&#…...

【Vue3 知识第五讲】条件渲染、列表渲染知识详解
文章目录 一、条件渲染1.1 概述1.2 演示代码 二、列表渲染2.1 使用 指令 v-for 遍历数组2.2 **使用 指令 v-for 遍历对象** 十、案例作业十一、总结 在前端开发过程中,条件和循环是经常被用到的逻辑。vue中封装了自己的组件渲染指令,可以更加方便的帮助开…...