|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
0 n) b* `* ^$ N2 ?$ ^9 n; b4 p; F2 G
目录:
: [1 D+ g' z7 T# c" Y9 _" r. {6 {, m. ^0 c7 e/ H3 m: o0 B
containers.Map简介
, c. r5 `; o' f/ M5 f! N数组,元胞和结构体的局限性; q" Q+ j- F% k+ ?
什么是containers.Map
) U) K; {# |5 d0 v+ kcontainers.Map的属性和成员方法
0 v4 P2 Z3 P1 m7 f! N: `& Gcontainers.Map的特点3 C% c7 @& e- P4 |, ]
containers.Map可以不断地扩张不需预分配
# k8 i: Z* O9 }4 L0 vcontainers.Map可以作为参数在函数内部直接修改
8 w( u' F$ e3 Z2 P) mcontainers.Map可以增强程序的可读性
# z' q5 I1 S+ n4 ]1 b2 `5 lcontainers.Map提供对数据的快速查找) b3 x# B3 s; f4 U" o& o3 x L
containers.Map的使用实例
( p+ ^6 F0 D' \; D; H用来盛放元素周期表. X* u: F; H9 P, z& J* L+ q
用来实现快速地检索
3 i$ ^8 n7 [; V+ Y3 K( @; \MATLAB常用基本数据类型有:整型,浮点型,字符型,函数句柄,元胞数组和结构体数组。除了这些基本数据类型,MATLAB还有很多其它的数据类型不为人熟悉,这些数据类型在编程中也非常有用。MATLAB高级数据类型系列旨在向大家介绍它们:比如 containers.Map,tables,enumeration和time series等等,它们为什么有用,用来解决什么问题,并且怎样在科学工程计算使用。本篇首先介绍 containers.Map 数据类型。
3 z. f$ K' N2 M' k, G( n9 U" Mcontainers.Map简介' o/ f* H4 i4 J, W e
MATLAB中最具代表性的高级数据类型是containers.Map,我们可以把它叫做映射表。它和函数映射有些类似,比如函数映射的定义是:
$ `0 }, ]- B- S2 H5 KF(x) = Y. r8 k: ]+ S# F7 U: r0 P. W
针对每一个X,都有一个唯一的Y与之对应,反之不一定。如图Fig.1所示。和函数映射相似,映射表用来形容键(Key)和键值(Key Value)之间的一一对应关系。每个Key都是独一无二的,且只能对一个Key Value。如图Fig.2所示。
/ E5 t. D- Q( t4 h6 RFig.1 函数映射关系4 T( e* }+ k! ?1 ^$ H1 \# w. [
Fig.2 containers.Map类的映射示意图
0 t1 J3 U7 r, h3 N7 b数组,元胞和结构体的局限性; e1 Q/ W. d9 v+ }7 _' t
开始我们先介绍一下数组,元胞数组和结构体的局限性,为什么有的时候这些基本的数据类型无法满足程序的要求,换句话说,我们为什么需要 containers.Map 数据结构。假设要用MATLAB来记录电话号码簿中数据,比如表Table.1所示:1 U: `7 i5 C% {* n
Table.1 电话号码簿
1 e! l6 O# z( L) x- ~; K$ R" k6 a) y6 y+ O, L ]+ }
姓名 电话号码5 \+ H5 t& l% `. A
Abby 5086470001
& i3 P: L: B9 g9 p1 q1 [Bob 5086470002: d% i; Q# _ _. r* N' F
Charlie 5086470003 先讨论数组,因为电话号码簿中既含有数字,又含有字符串,而数组中只能存放Double类型的数据,所以没有办法用数组直接记录电话号码薄中的内容。 再试试元胞数组,我们在第1行预先声明一个 3 X 3 的元胞数组,然后在2-4行按顺序填放号码簿的内容。
3 |; @ k& \5 v% a8 ?) k% 元胞数组初始化( a6 \! ], p: O' L
addressBook = cell(3,1); % 预分配大小是MATLAB编程的好习惯 1 T6 h6 \: f) }8 `
addressBook{1} = {'Abby', '5086470001'};/ V0 p9 D1 E# W7 V8 I5 ]
addressBook{2} = {'Bob' , '5086470002'};/ j0 z, u$ @ I; S8 Z
addressBook{3} = {'Charlie', '5086470003'};
/ J1 @, Z* w @7 |. [# w需要的时候,可以通过for循环访问其中的内容,比如:
) C$ s. P3 l9 H" _# k. Pfor iter = 1:length(addressBook) % 元胞数组的遍历! s8 B+ u. N/ Z& X+ R* m! d; {
addressBook{iter} % 通过Index访问元胞中的内容
8 b9 t8 y2 O( J1 `- Mend/ e- J4 \9 N& `8 i. u" O8 I
但是按照顺序遍历电话号码簿没有什么实际用处,号码簿的主要功能应该是提供查找的功能才是。比如要想查询Charlie的电话号码,我们希望程序最好可以写成如下这样:, n3 h% Y- F0 g# V, U+ U
CharlieNum = addressBook{'Charlie'} % 提示:这是错误的写法% k/ @; \0 K2 r Q4 C2 q6 Y) c2 @
或者
3 J7 A0 n9 g, K/ XCharlieNum = addressBook.Charlie % 提示:这是错误的写法9 }& h+ a$ u: W+ p9 R5 m) T
但是元胞数组的值只能通过Index去访问内容,不支持如上的访问方式。所以为了找到Charlie的电话号码,程序不得不遍历元胞中的所有内容,取出每一个元胞元素的第一列的字符串做比较,如果名字等于Charlie,则输出电话号码: ]7 {1 [6 _: ^% z$ g4 ^$ b# a
% 使用for循环查找
, q% O9 i4 j4 v( jfor iter = 1:length(addressBook)% h! }0 \ A' {' R, D
if strcmp(addressBook{iter}{1},'Charlie')1 P% D* l t0 L) N) M% K
addressBook{iter}{2} % 如找到则输出电话号码" I* a3 D o2 {% P1 @8 E3 E3 }, s
break;
0 Z2 j* x0 p- {" m- P3 S$ e end
) E/ y6 q1 | Yend3 f+ l2 `; E4 z' {: f" j
当然还有其他的方式来盛放电话号码簿,比如把电话和名字分别存到到两个元胞数组中去" h+ [' q" a4 J* R
names = {'Abby','Bob','Charlie'}; % 用元胞放号码 O: O: N! n+ s* t) w$ f
numbers = {'5086470001','5086470002','5086470001'}; % 用元胞放名字- k4 A6 m- V/ G+ B
ind = find(strcmp(names,'Charlie')); % strcmp接受向量的输入 返回Logical数组
2 i( Y* r' }- J) d3 i& Q0 v % find紧接着找出逻辑数组中非零元素的Index
$ k" d' ^+ v0 o7 h6 ]4 ^- D' ]1 o e9 @. lnumbers{ind}
O5 E8 p' z4 F其中第3行strcmp接受元胞作为输入,在其中寻找Charlie,find函数将返回Charlie所在的位置,这样的方式比使用for循环要快,但无一例外的是,两种方法都要从头开始遍历一个数组,终究来说是慢的。 除查找性能慢外,使用元胞盛放电话号码簿类型的数据还有其它缺点:1 {, ^ r! |: v8 w4 L& _& ?
无法方便的验证重复数据。电话号码簿要求每一个人的名字都是独一无二的,所以在数据录入的时候要防止姓名的重复,但是我们没有其它办法知道某名字是否已经被使用过了,除非在每次输入的时候都对整个元胞里的内容做遍历比较。* F* h: r# B0 ?) t+ l$ w
无法方便地添加内容。如果电话号码簿中的记录需要不断地增长,但是我们没有办法在一开始就估计出其大概的数量,于是无法有效的预先分配内存,所以添加数据时,一旦超过预先分配的量,MATLAB就要重新分配内存了。0 b% \& b2 Q8 `1 e
无法方便地删除内容。如果我们要从元胞中去掉某一记录,可以找到该记录,并把该位置的元胞内容置空,但这并不会自动减小元胞数组的长度,如果 这样的删减操作多了,元胞中会留下很多没有利用的空余位置。
: s: B7 o2 X8 D2 N不方便作为函数的参数,具体原因见struct的局限性.
- H# D) ~% W' A- N A- X最后我们再尝试一下用结构体盛放电话号码簿数据类型,struct的赋值很简单,比如可以直接赋值:
D9 s# N" b# ]4 T2 @! k% 赋值方法1) }4 ?+ Z2 q" d2 i
addressBook.Abby = '5086470001';/ s/ T4 a0 K; m9 Y7 [
addressBook.Bob = '5086470002';& |$ l- w- O+ m2 O% }/ m" I- i5 V
addressBook.Charlie = '5086470003';# d& `+ j; Z. `! P
或者:" N' S* f2 R( u9 b4 ^3 M9 X
% 赋值方法2
2 x9 S" C n: V" y1 ]$ j2 q- {- HaddressBook = struct('Abby','5086470001','Bob','5086470002','Charlie','5086470003')
7 T6 z! N. ~2 e方法1和方法2是等价的。 struct数据类型的查找很方便,比如要查询Charlie的电话号码,直接访问struct中的同名的field即可以了。0 L- C; q- x8 g
num = addressBook.Charlie $ r' Y' c0 S/ H5 s8 V) N
如果要查询的人名是一个变量, 我们可以使用getfield函数:4 i% B' q7 a; P0 q8 e8 F( D$ G( J
num = getfield(addressBook,name) % 其中name是变量
! d: K, h7 z [: w$ j/ M. Z1 x! astruct盛放电话号码簿类型的数据,查询起来确实比元胞进步了不少,但还是有些不方便的地方。 因为struct的field的名字要求必须是以字母开头,这是一个很大的局限,并不是所有的类似电话号码簿的结构都可以用人名做索引,比如账户号码簿,股票代码等等,他们通常是用数字开头的,比如图Table.2中的数据:4 @# T u7 S* D1 |# ~" h
Table.2 深证股票代码! D; z8 K! k/ R7 L, Y3 `* N. _
6 ]: h. j/ E! {6 d( F: G: F4 {股票代码 股票名称5 D+ k6 Z, N0 ~# }, c' v4 a& K
000001 深发展
3 E4 X$ {& c" `, x- R000002 万科
4 H9 b5 E, ~8 l, Y( }000004 ST国农 如果我们要求通过股票的代码来查找股票的具体信息,struct无法简单的直接做到。 使用struct的另一个不方便之处在于,当把struct当做函数参数,并且在函数内部需要对该struct做一定的修改时,为了要把修改后的结果返回,我们需要对原来的struct做完全的拷贝,显然如果struct中的数据很多的话,这样做是低效的。比如下面的函数中,addressBook被当做函数的参数传入,在函数中添加了一个新的field, 为了返回更新后的结构,函数内部必须重新构造一个新的struct,也就是s返回给该函数的调用者。0 P0 _% [$ {& v/ v0 u
% 把struct当做函数的参数 + x* k: ?, h d
function s = modifystruct(s)* W0 {7 p% B; m, d" e, ?4 _/ D
s.Dave = '5086470004';
$ H6 a# j& e& ^" v' G+ Eend
* |1 A. x- m$ {: _4 g9 A用containers.Map来记录电话号码簿
, P4 {& j) F8 e0 M: l& b3 {上面一节我们介绍了数组,元胞数组和结构体在模拟电话号码簿这种数据结构时的局限性,这节我们来看怎么用 containers.Map 来盛放电话号码簿中的内容:" W/ E4 K& k1 v0 }
addressMap = containers.Map; % 首先声明一个映射表对象变量
& o/ t4 v7 z+ d5 W3 K1 IaddressMap('Abby') = '5086470001';& H% I6 S2 a" J: S( U0 j: R3 {0 X
addressMap('Bob') = '5086470002';2 t' P) ^$ Y5 L7 C7 |6 z
addressMap('Charlie') = '5086470003'; k4 @$ Q8 I3 |
第一行我们声明一个containers.Map的变量(其实是containers.Map类的对象)叫做addressMap,2,3,4行通过提供Key Key Value的方式来给对象赋值,其中单引号内部的值叫做键(Key),等式右边的叫做键值(Key Value)。通过这个结构,我们在MATLAB内存中,建立起了如图Fig.3的映射关系数据结构。3 `% A0 Z7 V7 ^( `' N
Fig.3 电话号码簿映射表8 |. ]9 E) e4 w" M, s
Fig.4 Map类的UML 查找addressMap对象中的内容极其方便,比如查找我们想知道Charlie的电话号码,只需:7 f( s+ ~# k$ v; j# z) Y* {) o H, ^
% 查找
% G; l* Z0 D( znum = addressMap('Charlie') ; x/ H4 O4 ^* B& x }+ O Q
如果我们要修改Charlie的电话号码,只需 :$ v; f* P. b2 ~
% 赋值
! W/ C, _ I" L$ D$ ]) EaddressMap('Charlie') = newNum; 9 y: @1 e* |8 x7 w9 f y0 ?2 T
什么是containers.Map
! N, s/ v) y( c ~containers.Map是一个MATLAB的内置类(类是面向对象编程中的一个基本概念,在这里可以把类暂时理解成一种数据类型),所谓内置,就是MATLAB内部实现的,通常这种类的性能更加的优秀。containers.Map其中containers是Package的名称,Map是该Package中的一个类,Map是它的类名。用UML(Unified Modeling Language)的语法把该类表示出来,它的属性包括Count, KeyType,ValueType。它的常用方法包括keys,values,isKey,remove。如图Fig.4所示。* R; O( R) G: M0 f+ r6 j0 Q' W
containers.Map的属性和成员方法) V6 M6 w/ |+ O4 Z; v a, o
这节我们介绍containers.Map的属性和成员方法,假设我们这样初始化一个containers.Map对象:3 u) I/ w7 U$ }2 Y* g" w0 E) X
% 初始化一个Map对象
* b( m7 j8 j; d$ c1 n# B EaddressMap = containers.Map;
9 a @" J; o2 J! qaddressMap('Abby') = '5086470001';
8 `, r8 M7 I: v" m9 a/ Q1 MaddressMap('Bob') = '5086470002';
% }/ K# Q( E; b0 v6 Z' N* D" l) `addressMap('Charlie') = '5086470003';
+ U% a* X) W- ?# q! @7 I+ X在命令行中键入该对象的名称,MATLAB会显示该对象属性的基本信息:& I% F( Q8 ] z5 H! T
>> addressMap
1 J! |4 E7 u, T% Q. `. A! SaddressMap =0 `8 @( E- A/ m6 f6 M' F/ E2 U4 e
Map with properties:
4 J8 ~$ \) t- k r3 O8 n6 l4 }* v Count: 3 % MAP 中映射对的数目
?& k. f/ K0 a9 q5 f4 } KeyType: char % MAP 中键的类型
2 p& W0 p4 J+ v7 m ValueType: any % MAP 中键值的类型 9 `, g' J* I+ D& D* s* Q' J
其中Count 表示Map对象中映射对的数目。按照规定,键的类型一定要是字符类型,不能是其它数据类型,而键值的类型可以是MATLAB中的任意类型:数组,元胞,结构体,MATLAB对象,甚至JAVA对象等等。因为键值的类型可以是任何MATLAB类型,所以 containers.Map 是MATLAB中极其灵活的数据类型。 成员方法keys 用来返回对象中所有的键:7 w4 C U) N1 X% v y. p
>> addressMap.keys3 I! _9 H$ t7 V/ d
ans =/ C. |: d' F8 H( \, H0 }) m7 k
'Charlie' 'Abby' 'Bob' " ^* l' z" x* M4 a( A9 q
成员方法values用来返回对象中的所有键值. }' r# L9 S/ V4 f* E7 A
>> addressMap.values( ?) k; R' s9 q0 z) ~; [
ans =
f) d+ Y# h' F7 n" g8 ? '5086470003' '5086470001' '5086470002' 6 B- ~3 K' P9 J. C; H
remove用来移除对象中的一个键-键值对,经过下面的操作之后,该对象中的Count的值变成了2。& L; i& T/ s7 C, U. ]9 C
>> addressMap.remove('Charlie')* d. }2 w+ z% p! H: D
ans = . B4 r' m8 e' |2 Q
Map with properties:
6 V- A K' u6 S4 e Count: 2 % 映射对数目减少" V! _ ^* m! p S$ N0 ^
KeyType: char8 e1 ^, ^! Y3 o- u8 l3 x; _
ValueType: any- Q" @3 _( f2 _9 h1 i; [; \2 @7 f
, m. X. e6 X4 ~8 r# HisKey成员方法用来判断一个map对象中是否已经含有一个键值,比如:
# ?+ e! `$ C9 N( _% k5 _( x% ?>> addressMap.isKey('Abby')
7 ]0 D# o+ s7 u7 H- m) C0 Y* C# @ans =' {8 U0 c3 O9 x* N7 v- _0 L: S
1
9 A: p8 m& B" I1 M3 \>> addressMap.isKey('abby') % 键是大小写敏感的
& y8 Y. p$ S( j$ vans =/ ?1 L& V! i" h% }; Y1 S
0
% @* X9 |$ X( J2 c) hisKey的功能是查询,类似之前提到的find和strcmp函数合起来使用的例子。
/ M3 I6 q Z) n8 p4 Ccontainers.Map的特点
" P9 _8 b- N5 D: ?" F: r5 d0 L. p/ [containers.Map可以不断地扩张不需预分配
+ p7 l* J& L1 J6 i9 c! }) q4 f使用数组和元胞数组作为数据容器,通常要预先分配容器的大小。否则每次扩充容器,MATLAB都会重新分配内存,并且拷贝原来数组中的数据到新分配的内存中去。映射表是一个可以灵活扩充的容器,并且不需要预分配,每次往里面添加内容不会引起MATLAB重新分配内存。 我们可以通过提供两个元胞来构造映射表对象,比如下面我们构造一个机票簿数据结构:- k1 t, ~8 S0 v) S* g0 n5 `& c
% 映射表的初始化方法1% v9 d, S- F$ D+ D" ~
ticketMap = containers.Map( {'2R175', 'B7398', 'A479GY'}, ...
! o# y9 s6 P5 T, G3 {& F {'Abby', 'Bob, 'Charlie'});
0 ?/ o d, n: A. h" F5 }: j
5 j9 E4 W* O P0 V% S$ Z, G也可以在程序的一开始,先声明一个对象:4 q/ @. B* ]5 Z6 ]. f, e* T
% 映射表的初始化方法2 7 D4 D% D3 }; c2 {: [# }' T
>> ticketMap = containers.Map . h, @( W* N& I$ i3 l# {% k
然后在计算的过程中慢慢的向表中插入数据:
$ ]" t( W) Q* l% 映射表的初始化方法2
+ P7 Q! l% Q: |3 d>> ticketMap['2R175'] = 'Abby';
, {' S3 |/ |7 @, _, D! {...
. R! X8 y1 Y. ~2 E& t# U; O+ N>> ticketMap['A479GY'] = 'Charlie; $ k+ H- p6 S! S4 r1 Y) _
containers.Map可以作为参数在函数内部直接修改% m% k$ G2 c" |9 T2 f
因为containers.Map是handle类(handle类的介绍参见《MATLAB面向对象编程-从入门到设计模式》第三章:MATLAB的句柄类和实值类),我们还可以方便的将这个对象传给任何MATLAB的函数,并且在函数内部直接修改对象内部的数据,并且不用把它当做返回值输出,比如:1 s. ~, p$ x, D' m# z
>> modifyMap(ticketMap); % N* O/ l. B5 S9 z
modifyMap函数可以写成:
+ J3 {% f7 C2 y( f4 k9 x* H1 gfunction modifyMap(ticketMap) % 该函数没有返回值( M' f5 w. \& y7 k; B
.....
8 E- T; x/ l/ W ticketMap(NewKey) = newID
1 u) F; R* z! O1 J3 V# k' yend 0 _2 P& O4 D5 u/ D3 }- l6 ?
注意这里没有把修改的ticketMap当做返回值,在函数中我们直接修改了Map中的数据,这是MATLAB面向对象语言中handle类的特性。
9 p0 N |$ _( {; }0 m! k2 q7 ~/ Ycontainers.Map可以增强程序的可读性
. ~1 R5 J) S8 r1 m映射表内部可以存储各种类型的数据,并且给它们起一些有意义的名字。具体的例子见元素周期表的例子。 访问和修改这些数据的时候,可以直接使用这些有意义的名字,从而增加程序的可读性。而如果用元胞数组存储这些数据,在访问和修改这些数据的时候, 需要使用整数的Index,程序可读性不够好。
2 h6 R( r$ n* J0 s: N F- hcontainers.Map提供对数据的快速查找
1 y$ I& c8 m' z a. @* v映射表查找的复杂度是常数O(C)的,而传统的数组和元胞数组查找的复杂度是线性O(N)的。如果不熟悉算法中复杂度的概念,可以这样理解:如果使用数组或者元胞来存储数据,当我们要在该数据结构中搜索一个值时,只能做线性搜索,平均下来,搜索的时间和该数据结构中的元素的数目N成正比,即元胞和数组中的元素越多,找到一个值所用的时间就越长,这叫做线性的复杂度 O(N)。而映射表采取的底层实现是哈希表(Hash Map),搜索时间是常数C,理论上来说搜索速度和集合中元素的个数没有关系。所以当容器中元素数量众多的时候,要想查找得快,可以使用containers.Map结构。具体例子见快速查找的例子。 下面通过对假想的数据集合进行查找,来比较一下数组和container.Map的性能。我们第一行先构造一个含有1000000(10^7)个整数的数组(这是数组中元素的个数很多,如果只有少量元素,线性查找的效率会高一些),按顺序填充从1到(10^7)的整数;作为比较,第二行再构造一个containers.Map对象,往内部填充从1到(10^7)的整数,Key和KeyValue都相同。
$ ^# B. l$ \" A5 o- c8 O4 Na = 1:1000000;
X& n9 g: O: V( R. l. xm = containers.Map(1:1000000,ones(1,1000000));
; j- ?0 U1 {( H+ P3 x1 o$ W数组数据结构,使用find函数依次查找从1到(10^7)的整数,在笔者的机器上,耗时28.331901秒
$ N% ~8 {* [9 T% R1 a9 |7 ^/ d; K% array的查找
$ |& d; b8 t7 Ytic
8 y/ c8 Y! b& Y( ~9 Wfor i=1:100000, 2 R+ h* Z2 x8 I5 Q' T8 I% U
find(b==i);6 d5 G+ U- _9 Q" @, n8 Z
end;
+ h" W: [4 u& T5 H' E+ htoc
( [3 J& _5 i: Q% command line结果
0 E2 m& d1 o/ l- E* j/ g3 D( C" y: Z E/ ?
0 u2 m$ b) S. M! Q0 F+ T3 Q5 R" u ], i* [/ q2 d
4 n' J1 B; Y' O& I3 {; p, l. W2 rElapsed time is 28.331901 seconds.
8 U( b, A% n" gcontainers.Map数据结构,使用键值1到(10^7)进行访问, 在笔者的机器上,耗时只需1.323007秒, 结论是:如果有大量的数据,并且要频繁的进行查找操作,可以考虑使用containers.Map数据结构。(读者可以试试减少容器中元素的个数,观察一下什么情况下,数组的查找会更快一些。)% ~$ f9 Q$ E5 {, s- Z; b8 y
% 映射表的查找$ q$ L Q* ?3 a5 h. U
tic, }- a, y/ x3 s
for i=1:100000, ! l3 [* N3 W' l; x; t
m(i);
+ M# M* c8 A7 T& a+ l" n# ^end; 0 R a4 Z: n$ J) ^ @
toc
6 U o) F8 H; h% command line结果 * }" w, v1 s2 ~& D$ v
$ j+ l$ i5 ^. h% W m6 _* R7 Z7 j0 c" q: P( M9 p
8 i1 B: g) z% X" S
, {7 [: j: a5 h) Y
Elapsed time is 1.323007 seconds.5 r6 N; b, ]2 [! U
谈到在MATLAB中使用高级数据结构,另一种常见的做法是直接使用Java和Python对象,这里顺便比较一下在MATLAB中使用Java的哈希表的效率。再笔者的机器上,耗时3.072889秒.9 U( s$ h( w" {! X
% JAVA哈希表的查找0 l; M7 c+ s. t0 c% ~+ V
s = java.util.HashSet();8 O& L9 j" G; X. Y! x9 n7 Z
for i=1:100000, s.add(i); end
1 w( s5 x4 \, i/ V+ G) H Dtic; 0 ~) D+ A% g6 X7 i0 |4 b/ A
for i=1:100000,
; s5 w" `4 {% _/ ^- E s.contains(i); , r) p% b# x. C: ?9 ]# I# {
end;
( a' [8 U7 O0 T8 y5 ~' ztoc. ~% }/ i4 x! F& H7 g
% command line结果
( e( s7 t5 t& g, q v/ a5 b) E, e$ ^4 p" {# t
9 I3 W- L# r! S; o' b" i
4 h1 t* X# G2 \+ Y# c6 N1 C+ v9 T, i. s, M
' v- y3 J2 ]1 Y) V7 p7 b
9 ?4 v, e8 y2 {2 w* O( E2 zElapsed time is 3.072889 seconds.
* ?0 X4 Q8 \1 G+ V vcontainers.Map的使用实例+ N9 T9 _ U3 p& g# t3 \: J
用来盛放元素周期表
, ^' r& L3 @$ v7 ^' F& J8 b! C工程计算中可以使用这种键-键值的例子有很多。比如物理化学计算中可以用映射表来盛放元素周期表中的内容:" B* E2 V. p. r; M! t3 r" K
>> ptable = containers.Map;6 I( `; Q, ~/ [( {3 P
>> ptable('H') = 1 ;8 i* u% C$ X% N4 G9 L
>> ptable('He') = 2 ;
+ i I0 V9 Y* N) {* v% t# B其中键是原子符号,而键值是原子量。因为键值的类型不限,所以键值本身还可以是对象,比如Atom类对象,其中包涵更多有用的内容:
/ ?, E) J. l3 c; B V% 类文件Atom.m8 c. ~; U p( L$ z9 a8 h, c3 ^5 {
classdef Atom < handle/ M$ v; s, Q( V+ g
properties
' s& I0 g, `7 v& g8 w$ g0 B atomicNumber/ [& i' a7 Q8 l, n5 z
fullName
/ p6 r0 e1 S/ }# e end
; T- v) w0 h; \, z: a( a% ^ methods
# x' e( \0 Z a7 P function obj = Atom(num,name)$ D/ ~: i! [9 i& U! k3 i
obj.atomicNumber = num ;9 j2 P r9 T- e' F0 l% }
obj.fullName = name1 `$ P M: w' t/ o
end" F# @% P# M9 H
end
# W, G" H8 C( b! C& ~' J% jend % a% G/ u3 s" L
\end{Verbatim} 于是:
" P7 k4 }/ ]) C7 E% 键值可以是Atom对象
! G# x# c# I* L+ @/ `( c, L1 O7 E>> ptable('H') = Atom(1,'hydrogen');# F/ O# ^5 f. {. B) ]! Y' \0 w
>> ptable('H') = Atom(2,'helium'); . p8 n7 f5 d# \& w
用来实现快速地检索* x. ~3 W% e+ w [0 f
这个问题来自ilovematlab一个网友的提问:! M- L8 d% f! t
大家好,最近遇到一个问题,要构建20000次以上的三元素向量,且保证每次不重复构建,该向量元素值在2000―3000之间不定数,目前采用的方法是建立一历史档案矩阵A,构建一个存储一行,A大小行数持续增加。每次在构建出一新的向量时对该向量与历史档案矩阵每行进行对比,最终确定是否构建过,若没构建过,重新构建。算法显示该检查程序运算时间在执行20000次以上时,会超过200S的运行时间。想力争减小该时间,求方法。 多谢!3 v1 J" T' e9 B/ L. \5 r) r! v5 N
这位网友的问题不在于如何构建这些向量,而是如何保证每次不重复的构建。他一开始想出的解决方法是用矩阵A来存储历史档案,每建立一个新的向量,和该历史档案已有的内容做比较,如果在档案中没有存档,则构建。这样设计的问题是,如他所述,当A中存储的向量变多之后,每次检查该向量是否之前被创建过的时间加长。 这是一个典型的可以用映射表来解决的问题,把线性的复杂度转成常数的复杂度。他可以给每个向量设计一个独立无二的ID,比如直接转数字成id : num2str(vector). O- d+ l+ l* g! @
% 构建; e2 D6 w( ?$ |3 f7 o/ G/ i
mydictionary = containers.Map
+ x; Z# }+ G+ `; t* ^
5 Q" @" _7 E/ J1 k" M/ x% Y/ }% 插入数据! D+ {. }; S4 L! |) P7 O& ~
id = num2str(vector) ; % 比如vector = [1 2 3];
V3 { U h& w9 B/ lmydictionary('some_id') = vector ; $ M1 k0 P% G# S+ a9 s
之后的检索也是通过vector得到独一无二的ID,然后通过isKey来判断该键值是否已经被加入到MAP中去了。
+ X3 D7 }$ {1 ?* H, y" y6 C. M1 {some_otherID = some_other_vector ;: q* h0 [9 d! E+ l+ W
% 验证是否已经构建给过
( z2 g" @7 [ h0 m6 b, sif mydictinary.isKey('some_otherID'); E, B P& e, ^ F9 i, G" d3 `, K
% 做要做的工作
2 h/ X2 V) b! `, [6 ?2 Wend |
|