找回密码
 注册
关于网站域名变更的通知
查看: 499|回复: 1
打印 上一主题 下一主题

#技术风云榜#MATLAB映射表数据结构

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-11-30 15:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
, }6 ?  p; h* {
目录:
. @0 }; Q" @% p7 R9 |
: R7 o$ \7 r. X3 Pcontainers.Map简介4 t4 P& D7 w$ l# T
数组,元胞和结构体的局限性
* W3 r$ }7 t; j) {) z什么是containers.Map
1 O% v6 ^& G1 `& H9 B5 Fcontainers.Map的属性和成员方法
0 Z, o7 z1 J) S  D7 Q2 E  s: Rcontainers.Map的特点. G1 F2 `& W+ g% J' [9 f$ \. E
containers.Map可以不断地扩张不需预分配
% a0 T5 Y5 K# T, u# o( e+ W* i$ Vcontainers.Map可以作为参数在函数内部直接修改
0 n- S2 M( s: v, d! E, d0 Ncontainers.Map可以增强程序的可读性2 h+ f0 T7 k5 w, u
containers.Map提供对数据的快速查找/ j  O, _4 v2 `6 b* d
containers.Map的使用实例
) r  V  S! }0 s$ d7 @6 m; P用来盛放元素周期表
" V% V5 M1 d$ g4 b( y2 `/ b0 O) H用来实现快速地检索
; M7 G4 X9 N! Q- [MATLAB常用基本数据类型有:整型,浮点型,字符型,函数句柄,元胞数组和结构体数组。除了这些基本数据类型,MATLAB还有很多其它的数据类型不为人熟悉,这些数据类型在编程中也非常有用。MATLAB高级数据类型系列旨在向大家介绍它们:比如 containers.Map,tables,enumeration和time series等等,它们为什么有用,用来解决什么问题,并且怎样在科学工程计算使用。本篇首先介绍 containers.Map 数据类型。# J0 B5 G. p  a
containers.Map简介
+ Y1 x5 U" ^! T5 K' FMATLAB中最具代表性的高级数据类型是containers.Map,我们可以把它叫做映射表。它和函数映射有些类似,比如函数映射的定义是:3 \0 Q: R' i6 b7 W/ U& b
F(x) = Y5 P- o4 c( n' Z- m# I0 H
针对每一个X,都有一个唯一的Y与之对应,反之不一定。如图Fig.1所示。和函数映射相似,映射表用来形容键(Key)和键值(Key Value)之间的一一对应关系。每个Key都是独一无二的,且只能对一个Key Value。如图Fig.2所示。
" Z2 A7 X4 W' J; N! wFig.1 函数映射关系2 G7 D3 n7 n) A! o9 L. A
Fig.2 containers.Map类的映射示意图& Q# ?$ {  W* i) g4 Q& S
数组,元胞和结构体的局限性2 b3 L2 g) m! b3 V: w
开始我们先介绍一下数组,元胞数组和结构体的局限性,为什么有的时候这些基本的数据类型无法满足程序的要求,换句话说,我们为什么需要 containers.Map 数据结构。假设要用MATLAB来记录电话号码簿中数据,比如表Table.1所示:
4 @9 M& @# j" c1 U. xTable.1 电话号码簿
  w1 V# Q# n5 t, F% i
  o9 L% K9 s7 Y( N& x) q. A姓名        电话号码
- d( S% a" M% [1 d8 VAbby        50864700017 @' x- i; ^* @$ g
Bob        5086470002
$ M0 T' x9 s2 d! [Charlie        5086470003 先讨论数组,因为电话号码簿中既含有数字,又含有字符串,而数组中只能存放Double类型的数据,所以没有办法用数组直接记录电话号码薄中的内容。 再试试元胞数组,我们在第1行预先声明一个 3 X 3 的元胞数组,然后在2-4行按顺序填放号码簿的内容。# S' {1 e: {( P
% 元胞数组初始化
& X5 b3 ~9 L! D, maddressBook    = cell(3,1);    % 预分配大小是MATLAB编程的好习惯 & x( y- |6 Y7 t3 }) w7 o$ V- n
addressBook{1} = {'Abby',     '5086470001'};
; ^8 z) ^7 f' m# DaddressBook{2} = {'Bob' ,     '5086470002'};2 h. S0 z) x) }' C
addressBook{3} = {'Charlie',  '5086470003'};      & h/ h! M. z) m5 E) j0 }
需要的时候,可以通过for循环访问其中的内容,比如:
& F! X  C3 G( O! bfor iter = 1:length(addressBook)         % 元胞数组的遍历
: n2 K' R8 A; e: E   addressBook{iter}               % 通过Index访问元胞中的内容1 [) V! g0 V' o
end
+ l% a" g8 g; a9 e4 [5 d# G但是按照顺序遍历电话号码簿没有什么实际用处,号码簿的主要功能应该是提供查找的功能才是。比如要想查询Charlie的电话号码,我们希望程序最好可以写成如下这样:
- m5 V. ^+ {$ ^" aCharlieNum = addressBook{'Charlie'}     % 提示:这是错误的写法9 C# S0 Y/ L2 [" o" @. L  r  X
或者9 [4 P; l# F' a* K3 y
CharlieNum = addressBook.Charlie        % 提示:这是错误的写法2 L0 M' V1 ^" s- @
但是元胞数组的值只能通过Index去访问内容,不支持如上的访问方式。所以为了找到Charlie的电话号码,程序不得不遍历元胞中的所有内容,取出每一个元胞元素的第一列的字符串做比较,如果名字等于Charlie,则输出电话号码:
; _" Q6 f& r. g8 s, S% 使用for循环查找4 C3 |( S2 T; \
for iter = 1:length(addressBook)
1 a5 Q: J0 ~4 f. A6 t  C   if strcmp(addressBook{iter}{1},'Charlie')! P7 V; s# }; V% j( a; A
        addressBook{iter}{2}              % 如找到则输出电话号码
* {: A1 X2 Z+ L! y1 h8 t      break;8 t, Z* R0 {, l, z2 R, H$ H
   end. j& q, d" |6 j. V+ Y
end
- u/ u) C' ]4 a& s4 F当然还有其他的方式来盛放电话号码簿,比如把电话和名字分别存到到两个元胞数组中去
3 L; q; I5 @. T% ynames   = {'Abby','Bob','Charlie'};                 % 用元胞放号码/ R! U. }  c. N  x* G8 E
numbers = {'5086470001','5086470002','5086470001'}; % 用元胞放名字
+ D( M) H- V' u" I4 G+ ^/ Aind = find(strcmp(names,'Charlie'));  % strcmp接受向量的输入 返回Logical数组
, x0 B. m3 }: ]+ n8 u! ~" k                                      % find紧接着找出逻辑数组中非零元素的Index  @3 x* W6 a6 I
numbers{ind}  
1 p5 [4 ~* i& p& {其中第3行strcmp接受元胞作为输入,在其中寻找Charlie,find函数将返回Charlie所在的位置,这样的方式比使用for循环要快,但无一例外的是,两种方法都要从头开始遍历一个数组,终究来说是慢的。 除查找性能慢外,使用元胞盛放电话号码簿类型的数据还有其它缺点:. Y2 N3 i* J. ]- o" K1 f
无法方便的验证重复数据。电话号码簿要求每一个人的名字都是独一无二的,所以在数据录入的时候要防止姓名的重复,但是我们没有其它办法知道某名字是否已经被使用过了,除非在每次输入的时候都对整个元胞里的内容做遍历比较。
3 `8 G* j7 V% N: Z& \无法方便地添加内容。如果电话号码簿中的记录需要不断地增长,但是我们没有办法在一开始就估计出其大概的数量,于是无法有效的预先分配内存,所以添加数据时,一旦超过预先分配的量,MATLAB就要重新分配内存了。
' O$ w* x0 _0 t无法方便地删除内容。如果我们要从元胞中去掉某一记录,可以找到该记录,并把该位置的元胞内容置空,但这并不会自动减小元胞数组的长度,如果 这样的删减操作多了,元胞中会留下很多没有利用的空余位置。9 `5 t4 `, X: E( m5 C9 w6 V/ H9 }
不方便作为函数的参数,具体原因见struct的局限性.
' U" U, O0 R4 ~" k$ B0 m7 X6 M最后我们再尝试一下用结构体盛放电话号码簿数据类型,struct的赋值很简单,比如可以直接赋值:
6 o; l* g. N" ~' g  c( t4 x$ D- \% 赋值方法11 G9 @8 A" w/ |( |" _. K7 t, }
addressBook.Abby   = '5086470001';
( i! ^/ v1 R6 P8 [addressBook.Bob  = '5086470002';/ x1 U; c+ b' E
addressBook.Charlie  = '5086470003';
% w  q2 o( _" p! r; J) q或者:
1 m$ M, q& b' W1 _9 }2 \% 赋值方法2! }" v7 K( i3 b/ M, z
addressBook = struct('Abby','5086470001','Bob','5086470002','Charlie','5086470003')+ J/ r, Y9 g& F( M4 R/ n! o
方法1和方法2是等价的。 struct数据类型的查找很方便,比如要查询Charlie的电话号码,直接访问struct中的同名的field即可以了。: ^$ U# y4 T9 q' d. V8 C
num = addressBook.Charlie  5 a* _8 f3 @) z4 E4 J' B
如果要查询的人名是一个变量, 我们可以使用getfield函数:$ H7 [6 d$ D) \2 F0 l' m" z
num = getfield(addressBook,name)  % 其中name是变量   ; G- Y- r8 J) _
struct盛放电话号码簿类型的数据,查询起来确实比元胞进步了不少,但还是有些不方便的地方。 因为struct的field的名字要求必须是以字母开头,这是一个很大的局限,并不是所有的类似电话号码簿的结构都可以用人名做索引,比如账户号码簿,股票代码等等,他们通常是用数字开头的,比如图Table.2中的数据:$ P  q% h1 p3 |
Table.2 深证股票代码; n) p- z) `5 N0 r# e; M( B
4 b) q, G# p* f* o
股票代码        股票名称& E3 x! j9 T& v( v+ J
000001        深发展
% m* a: ]9 G+ A# i000002        万科
+ u, V% i" h, _# \000004        ST国农 如果我们要求通过股票的代码来查找股票的具体信息,struct无法简单的直接做到。 使用struct的另一个不方便之处在于,当把struct当做函数参数,并且在函数内部需要对该struct做一定的修改时,为了要把修改后的结果返回,我们需要对原来的struct做完全的拷贝,显然如果struct中的数据很多的话,这样做是低效的。比如下面的函数中,addressBook被当做函数的参数传入,在函数中添加了一个新的field, 为了返回更新后的结构,函数内部必须重新构造一个新的struct,也就是s返回给该函数的调用者。
- b/ G% D2 Y5 P' {8 U/ m4 H% 把struct当做函数的参数   
5 w2 E% [. ^6 S6 U; Ufunction s = modifystruct(s)8 F+ t3 ?+ T* N* p* D' j, H
    s.Dave =  '5086470004';  G; _( L" |* ^
end
# N! ]4 p8 v9 `9 _- N用containers.Map来记录电话号码簿
9 C, M/ [2 C( r% U上面一节我们介绍了数组,元胞数组和结构体在模拟电话号码簿这种数据结构时的局限性,这节我们来看怎么用 containers.Map 来盛放电话号码簿中的内容:
5 l% u+ i. a% G# O; Z% waddressMap = containers.Map;             % 首先声明一个映射表对象变量' k. p4 ]0 m8 M1 t4 y
addressMap('Abby')    = '5086470001';7 P2 c# e# J* n( j
addressMap('Bob')     = '5086470002';! K) J* T0 c% @: ~
addressMap('Charlie') = '5086470003';  + ^! L) x! P+ `% j
第一行我们声明一个containers.Map的变量(其实是containers.Map类的对象)叫做addressMap,2,3,4行通过提供Key Key Value的方式来给对象赋值,其中单引号内部的值叫做键(Key),等式右边的叫做键值(Key Value)。通过这个结构,我们在MATLAB内存中,建立起了如图Fig.3的映射关系数据结构。
" R! Z6 M( x. c4 [/ v" aFig.3 电话号码簿映射表
" F; i. d/ ~4 A" y3 R, ?9 G. XFig.4 Map类的UML 查找addressMap对象中的内容极其方便,比如查找我们想知道Charlie的电话号码,只需:
( e% g9 m% b) `3 A1 @1 U! C% 查找  
9 n" r, @  v8 l" W6 Q/ Hnum  = addressMap('Charlie')  ' H9 M6 _+ |# C9 |% u  S
如果我们要修改Charlie的电话号码,只需 :
( @0 L0 _: t8 X# }' C  K9 Y% 赋值
1 H/ \" ^2 T( C+ k9 g9 q. e7 S; CaddressMap('Charlie') = newNum;  
  f1 H* X+ `2 `' k& `什么是containers.Map
& a2 P5 p  R: ~' Z# b9 Icontainers.Map是一个MATLAB的内置类(类是面向对象编程中的一个基本概念,在这里可以把类暂时理解成一种数据类型),所谓内置,就是MATLAB内部实现的,通常这种类的性能更加的优秀。containers.Map其中containers是Package的名称,Map是该Package中的一个类,Map是它的类名。用UML(Unified Modeling Language)的语法把该类表示出来,它的属性包括Count, KeyType,ValueType。它的常用方法包括keys,values,isKey,remove。如图Fig.4所示。
# `# F, m* X/ U, tcontainers.Map的属性和成员方法; E" M4 |+ [0 @$ O, ^) k
这节我们介绍containers.Map的属性和成员方法,假设我们这样初始化一个containers.Map对象:
! J6 j' `" f9 S1 s% 初始化一个Map对象
( S. U& Y9 K1 V% E9 Y! K3 Y# }addressMap = containers.Map;
  y: e0 g8 i( E& f) maddressMap('Abby')    = '5086470001';; F* n! ^! }  K" q) ~+ u! c1 v
addressMap('Bob')     = '5086470002';6 ]( z, k! J2 |
addressMap('Charlie') = '5086470003';: a" U' d) z4 t5 K6 r, O/ g/ v
在命令行中键入该对象的名称,MATLAB会显示该对象属性的基本信息:* l+ P+ J% R6 S
>> addressMap
6 d  Q& R; E# m7 X: \addressMap =! K& ?; q& i8 ], j  N
  Map with properties:) H+ Y& w9 d2 M9 Y) w! a7 [
        Count: 3          % MAP 中映射对的数目
* Q# P1 K  T" f' E1 h1 |5 T7 p      KeyType: char       % MAP 中键的类型 8 o/ u+ m9 n2 X3 D
    ValueType: any        % MAP 中键值的类型   
+ a1 U0 h. e( y& k3 M其中Count 表示Map对象中映射对的数目。按照规定,键的类型一定要是字符类型,不能是其它数据类型,而键值的类型可以是MATLAB中的任意类型:数组,元胞,结构体,MATLAB对象,甚至JAVA对象等等。因为键值的类型可以是任何MATLAB类型,所以 containers.Map 是MATLAB中极其灵活的数据类型。 成员方法keys 用来返回对象中所有的键:& D" [5 O# ]0 z
>> addressMap.keys
$ \, v6 z3 O# J* l: t+ ians =
! e$ A4 T: d9 m2 `& a! M8 K% A    'Charlie'    'Abby'    'Bob'  
2 V7 }2 s7 O) b成员方法values用来返回对象中的所有键值' R- @! B6 `/ P  N) f3 j$ N
>> addressMap.values
5 S- }! `. f# x( k$ ?ans = 7 Y7 w: g, U9 P2 n3 e
    '5086470003'    '5086470001'    '5086470002'  3 C5 k2 `* k+ G
remove用来移除对象中的一个键-键值对,经过下面的操作之后,该对象中的Count的值变成了2。
! x! j! Z& p: k0 s>> addressMap.remove('Charlie')3 I7 S/ K2 W/ N" e3 D" ~
ans =
9 `8 L/ l3 {) F% [# K4 B  Map with properties:
3 z& f  w9 E$ }' Z+ E1 {1 H        Count: 2          % 映射对数目减少
8 Z) U$ s! E. j) W2 Y      KeyType: char
; p1 Y! y* ^# Y$ J% P    ValueType: any0 M: p9 M$ ^# K% u8 t# l
  ) e8 \3 }: B$ o) {
isKey成员方法用来判断一个map对象中是否已经含有一个键值,比如:
6 U/ u. N: h# \1 a( G! n>> addressMap.isKey('Abby')
" f- F+ D, g: T  j9 @, _ans =# l7 G$ `8 S8 f. y4 a
     1* p- ~$ y8 G. f8 T$ Z1 M
>> addressMap.isKey('abby')    % 键是大小写敏感的
5 f5 T. `0 Q) x! Xans =- _: p8 K1 [$ J& M: b* V
     0  & D4 z; a6 g4 W( |2 H" d
isKey的功能是查询,类似之前提到的find和strcmp函数合起来使用的例子。
8 ]0 k# @+ ^# J" N: _containers.Map的特点, f# w: i3 i% ^; J- y# \
containers.Map可以不断地扩张不需预分配
( q6 Y  x: N& X4 `/ B' o8 @! {' l使用数组和元胞数组作为数据容器,通常要预先分配容器的大小。否则每次扩充容器,MATLAB都会重新分配内存,并且拷贝原来数组中的数据到新分配的内存中去。映射表是一个可以灵活扩充的容器,并且不需要预分配,每次往里面添加内容不会引起MATLAB重新分配内存。 我们可以通过提供两个元胞来构造映射表对象,比如下面我们构造一个机票簿数据结构:9 [. {% `/ a9 B9 V! f3 W
% 映射表的初始化方法1
" s, d  w; \8 E4 yticketMap = containers.Map( {'2R175', 'B7398', 'A479GY'}, ...2 o5 q# T, G1 ^. L8 d3 D- t
                            {'Abby', 'Bob, 'Charlie'});
# z+ M: p, m+ z0 i, f  
7 U! K* H* G1 b! i2 r' Z( L/ h也可以在程序的一开始,先声明一个对象:
+ Q# v" S; l+ v, _2 H% 映射表的初始化方法2  
7 I, ^" ^3 ?8 I. `$ v* F# y0 P  U>> ticketMap = containers.Map 4 I1 A5 Q! f0 L
然后在计算的过程中慢慢的向表中插入数据:; H# a5 Q) W$ e- k
% 映射表的初始化方法2  ; h; f2 A% a0 A" s/ S
>> ticketMap['2R175'] = 'Abby';0 |/ }0 \  V2 T& y
...* b# {$ d! a- F6 ?0 e5 s
>> ticketMap['A479GY'] = 'Charlie;  0 j0 X! v! X. U( `6 O/ c1 y
containers.Map可以作为参数在函数内部直接修改
# t, k+ X$ e! n( B因为containers.Map是handle类(handle类的介绍参见《MATLAB面向对象编程-从入门到设计模式》第三章:MATLAB的句柄类和实值类),我们还可以方便的将这个对象传给任何MATLAB的函数,并且在函数内部直接修改对象内部的数据,并且不用把它当做返回值输出,比如:
4 M/ K8 Y& L4 v) P>> modifyMap(ticketMap);  + d# J8 y+ M9 N" k2 Y5 Q
modifyMap函数可以写成:
$ G! H( Q. O# I% lfunction modifyMap(ticketMap)   % 该函数没有返回值
1 E& j8 z& I( Q( k6 u, `   .....* w. `1 v1 |5 {
   ticketMap(NewKey) = newID
0 a+ ]+ T- d7 `' Y8 mend  
: x* Q7 t  u+ y$ a. W. }1 b注意这里没有把修改的ticketMap当做返回值,在函数中我们直接修改了Map中的数据,这是MATLAB面向对象语言中handle类的特性。
% [( u# Z; y& X2 C* ccontainers.Map可以增强程序的可读性
6 m; Q2 f* ]# J8 ~# _" B映射表内部可以存储各种类型的数据,并且给它们起一些有意义的名字。具体的例子见元素周期表的例子。 访问和修改这些数据的时候,可以直接使用这些有意义的名字,从而增加程序的可读性。而如果用元胞数组存储这些数据,在访问和修改这些数据的时候, 需要使用整数的Index,程序可读性不够好。
# l' I- l7 p* i, J( j5 m( fcontainers.Map提供对数据的快速查找
4 |  Q; W0 t- |3 Q1 \$ A映射表查找的复杂度是常数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都相同。
8 g6 m0 E1 A2 Y0 L2 w( Va = 1:1000000;2 j' T5 n( I& V( v# z
m = containers.Map(1:1000000,ones(1,1000000));  & w6 T5 _: B* L
数组数据结构,使用find函数依次查找从1到(10^7)的整数,在笔者的机器上,耗时28.331901秒
: V, J  m. y4 q. Z" n/ ?% array的查找
- u3 H. {, p& C3 S0 Itic5 b! E4 }5 ^4 l
for i=1:100000, 4 F5 B+ u  z( z2 h. Y0 }! y! ^
    find(b==i);0 B2 Q; u2 S5 F0 S1 F
end;
: z( ?+ ~5 L$ H2 Y8 Dtoc  " R  R% f! }5 Q. I
% command line结果& R2 Z6 M  l% N: v6 F/ _
0 D. l4 R6 C+ b3 Y/ g. v
2 n' R% h) }; |- @2 l
8 Z0 ]; X8 g( t. t% G

0 E& l8 o; Q3 p0 _$ KElapsed time is 28.331901 seconds.  - F, X) t: t: d0 x
containers.Map数据结构,使用键值1到(10^7)进行访问, 在笔者的机器上,耗时只需1.323007秒, 结论是:如果有大量的数据,并且要频繁的进行查找操作,可以考虑使用containers.Map数据结构。(读者可以试试减少容器中元素的个数,观察一下什么情况下,数组的查找会更快一些。)
+ `. D8 C# @; Q# b- q- |% 映射表的查找
/ i3 r1 j4 [, r+ z0 F& C$ A: ], s- w* Btic6 T* ^$ A1 j# [9 H# U6 s
for i=1:100000,
, {1 e' k3 S" R$ O    m(i);4 ^9 P' v. y6 O8 {* }
end; 0 n5 F* `0 f$ {  u% P3 V8 o1 p% L
toc
+ `3 _) i( J$ z% command line结果      0 f1 P7 d- H9 ^
1 ~3 c5 c: W4 s: B0 B, d$ P7 r
; L) V+ g5 `0 \% @

, S2 a1 ^" Q1 r; a3 Y: `
6 c5 Q4 @1 x8 fElapsed time is 1.323007 seconds.2 G  n$ I& X3 G. C
谈到在MATLAB中使用高级数据结构,另一种常见的做法是直接使用Java和Python对象,这里顺便比较一下在MATLAB中使用Java的哈希表的效率。再笔者的机器上,耗时3.072889秒.) J' Q* B) F: b" Z! S' @5 {9 @
% JAVA哈希表的查找- V! s: B5 c0 B& A+ |  L  J# O
s = java.util.HashSet();; T- f% [2 I* t+ T  S0 o3 W
for i=1:100000, s.add(i); end   3 {, f- S4 I! `) o
tic; " g$ _  J2 t; J) f0 t: N5 S
for i=1:100000,
# F! w6 G# A1 {- \( g: g4 n    s.contains(i);
. e' d, i: y* C: ]  xend; $ v' N, s7 }  p7 G! `+ t' @
toc
) o8 @8 Y% ~' N& s( G7 Z% command line结果      ) V; b0 b1 G8 q3 }2 z; a' q+ R+ y) A
1 i! H2 P6 J5 ?

4 M0 B* ], _+ i$ T6 F4 p; e: [; c8 u" D

1 m; Y8 L* g  s8 B5 ]8 E5 U
- F5 Y; O7 C7 d" ]! \! s. E
2 U3 H4 U1 W3 }; M  g% j* ~5 DElapsed time is 3.072889 seconds.5 K& @! c5 e9 V7 ^. A3 \
containers.Map的使用实例
$ R4 H/ h! N+ t" ~用来盛放元素周期表$ @& S. z3 z9 @
工程计算中可以使用这种键-键值的例子有很多。比如物理化学计算中可以用映射表来盛放元素周期表中的内容:5 O2 }! B9 }  ^* ]2 S2 K7 K' d/ L3 b
>> ptable = containers.Map;! t8 l- j& u% Q
>> ptable('H')  = 1 ;. ]2 q1 n, \2 p+ V
>> ptable('He') = 2 ;  
. D6 p" H) O, \; a" {其中键是原子符号,而键值是原子量。因为键值的类型不限,所以键值本身还可以是对象,比如Atom类对象,其中包涵更多有用的内容:5 S' ?9 |6 \' D+ @$ S" {" |
% 类文件Atom.m* c: M+ |5 M3 W: i
classdef Atom < handle, Z) A& Q( l# V4 J
  properties/ j" `* g0 o( `9 F0 F! Y6 f$ w$ t
      atomicNumber0 T2 f: u/ S9 S* c3 N
      fullName
/ w3 z9 {/ E9 w# m3 ?  end0 V7 X/ e: q% m  x6 z* p
  methods
0 ^9 p  N7 m( E& M    function obj = Atom(num,name)- d' ~% F0 O" a  D
       obj.atomicNumber = num ;1 v( f+ a) ~% H5 D
       obj.fullName = name
; C" J& l1 r; g, u  |& Q( V$ `3 ^; H    end
" k! K, B9 I! A: [/ a& x  end+ b( P5 [8 L) w* s
end  ' ~; U% P0 I+ T" u
\end{Verbatim} 于是:7 W) y2 Q, A  X' j/ U5 V0 L
% 键值可以是Atom对象                  
$ [( h! z4 X, g6 Y* d" [) C# H>> ptable('H') = Atom(1,'hydrogen');9 F9 I" q0 t, z- S
>> ptable('H') = Atom(2,'helium');  
, \! P" v3 L9 l+ P用来实现快速地检索
* _7 D, O/ o+ a这个问题来自ilovematlab一个网友的提问:
% }. s, w1 H6 ~+ l6 h' t2 m2 q大家好,最近遇到一个问题,要构建20000次以上的三元素向量,且保证每次不重复构建,该向量元素值在2000―3000之间不定数,目前采用的方法是建立一历史档案矩阵A,构建一个存储一行,A大小行数持续增加。每次在构建出一新的向量时对该向量与历史档案矩阵每行进行对比,最终确定是否构建过,若没构建过,重新构建。算法显示该检查程序运算时间在执行20000次以上时,会超过200S的运行时间。想力争减小该时间,求方法。 多谢!+ N2 H: o1 S& U( h8 L+ K
这位网友的问题不在于如何构建这些向量,而是如何保证每次不重复的构建。他一开始想出的解决方法是用矩阵A来存储历史档案,每建立一个新的向量,和该历史档案已有的内容做比较,如果在档案中没有存档,则构建。这样设计的问题是,如他所述,当A中存储的向量变多之后,每次检查该向量是否之前被创建过的时间加长。 这是一个典型的可以用映射表来解决的问题,把线性的复杂度转成常数的复杂度。他可以给每个向量设计一个独立无二的ID,比如直接转数字成id : num2str(vector)) O# U- I. j7 l! R
% 构建
* ^0 ~1 A6 K9 M1 o9 Xmydictionary = containers.Map
) R* Q5 v% I1 o. z! r9 M# J9 o4 l5 }6 Z% V, w
% 插入数据
8 y; w0 ?' ~9 N- v8 ?- i# x# U- v( cid = num2str(vector) ; % 比如vector = [1 2 3];
4 ?, v. r) Y  r5 i+ o# ]$ wmydictionary('some_id') = vector ;  5 Y/ L7 M" f, x6 {- q
之后的检索也是通过vector得到独一无二的ID,然后通过isKey来判断该键值是否已经被加入到MAP中去了。
/ {' K; ?0 [# d9 [, ^some_otherID = some_other_vector ;
& n  o! G4 v! r; X* c! m1 I% 验证是否已经构建给过
# Q& q6 ~. k! `+ W$ {% l% k$ T; j% b5 xif mydictinary.isKey('some_otherID')
8 z; \3 G0 J7 s0 s7 X: O      % 做要做的工作; M! P: R! `. L/ ?9 N* n% E$ x- K
end  
  • TA的每日心情

    2019-11-29 15:37
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2020-11-30 16:47 | 只看该作者
    MATLAB映射表数据结构
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-24 10:48 , Processed in 0.171875 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表