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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x

& d0 B8 m( ]8 G0 ^7 y4 D目录:
4 U  e# Z! @' [& v  _, Q9 S  E* m' m9 N& `( `& r, l3 j
containers.Map简介/ T# f: `+ \8 ^9 e# e5 j9 k
数组,元胞和结构体的局限性
# H& {3 b' ^& m; D什么是containers.Map" K5 o" Y' s3 Y! Y% g% H
containers.Map的属性和成员方法
1 K( I& r  s- q1 q9 A1 S& o; Lcontainers.Map的特点
* f9 `: V( u9 _& M; u$ Zcontainers.Map可以不断地扩张不需预分配+ Y+ C5 F/ N& M7 O( ]
containers.Map可以作为参数在函数内部直接修改
" T0 e/ P3 K& z0 E5 I3 Tcontainers.Map可以增强程序的可读性
0 f+ A- R+ Z3 q% econtainers.Map提供对数据的快速查找
; O5 q- {7 [1 }9 N/ A" U7 |containers.Map的使用实例" p! j8 w( ?4 H' t% f4 b0 c
用来盛放元素周期表
# x) [; W( g5 W用来实现快速地检索
0 Q1 ^% @& F6 M/ hMATLAB常用基本数据类型有:整型,浮点型,字符型,函数句柄,元胞数组和结构体数组。除了这些基本数据类型,MATLAB还有很多其它的数据类型不为人熟悉,这些数据类型在编程中也非常有用。MATLAB高级数据类型系列旨在向大家介绍它们:比如 containers.Map,tables,enumeration和time series等等,它们为什么有用,用来解决什么问题,并且怎样在科学工程计算使用。本篇首先介绍 containers.Map 数据类型。: O$ h, r6 h  R
containers.Map简介
$ Y' K$ J+ X( X5 L! dMATLAB中最具代表性的高级数据类型是containers.Map,我们可以把它叫做映射表。它和函数映射有些类似,比如函数映射的定义是:
" u, `4 B4 g! u4 fF(x) = Y
2 I( y4 G" H6 Z" Z$ T; o针对每一个X,都有一个唯一的Y与之对应,反之不一定。如图Fig.1所示。和函数映射相似,映射表用来形容键(Key)和键值(Key Value)之间的一一对应关系。每个Key都是独一无二的,且只能对一个Key Value。如图Fig.2所示。
5 ^" R3 X8 P% {Fig.1 函数映射关系* o0 C1 g3 d6 a
Fig.2 containers.Map类的映射示意图
- y  L6 W; E, ~5 k; n数组,元胞和结构体的局限性
0 Y, n* R$ g( Z开始我们先介绍一下数组,元胞数组和结构体的局限性,为什么有的时候这些基本的数据类型无法满足程序的要求,换句话说,我们为什么需要 containers.Map 数据结构。假设要用MATLAB来记录电话号码簿中数据,比如表Table.1所示:
% B: m" p! d) _( y: `, d6 OTable.1 电话号码簿8 _7 {2 e6 m. a

# t9 E6 ^" s! e0 F% a/ O1 }姓名        电话号码0 Q9 M. j  J5 I0 w; q+ V6 `
Abby        5086470001, s9 c/ s* E& b3 b( Q& f
Bob        5086470002
, G- ~) w. z9 g  o. B0 q% KCharlie        5086470003 先讨论数组,因为电话号码簿中既含有数字,又含有字符串,而数组中只能存放Double类型的数据,所以没有办法用数组直接记录电话号码薄中的内容。 再试试元胞数组,我们在第1行预先声明一个 3 X 3 的元胞数组,然后在2-4行按顺序填放号码簿的内容。  O' M) I7 w; _8 p& \( N  `
% 元胞数组初始化! k# [: y; @/ Y" c6 \
addressBook    = cell(3,1);    % 预分配大小是MATLAB编程的好习惯 ) ]* w, U% G- e  ]+ v2 e
addressBook{1} = {'Abby',     '5086470001'};0 q4 c- \0 O" Y0 ^- ?2 z7 J1 j
addressBook{2} = {'Bob' ,     '5086470002'};
' o& u7 {: V* p' P. T6 Q( ]addressBook{3} = {'Charlie',  '5086470003'};      2 O7 M/ c% D6 b
需要的时候,可以通过for循环访问其中的内容,比如:- Z& l# ]1 T1 z9 O! @
for iter = 1:length(addressBook)         % 元胞数组的遍历+ I6 U1 H' a/ N0 T3 O% C" {+ P4 S
   addressBook{iter}               % 通过Index访问元胞中的内容
5 X4 H7 }7 C9 b8 eend
, r- _1 ?. |# |8 j但是按照顺序遍历电话号码簿没有什么实际用处,号码簿的主要功能应该是提供查找的功能才是。比如要想查询Charlie的电话号码,我们希望程序最好可以写成如下这样:0 ]$ g" X( o7 _! s
CharlieNum = addressBook{'Charlie'}     % 提示:这是错误的写法
( e6 ~1 q4 Q% m1 ^5 w4 M或者
( G- j6 L3 c. M4 `CharlieNum = addressBook.Charlie        % 提示:这是错误的写法
! A( e% B! }/ H+ d8 w' ^9 y但是元胞数组的值只能通过Index去访问内容,不支持如上的访问方式。所以为了找到Charlie的电话号码,程序不得不遍历元胞中的所有内容,取出每一个元胞元素的第一列的字符串做比较,如果名字等于Charlie,则输出电话号码:
" s# _3 E0 Z/ Z# K, b6 w% 使用for循环查找( Q" s# I/ _& w, U; ^
for iter = 1:length(addressBook)
: d4 c$ \5 l- {7 a- s   if strcmp(addressBook{iter}{1},'Charlie')5 q( p' q5 S7 c9 V' H! l
        addressBook{iter}{2}              % 如找到则输出电话号码
% u! s- R# Y5 e% m$ n      break;  E: ~$ d+ V" ~  T$ N1 w
   end. v7 V* m, S7 c: d# y  t1 a  d! @
end4 Q/ R7 b% `) l% k9 H
当然还有其他的方式来盛放电话号码簿,比如把电话和名字分别存到到两个元胞数组中去
0 H: d3 k* y& c8 onames   = {'Abby','Bob','Charlie'};                 % 用元胞放号码; z! q% r+ ^( i
numbers = {'5086470001','5086470002','5086470001'}; % 用元胞放名字1 J; n! v% i3 t" {) D- q( W
ind = find(strcmp(names,'Charlie'));  % strcmp接受向量的输入 返回Logical数组
5 W. _7 m' P/ Z' _; b                                      % find紧接着找出逻辑数组中非零元素的Index
- ^6 ^/ G6 g- x' E$ V6 Cnumbers{ind}  
$ Y) r) a$ w) _7 s# T其中第3行strcmp接受元胞作为输入,在其中寻找Charlie,find函数将返回Charlie所在的位置,这样的方式比使用for循环要快,但无一例外的是,两种方法都要从头开始遍历一个数组,终究来说是慢的。 除查找性能慢外,使用元胞盛放电话号码簿类型的数据还有其它缺点:. u8 V% P, D* M6 E% L3 x  s
无法方便的验证重复数据。电话号码簿要求每一个人的名字都是独一无二的,所以在数据录入的时候要防止姓名的重复,但是我们没有其它办法知道某名字是否已经被使用过了,除非在每次输入的时候都对整个元胞里的内容做遍历比较。& b! j( r& u* M- Z
无法方便地添加内容。如果电话号码簿中的记录需要不断地增长,但是我们没有办法在一开始就估计出其大概的数量,于是无法有效的预先分配内存,所以添加数据时,一旦超过预先分配的量,MATLAB就要重新分配内存了。
* z9 I3 N' U/ S2 O无法方便地删除内容。如果我们要从元胞中去掉某一记录,可以找到该记录,并把该位置的元胞内容置空,但这并不会自动减小元胞数组的长度,如果 这样的删减操作多了,元胞中会留下很多没有利用的空余位置。
% [$ S& p; w/ {3 f$ f* q1 d不方便作为函数的参数,具体原因见struct的局限性.
# Q; l; h3 `* h* G! v最后我们再尝试一下用结构体盛放电话号码簿数据类型,struct的赋值很简单,比如可以直接赋值:- M9 ~4 p7 L/ l9 b
% 赋值方法1
1 `0 Z4 Y' p  V1 m8 gaddressBook.Abby   = '5086470001';
. c8 d8 W6 T  j. P* r2 haddressBook.Bob  = '5086470002';
3 M; u' F' C5 U9 o% jaddressBook.Charlie  = '5086470003';
" w# t( n2 m8 s! l) w4 T! \或者:7 ]6 s- v: Y% e6 z, e; b) O9 U2 P
% 赋值方法2
+ x% |- R: w& `  P: `; W1 yaddressBook = struct('Abby','5086470001','Bob','5086470002','Charlie','5086470003')
! p7 `; y, d" U# H方法1和方法2是等价的。 struct数据类型的查找很方便,比如要查询Charlie的电话号码,直接访问struct中的同名的field即可以了。8 U0 \, i, n6 @
num = addressBook.Charlie  
9 R- T4 G/ B! ?4 G4 E如果要查询的人名是一个变量, 我们可以使用getfield函数:
$ {1 y/ q8 _" g; S8 \num = getfield(addressBook,name)  % 其中name是变量   ; N) r" h. N6 o  c1 p) V5 Q
struct盛放电话号码簿类型的数据,查询起来确实比元胞进步了不少,但还是有些不方便的地方。 因为struct的field的名字要求必须是以字母开头,这是一个很大的局限,并不是所有的类似电话号码簿的结构都可以用人名做索引,比如账户号码簿,股票代码等等,他们通常是用数字开头的,比如图Table.2中的数据:
) s) }; Q6 ^/ I1 kTable.2 深证股票代码7 i: U. g) }8 L, B& V

) W" h! s2 r! v/ O股票代码        股票名称
8 T" k. J4 g; j" U) n2 Q000001        深发展
; ]6 J' m' m1 h/ c000002        万科
4 B0 W6 f/ G; E- X" E* u! q000004        ST国农 如果我们要求通过股票的代码来查找股票的具体信息,struct无法简单的直接做到。 使用struct的另一个不方便之处在于,当把struct当做函数参数,并且在函数内部需要对该struct做一定的修改时,为了要把修改后的结果返回,我们需要对原来的struct做完全的拷贝,显然如果struct中的数据很多的话,这样做是低效的。比如下面的函数中,addressBook被当做函数的参数传入,在函数中添加了一个新的field, 为了返回更新后的结构,函数内部必须重新构造一个新的struct,也就是s返回给该函数的调用者。
- o, z6 X( f9 m1 ~% k7 S% 把struct当做函数的参数   
2 s: z4 r% E8 \9 Ufunction s = modifystruct(s)
  S7 t4 D! Z; j7 e    s.Dave =  '5086470004';! {) [3 l1 V$ G2 N7 o, ^5 S
end: L. w: S9 r. h/ J: }8 W- G7 R
用containers.Map来记录电话号码簿% g% H  @, S, o2 m0 M+ o* O. C
上面一节我们介绍了数组,元胞数组和结构体在模拟电话号码簿这种数据结构时的局限性,这节我们来看怎么用 containers.Map 来盛放电话号码簿中的内容:
. W( p6 h( P" ~; D' L% \( ~8 kaddressMap = containers.Map;             % 首先声明一个映射表对象变量
, N  {4 R6 M( A0 z5 w1 \addressMap('Abby')    = '5086470001';  K4 l( c3 C$ H/ R  a
addressMap('Bob')     = '5086470002';
9 O& W# F7 M6 ^addressMap('Charlie') = '5086470003';  
3 O, y& [: ]2 g$ c/ y第一行我们声明一个containers.Map的变量(其实是containers.Map类的对象)叫做addressMap,2,3,4行通过提供Key Key Value的方式来给对象赋值,其中单引号内部的值叫做键(Key),等式右边的叫做键值(Key Value)。通过这个结构,我们在MATLAB内存中,建立起了如图Fig.3的映射关系数据结构。. W$ t/ B3 N% V: O; u2 K" F
Fig.3 电话号码簿映射表
& ]5 _' R, M, u/ X4 o0 j* FFig.4 Map类的UML 查找addressMap对象中的内容极其方便,比如查找我们想知道Charlie的电话号码,只需:
6 C5 _1 x" q9 @+ }/ N- b7 M8 }% 查找  
5 t4 ?- Y& a! h3 i$ \num  = addressMap('Charlie')  
# m& G3 F6 Y' I7 @- M6 v如果我们要修改Charlie的电话号码,只需 :
4 j4 v5 a9 K0 e9 O; o% 赋值- q2 M9 f$ ]' X$ z
addressMap('Charlie') = newNum;  
$ s+ I4 N" a, z什么是containers.Map
# W" Z$ U2 J1 l1 m8 D# Pcontainers.Map是一个MATLAB的内置类(类是面向对象编程中的一个基本概念,在这里可以把类暂时理解成一种数据类型),所谓内置,就是MATLAB内部实现的,通常这种类的性能更加的优秀。containers.Map其中containers是Package的名称,Map是该Package中的一个类,Map是它的类名。用UML(Unified Modeling Language)的语法把该类表示出来,它的属性包括Count, KeyType,ValueType。它的常用方法包括keys,values,isKey,remove。如图Fig.4所示。- U1 Q2 ?9 ~9 T& `* ]
containers.Map的属性和成员方法
  w; {* C' y. L, A. f+ ~这节我们介绍containers.Map的属性和成员方法,假设我们这样初始化一个containers.Map对象:
2 [  l# z3 c2 l1 Q% 初始化一个Map对象
! e% q: I; t% Z3 i6 W# ]addressMap = containers.Map;$ L" g8 e+ z: v. w. Q
addressMap('Abby')    = '5086470001';
/ d  v, q( U5 S: NaddressMap('Bob')     = '5086470002';
, D# D/ o6 N2 {9 [' g& jaddressMap('Charlie') = '5086470003';
/ U' g' V5 A  @4 S在命令行中键入该对象的名称,MATLAB会显示该对象属性的基本信息:$ g: ~. z4 y  Z# g$ o, W: f% y
>> addressMap
" R4 i/ f4 J0 H/ g, Y2 XaddressMap =' ^- G% b$ D' ?! o4 E- e
  Map with properties:( C& K  V* k" d' c7 \( ~
        Count: 3          % MAP 中映射对的数目
' J& y- i/ B' M+ K6 P& P( _      KeyType: char       % MAP 中键的类型
# Q5 B: W$ N6 R/ w1 @    ValueType: any        % MAP 中键值的类型   
5 U- V( Q! E' V1 U5 v, Z' h其中Count 表示Map对象中映射对的数目。按照规定,键的类型一定要是字符类型,不能是其它数据类型,而键值的类型可以是MATLAB中的任意类型:数组,元胞,结构体,MATLAB对象,甚至JAVA对象等等。因为键值的类型可以是任何MATLAB类型,所以 containers.Map 是MATLAB中极其灵活的数据类型。 成员方法keys 用来返回对象中所有的键:
* {# V$ z( i% a0 r9 e5 E' T* C>> addressMap.keys
3 H+ O# v8 _, `, |. l$ g( N/ |ans =/ k3 W" ]! [3 x, \. N$ |7 |. p
    'Charlie'    'Abby'    'Bob'  
$ P( G5 i7 {: S8 V& M! o( D* e. w成员方法values用来返回对象中的所有键值
6 Q7 f' o" x- E" L9 K. C, s; W>> addressMap.values
& z8 g7 D1 c; `. h% r) Tans =
4 y& P, _: c+ I. L- B2 p: i+ b    '5086470003'    '5086470001'    '5086470002'  
# v/ Q, m/ j% W$ N+ Vremove用来移除对象中的一个键-键值对,经过下面的操作之后,该对象中的Count的值变成了2。
# ^1 ?0 n# B: ]>> addressMap.remove('Charlie')
: `. V- K* W5 Z( r# hans = 2 Q5 h& K4 [. H: S: k
  Map with properties:8 Q6 P; N1 q0 t0 }# D% `
        Count: 2          % 映射对数目减少, h& R! A( K; e/ w
      KeyType: char" P' C1 d1 W; `% z
    ValueType: any2 E' w7 ~4 h: Q0 a9 T/ R( f
  
6 w6 b0 l+ H# s) w: O' x# SisKey成员方法用来判断一个map对象中是否已经含有一个键值,比如:5 L6 o8 b* k" M! m
>> addressMap.isKey('Abby')
( `! \1 V7 m7 |! Y% R% C4 G3 uans =
% [9 h- C$ I) Y+ K0 Q9 D  w     1' g$ ~7 V: i: f. t, w4 W1 ]
>> addressMap.isKey('abby')    % 键是大小写敏感的
3 C% q5 j' `6 r. v% K2 a0 I$ Oans =* J7 s) l8 b9 s: l
     0  
+ ?2 u$ d9 g. V0 ~* ?# gisKey的功能是查询,类似之前提到的find和strcmp函数合起来使用的例子。
" D- s1 [8 i; s2 Qcontainers.Map的特点4 t/ W/ d3 D; G3 ]
containers.Map可以不断地扩张不需预分配
# e) p$ S8 d5 t- e使用数组和元胞数组作为数据容器,通常要预先分配容器的大小。否则每次扩充容器,MATLAB都会重新分配内存,并且拷贝原来数组中的数据到新分配的内存中去。映射表是一个可以灵活扩充的容器,并且不需要预分配,每次往里面添加内容不会引起MATLAB重新分配内存。 我们可以通过提供两个元胞来构造映射表对象,比如下面我们构造一个机票簿数据结构:( `: _, d: C: }" q7 R4 \" ]' B: t. a1 o
% 映射表的初始化方法11 |% \% Y0 R. p9 C  V8 u4 Z& K+ N7 N
ticketMap = containers.Map( {'2R175', 'B7398', 'A479GY'}, ...
+ ^2 m3 N( E2 ^- {& i                            {'Abby', 'Bob, 'Charlie'});
+ Q" g' }) |3 k) {5 ^6 N  ) |' Q/ y6 @2 B' v
也可以在程序的一开始,先声明一个对象:
8 V$ U' ?! l" p, B( |% 映射表的初始化方法2  
3 o. p0 N; r1 v" b>> ticketMap = containers.Map 2 t; g; r! J) `8 v
然后在计算的过程中慢慢的向表中插入数据:7 K' H& O* `+ m' I/ {" V  o
% 映射表的初始化方法2  
7 C4 N& |! Z. U6 g>> ticketMap['2R175'] = 'Abby';0 H# _! M# @2 G6 E% g0 U  [
...; l2 g6 m6 m) @# a( f
>> ticketMap['A479GY'] = 'Charlie;  / o! R2 }$ |. e6 T* d
containers.Map可以作为参数在函数内部直接修改- n* y* T8 h# [' j! c# C
因为containers.Map是handle类(handle类的介绍参见《MATLAB面向对象编程-从入门到设计模式》第三章:MATLAB的句柄类和实值类),我们还可以方便的将这个对象传给任何MATLAB的函数,并且在函数内部直接修改对象内部的数据,并且不用把它当做返回值输出,比如:5 D+ _( m& c3 n) r
>> modifyMap(ticketMap);  & I- s# h- q% X; v
modifyMap函数可以写成:
  }: `' N. p/ _; vfunction modifyMap(ticketMap)   % 该函数没有返回值
& T; k2 J( ?$ h: ^   .....; E! ]+ F0 m( f( L# _
   ticketMap(NewKey) = newID / B4 S2 _- {8 B
end  
8 v' O8 p. P5 R5 {' `8 n注意这里没有把修改的ticketMap当做返回值,在函数中我们直接修改了Map中的数据,这是MATLAB面向对象语言中handle类的特性。% o8 G5 ^6 O0 Q; r
containers.Map可以增强程序的可读性
5 b. n3 Q4 x& O$ q/ @映射表内部可以存储各种类型的数据,并且给它们起一些有意义的名字。具体的例子见元素周期表的例子。 访问和修改这些数据的时候,可以直接使用这些有意义的名字,从而增加程序的可读性。而如果用元胞数组存储这些数据,在访问和修改这些数据的时候, 需要使用整数的Index,程序可读性不够好。
( U0 W' U5 g8 Jcontainers.Map提供对数据的快速查找
- A  j$ x& T) t; p# q& z映射表查找的复杂度是常数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都相同。3 J' J5 C. z* T+ B" n# |& f; j) J
a = 1:1000000;
/ R3 H2 o  {. Jm = containers.Map(1:1000000,ones(1,1000000));  
5 o: T+ X8 w) r0 f数组数据结构,使用find函数依次查找从1到(10^7)的整数,在笔者的机器上,耗时28.331901秒
: _$ d1 @. Z$ x, L- _# i, T! S# T% array的查找" ?% U3 e% T! l+ M. I0 A
tic
# g; A8 c5 }3 d+ k1 Gfor i=1:100000,
, x  I0 W* w. R    find(b==i);
- n7 C5 C8 i! w9 }5 Xend; 3 J8 W3 r# e4 K; j+ Y* M! J( C4 T
toc  . n: g# L2 h* i' _1 z) y
% command line结果
6 D0 j1 u) T& n9 e2 i0 i3 b. e0 D+ x% ]

8 W: `3 _6 V! E! X: @! {3 p& {( q8 H& j: M: N: D% c

7 Y8 R/ C# j! s& L3 S( s( S! QElapsed time is 28.331901 seconds.  
: }5 k7 g! y4 I/ ^; Y2 Ycontainers.Map数据结构,使用键值1到(10^7)进行访问, 在笔者的机器上,耗时只需1.323007秒, 结论是:如果有大量的数据,并且要频繁的进行查找操作,可以考虑使用containers.Map数据结构。(读者可以试试减少容器中元素的个数,观察一下什么情况下,数组的查找会更快一些。)
" ?5 R+ W; H9 d! G& o% 映射表的查找& p/ y+ D' @/ h) W1 X
tic$ d6 A: u5 w1 w, R9 C9 K
for i=1:100000, 4 a' u* Q7 a9 Z' U7 F1 T
    m(i);
( g( B# C- e" X* Kend; . N+ Z. i0 @( T! w. m
toc: h# K8 Q1 g5 N; K) x
% command line结果      * J( K6 e$ V$ L, R: i" Q
" ~  v! |7 M7 s$ e  \: S
+ u' E6 M* t4 D( \# Q3 W
+ A3 A7 c' d4 `& ?! o  W5 @
2 t) E3 @& _2 @
Elapsed time is 1.323007 seconds.& j8 @; ?1 t$ |% u+ P5 |" F
谈到在MATLAB中使用高级数据结构,另一种常见的做法是直接使用Java和Python对象,这里顺便比较一下在MATLAB中使用Java的哈希表的效率。再笔者的机器上,耗时3.072889秒.1 ?" H% G# L9 r
% JAVA哈希表的查找
- u/ ?! [3 g7 b* Ps = java.util.HashSet();5 Y5 D, F5 h: J9 G5 H3 \. m
for i=1:100000, s.add(i); end   " O0 Q; t6 ]& q: P' d
tic; 0 d* ~- P0 a, ?$ @4 g' ], {' W8 s+ |
for i=1:100000, 5 \4 S3 V1 f# K+ m
    s.contains(i); ) w% V# J" R5 r, L' z
end;
* d; b: G- W/ l0 z5 M9 qtoc
1 r# a- n& V( S( n0 Y% command line结果      
' \1 A, _. Z* S+ q, `6 [# ?9 L% G. l6 e8 @4 k$ X, Q

1 D# i1 e$ k# k! A( d* z
. r6 \) N; f$ N
) l$ V6 }; _3 z& Q3 ^4 F9 a) a! u, R1 i( c& v

# F( b* u# w1 `+ mElapsed time is 3.072889 seconds.
) f+ |# t) a% e5 ncontainers.Map的使用实例
4 K  {3 `! U- n4 v  b/ O用来盛放元素周期表# H% `9 W2 @; H: D
工程计算中可以使用这种键-键值的例子有很多。比如物理化学计算中可以用映射表来盛放元素周期表中的内容:
! c* o0 i& g: q! N& p>> ptable = containers.Map;
! x, {* }/ }. z. \( {% W; q4 K>> ptable('H')  = 1 ;9 _! K2 {* K+ q+ c6 G+ L# F
>> ptable('He') = 2 ;  
6 ^/ d7 n$ q$ b! v, m- J' L& y其中键是原子符号,而键值是原子量。因为键值的类型不限,所以键值本身还可以是对象,比如Atom类对象,其中包涵更多有用的内容:2 R  c5 ]$ ]0 D1 G3 |" h& I6 J% s" n
% 类文件Atom.m
- N' M) @3 U& e& T) c! Bclassdef Atom < handle! b; Y8 M0 U' S7 I+ U9 a
  properties
& O) \% K& X5 N$ k      atomicNumber$ v" U% a: N& h1 G: n, u
      fullName
6 C' I+ m2 l+ {( g  end0 |4 M$ S3 z- U, W; C; k" c5 f1 M
  methods* P' T, V4 q' i
    function obj = Atom(num,name)0 f" ?) l  e0 Z* @" c$ X
       obj.atomicNumber = num ;6 Y2 S; e7 }& }3 M
       obj.fullName = name
3 U: S6 a- P8 H    end/ M  g9 C5 x$ v! V8 V, F  Z
  end: a1 @: f, K' s7 ]2 ^! M0 I5 g
end  
% G  ~: \  |# C) C\end{Verbatim} 于是:2 j  M( Z! C9 b+ U7 ]5 W
% 键值可以是Atom对象                  
9 z% c+ l' V2 F* Q, ]: f>> ptable('H') = Atom(1,'hydrogen');
7 T& @) {$ I+ x  }2 w  ^0 H>> ptable('H') = Atom(2,'helium');  8 p# @8 d. _, j% h* }9 X  r) T, ]
用来实现快速地检索  `, `1 o4 Y2 p* p
这个问题来自ilovematlab一个网友的提问:( @: k9 S( m. K( b# b6 e- J
大家好,最近遇到一个问题,要构建20000次以上的三元素向量,且保证每次不重复构建,该向量元素值在2000―3000之间不定数,目前采用的方法是建立一历史档案矩阵A,构建一个存储一行,A大小行数持续增加。每次在构建出一新的向量时对该向量与历史档案矩阵每行进行对比,最终确定是否构建过,若没构建过,重新构建。算法显示该检查程序运算时间在执行20000次以上时,会超过200S的运行时间。想力争减小该时间,求方法。 多谢!- ?4 l. z2 B2 Q& {% L8 x
这位网友的问题不在于如何构建这些向量,而是如何保证每次不重复的构建。他一开始想出的解决方法是用矩阵A来存储历史档案,每建立一个新的向量,和该历史档案已有的内容做比较,如果在档案中没有存档,则构建。这样设计的问题是,如他所述,当A中存储的向量变多之后,每次检查该向量是否之前被创建过的时间加长。 这是一个典型的可以用映射表来解决的问题,把线性的复杂度转成常数的复杂度。他可以给每个向量设计一个独立无二的ID,比如直接转数字成id : num2str(vector)/ a! W* E/ v! V
% 构建+ C( b4 X+ `$ j' ]; h& A
mydictionary = containers.Map
/ r* V8 p* ]- V" L$ C" |3 ]" \" [; E8 }0 @: @1 Z& h& U
% 插入数据
1 D5 S9 U- |( u  G- mid = num2str(vector) ; % 比如vector = [1 2 3];
. T' i% G: M7 O  K0 G2 Q6 c: smydictionary('some_id') = vector ;  
* e& m9 T) \# C& Q( t: @; m, F之后的检索也是通过vector得到独一无二的ID,然后通过isKey来判断该键值是否已经被加入到MAP中去了。" [  w7 ^: ]2 r
some_otherID = some_other_vector ;  v' r2 ]3 z, R$ W: P# U
% 验证是否已经构建给过  O9 J& w6 n# M5 n+ ]
if mydictinary.isKey('some_otherID')
' E+ e% z' k2 k% C  @' N1 d+ z/ j      % 做要做的工作
8 [5 r3 k8 I" G  X! R6 Q; L5 Iend  
  • 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 08:21 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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