数组问题: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已测
工具:IDEA;
结果:arr[i][j]较快,大约90ms;arr[j][i]大约2270ms
数组问题:a[i][j] 和 a[j][i] 有什么区别?的更多相关文章
- 无序数组a,求a[i]-a[j]的最大值,且i<j
一道面试题:对于无序数组a,求a[i]-a[j]的最大值,其中i<j package test; import java.util.Arrays; public class FindMax { ...
- 给出一个数组A,找出一对 (i, j)使得A[i] <= A[j] (i < j)并且j-i最大
题目:给出一个数组A,找出一对 (i, j)使得A[i] <= A[j] (i <= j)并且j-i最大 ,若有多个这样的位置对,返回i最小的那一对. 最直接的想法就是对于每一个 i 从数 ...
- 在数组a中,a[i]+a[j]=a[k],求a[k]的最大值,a[k]max——猎八哥fly
在数组a中,a[i]+a[j]=a[k],求a[k]的最大值,a[k]max. 思路:将a中的数组两两相加,组成一个新的数组.并将新的数组和a数组进行sort排序.然后将a数组从大到小与新数组比较,如 ...
- a[i][j] 和 a[j][i] 有什么区别?
本文以一个简单的程序开头--数组赋值: int LEN = 10000;int[][] arr = new int[LEN][LEN]; for (int i = 0; i < LEN; 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, ...
- 数组的常用方法concat,join,slice和splice的区别,map,foreach,reduce
1.concat()和join() concat()是连对两个或两个数组的方法,直接可以将数组以参数的形式放入 join()是将数组中的所有元素放入一个字符串中,通俗点讲就是可以将数组转换成字符串 2 ...
- Eclipse中Java文件图标由实心J变成空心J的问题
在eclipse中空心J的java文件,表示不被包含在项目中进行编译,而是当做资源存在项目中.例如 当是单个文件为空心J的时候 1.右击该文件 -- >BuildPath -->Inclu ...
随机推荐
- 医学AI论文解读 |Circulation|2018| 超声心动图的全自动检测在临床上的应用
文章来自微信公众号:机器学习炼丹术.号主炼丹兄WX:cyx645016617.文章有问题或者想交流的话欢迎- 参考目录: @ 目录 0 论文 1 概述 2 pipeline 3 技术细节 3.1 预处 ...
- Camtasia处理音频制作BGM
我们在剪辑自己录制的视频时经常会需要同时对音频进行处理,camtasia是一个可以对视频的音频或者单独的音频文件进行处理的软件,那我们就来看看到底如何使用camtasia 2019(Win)来处理音频 ...
- 快来,Boom 3D广播功能还能这样用
Boom 3D不仅为用户提供了包括3D立体音效.古典音乐音效在内的多种音效增强功能,而且还为用户提供了广播功能.该广播功能不仅涵盖了国内广播节目,而且还涵盖了国际广播节目. Boom 3D的广播功能还 ...
- Netty源码分析之ByteBuf(二)—内存分配器ByteBufAllocator
Netty中的内存分配是基于ByteBufAllocator这个接口实现的,通过对它的具体实现,可以用来分配我们之前描述过的任意类型的BytebBuf实例:我们先看一下ByteBufAllocator ...
- php 缩略图封装的方法
/** * PHP生成缩略图 * @param $basepath /原文件地址 * @param $des_w /缩略图的宽 * @param $des_h /缩略图的高 * @param $sty ...
- C++基础知识篇:C++ 变量作用域
作用域是程序的一个区域,一般来说有三个地方可以定义变量: 在函数或一个代码块内部声明的变量,称为局部变量. 在函数参数的定义中声明的变量,称为形式参数. 在所有函数外部声明的变量,称为全局变量. 我们 ...
- AA:白细胞计数
总时间限制: 1000ms 内存限制: 65536kB 描述 医院采样了某临床病例治疗期间的白细胞数量样本n份,用于分析某种新抗生素对该病例的治疗效果.为了降低分析误差,要先从这n份样本中去除一个 ...
- 图解连接阿里云(一)创建阿里云物联网平台产品和设备,使用MQTT.fx快速体验
1. 打开 https://www.aliyun.com/ 注册账号 2.注册账号登录后点击控制台 3. 在下图1处输入物联网平台,会弹出2处所示物联网平台的入口,点击红色箭头所示处,进入物联网平 ...
- springsecurity实现前后端分离之jwt-资料收集
https://www.jianshu.com/p/5b9f1f4de88d https://www.jianshu.com/p/725d32ab92f8 https://blog.csdn.net/ ...
- java并发编程实战《六》等待-通知机制
用"等待-通知"机制优化循环等待 前言 在破坏占用且等待条件的时候,如果转出账本和转入账本不满足同时在文件架上这个条件,就用死循环的方式来循环等待. 1 // 一次性申请转出账户和 ...