a[i][j] 和 a[j][i] 有什么区别?
本文以一个简单的程序开头——数组赋值:
int LEN = 10000;
int[][] arr = new int[LEN][LEN];
for (int i = 0; i < LEN; i++) {
for (int j = 0; j < LEN; j++) {
arr[i][j] = 1;
}
}
示例中虽然采用了Java,但是熟悉其他编程语言的同学可以自行脑补成自己熟悉的语言,如C/C++、Go、Python之类的,这里的知识点不限制在语言层级。
我们在使用这种for循环的时候,是否会习惯性地使用arr[i][j]的这种写法? 其实很多开源代码、技术书籍示例中都是如此。你是否还考虑过还有如下这种写法,即将 arr[i][j] 替换成 arr[j][i] :
int LEN = 10000;
int[][] arr = new int[LEN][LEN];
for (int i = 0; i < LEN; i++) {
for (int j = 0; j < LEN; j++) {
arr[j][i] = 1;
}
}
两段代码功能完全一样,究竟有何区别?两者性能会有数十倍或者数百倍之差。
有些同学看到这里,直接祭出IDE,运行试了一下,发现第一段代码的性能最优,所以很快地得出了a[i][j]最优的结论。不过也会有同学会运行出第二段代码性能更优的结果,即a[j][i]最优。这到底是怎么回事呢?
从语言本身看,这个问题的要点在于语言怎样实现矩阵 arr[M][N] 的存放。有两种最基本的存放方式:一种是第1下标优先存放;另一种是第2下标优先存放。也就是一般所说的行优先(Row-major)和列优先(Column-major)。
举个例子,对于下面的数组:
可以有两种存储方式:左为列优先,右为行优先。
行优先存储,顾名思义,就是一行的数据存放在一起,然后逐行存放。列优先存储,就是每一列的数据是存储在一起的,一列一列地存放在内存中。这两种存储方法,对于编写遍历二维矩阵的循环语句,还是有一定影响的。比如,如果是按行优先存储的,那么在遍历时,一行一行的读取数据,肯定比一列一列地读取整个数组,要方便许多(前者连续访问内存,有利于CPU高速缓存,后者不联系访问内存,会引起频繁更新高速缓存)。
行优先(Row-major)或者列优先(Column-major)没有好坏,但其直接涉及到对内存中数据的最佳存储访问方式。因为在内存使用上,程序访问的内存地址之间连续性越好,程序的访问效率就越高;相应地,程序访问的内存地址之间连续性越差。所以,我们应该尽量在行优先机制的编译器,比如C/C++、Objective-C(for C-style arrays)、Pascal等等上,采用行优先的数据存储方式;在列优先机制的编译器,比如Fortune、Matlab、R等等上,采用列优先的数据存储方式。但这种思想渗透到编程中之后,代码的质量就会提高一个档次。
这里没有提到市场占有率很高的Java语言,那么它们属于行优先还是列优先呢?
答案:两者都不是。
密集数组存储的一个典型替代方案是使用伊利夫向量(Iliffe vector),它通常将元素存储在连续的同一行中(如同Row-major顺序),但不存储行本身。
什么是Iliffe向量?在计算机编程中,Iliffe向量是一种用于实现多维数组的数据结构。n维数组的Iliffe向量(其中n≥2)由指向(n - 1)维数组的指针的向量(或1维数组)组成。它们通常用于避免在对数组元素执行地址计算时执行昂贵的乘法操作。它们还可用于实现交错数组,如三角形数组、三角形矩阵和其他各种不规则形状的数组。数据结构以John K. Iliffe命名。
它们的缺点包括需要多个链接指针来访问一个元素,以及需要额外的工作来确定n维数组中的下一行,以允许优化编译器预取它。在CPU明显快于主存的系统中,这两个问题都是延迟的来源。
在Java、Python(多维列表)、Ruby、Visual Basic . net、Perl、PHP、JavaScript、Objective-C(当使用NSArray时,不是一个Row-magor C-Style的数组)、Swift和Atlas Autocode等语言中的多维数组被实现为Iliffe向量。利用Iliffe向量实现OLAP产品全息的稀疏多维阵列。
回到开头,如果使用Java语言运行文中开头的2段代码,哪个更叫高效呢?请在留言区留下你的答案和思考。
a[i][j] 和 a[j][i] 有什么区别?的更多相关文章
- GCD - Extreme (II) for(i=1;i<N;i++) for(j=i+1;j<=N;j++) { G+=gcd(i,j); } 推导分析+欧拉函数
/** 题目:GCD - Extreme (II) 链接:https://vjudge.net/contest/154246#problem/O 题意: for(i=1;i<N;i++) for ...
- Codeforces Round #191 (Div. 2) A. Flipping Game【*枚举/DP/每次操作可将区间[i,j](1=<i<=j<=n)内牌的状态翻转(即0变1,1变0),求一次翻转操作后,1的个数尽量多】
A. Flipping Game time limit per test 1 second memory limit per test 256 megabytes input standard ...
- 组合数性质求K个数选取i*j个数分成j组的方案数
分析:设方案数为ANS,C代表组合数: ANS=(C[K,I]*C[K-I,I][K-2*I,I]*...*C[K-(J-1)*I,I])/(J!); 也即: ANS=C[K,I*J]*(C[I*J, ...
- 数组问题:a[i][j] 和 a[j][i] 有什么区别?
本文以一个简单的程序开头--数组赋值: int LEN = 10000; int[][] arr = new int[LEN][LEN]; for (int i = 0; i < LEN; i+ ...
- Eclipse中Java文件图标由实心J变成空心J的问题
在eclipse中空心J的java文件,表示不被包含在项目中进行编译,而是当做资源存在项目中.例如 当是单个文件为空心J的时候 1.右击该文件 -- >BuildPath -->Inclu ...
- 暑假集训#2 div1 J 四点直角 J - Space Invader 四点共面+跨立实验
题意:给你四个点,判断能否先依次通过A,B两点,然后再在某个地方只进行一次直角转弯再一次经过C,D两点: #include <iostream> #include <cstdio&g ...
- 面试题:给定数组a,找到最大的j-i, 使a[j]>a[i]
第一种方法: 用两重循环对每对点都试一下,然后取最大值即可,时间复杂度为O(n2) #include <iostream> #include <algorithm> using ...
- 2013成都网络赛 J A Bit Fun(水题)
A Bit Fun Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- Trie URAL 7192 Chip Factory (15长春J)
题目传送门 题意:从n个数中选出不同的三个数a b c,使得(a+b)^c 最大 分析:先将所有数字按位插入到字典树上,然后删除两个数字,贪心询问与剩下的数字最大异或值. /************* ...
随机推荐
- 如何写好PPT,什么样的PPT容易被人理解记住
PPT一般是用于讲解性的行为而存在,那如果写好PPT呢?如果写好,这个完全要取决于你所面向的目标读者,是用于学术行为呢?还是用于商业行为.面对不同的目标群体,有不同的策略.但是无论面向群体是谁我们都有 ...
- STL——容器(List)List 的概念
1. List 容器的基本概念 1. list 是一个双向链表容器,可高效的进行插入删除元素,他的原理在于每个元素都有两个指针来记录前后两个元素的地址,像火车车厢一样,list 中各个元素在物理存储单 ...
- Spring中毒太深,离开Spring我居然连最基本的接口都不会写了
前言 随着 Spring 的崛起以及其功能的完善,现在可能绝大部分项目的开发都是使用 Spring(全家桶) 来进行开发,Spring也确实和其名字一样,是开发者的春天,Spring 解放了程序员的双 ...
- 精尽Spring MVC源码分析 - WebApplicationContext 容器的初始化
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 基于nacos注册中心的ribbon定制规则
前面说到基于nacos的注册发现有可以扩展实现我们自己的负载均衡算法(Nacos数据模型),来实现同集群调用,是基于spring.cloud.nacos.discovery.cluster-name参 ...
- vue中的样式绑定
样式绑定 样式绑定有class绑定和style绑定,这里我们分别说说 class绑定 class样式绑定与有两种语法:对象语法(v-bind:class='{active:isActive}').数组 ...
- Kafka高性能的原理
Kafka高性能的原理 高性能,高并发,高可用 使用了NIO技术.高并发. 顺序读写.硬盘的顺序读写性能要高于内存的随机读写. 跳表设计. 稀疏索引.index文件里面有部分offset的位置. 使用 ...
- 十行 Python 代码就提取了韦小宝的身份证信息
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 记得以前有个广告词叫:"学好数理化,走遍天下都不怕",感觉应该再加一句:&quo ...
- VC++安装window8.1系统
下载VC++软件 解压安装(这个过程一般不会出现问题) 安装完成后,运行VC++会出现不兼容信息,照着一下方法就可以解决了. 将MSDEV.EXE重命名为MSDEV1.EXE.(路径:Common/M ...
- Centos7路由设置
再添加路由时,很多时候都是采用命令行用route添加的.但是在机器重启后.就失效了.这里也是参考了几位博主的经验 作出以下记载 一:路由表常用设置 1.route命令路由表常用设置: //添加到主机的 ...