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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x

6 f0 b4 @! d7 b目录:
+ [' Q# {: w% K: z
% H' d2 t2 z% Ncontainers.Map简介
  Q2 t+ D- v& A, h3 ~数组,元胞和结构体的局限性- \5 W6 {8 t/ l/ E2 R+ p/ q
什么是containers.Map
1 y9 k5 I  I. g. q. Z, I: \* wcontainers.Map的属性和成员方法
2 ^/ B* w& r3 E% d9 ?9 Dcontainers.Map的特点
$ h' R& w% B2 ?containers.Map可以不断地扩张不需预分配) a5 A0 Q- l1 G  O0 U9 O# j
containers.Map可以作为参数在函数内部直接修改
' M. A) P, k3 v: Acontainers.Map可以增强程序的可读性
8 \  T" k8 o. f' N- X" Y: i! ycontainers.Map提供对数据的快速查找
! _' L* C, G: D$ N. O- `containers.Map的使用实例$ b3 M5 e& O, _+ ~% U. M4 `
用来盛放元素周期表
% ~) P- q+ ^2 R用来实现快速地检索3 N/ p7 l/ T5 Y; C) y7 [( }1 r
MATLAB常用基本数据类型有:整型,浮点型,字符型,函数句柄,元胞数组和结构体数组。除了这些基本数据类型,MATLAB还有很多其它的数据类型不为人熟悉,这些数据类型在编程中也非常有用。MATLAB高级数据类型系列旨在向大家介绍它们:比如 containers.Map,tables,enumeration和time series等等,它们为什么有用,用来解决什么问题,并且怎样在科学工程计算使用。本篇首先介绍 containers.Map 数据类型。; k4 @5 d, y; y1 m
containers.Map简介2 I5 e/ E# S' y( A
MATLAB中最具代表性的高级数据类型是containers.Map,我们可以把它叫做映射表。它和函数映射有些类似,比如函数映射的定义是:1 C* L0 W2 K" Q/ C  O2 l# Y
F(x) = Y
* U, H) G2 b5 j5 _. y+ Q& i针对每一个X,都有一个唯一的Y与之对应,反之不一定。如图Fig.1所示。和函数映射相似,映射表用来形容键(Key)和键值(Key Value)之间的一一对应关系。每个Key都是独一无二的,且只能对一个Key Value。如图Fig.2所示。" ^" U2 D, u8 d! j& y: h
Fig.1 函数映射关系
" Z3 D9 t% t( Z- {, R+ f8 XFig.2 containers.Map类的映射示意图
! B1 t" x5 y. T8 Y数组,元胞和结构体的局限性
& w, u" |1 \' J" G& @: [! g, q7 R/ `开始我们先介绍一下数组,元胞数组和结构体的局限性,为什么有的时候这些基本的数据类型无法满足程序的要求,换句话说,我们为什么需要 containers.Map 数据结构。假设要用MATLAB来记录电话号码簿中数据,比如表Table.1所示:
# q3 F3 ?! R# s4 k. f5 iTable.1 电话号码簿5 I. J/ K8 O5 B
+ d: V; z4 A+ J3 |
姓名        电话号码" _' V' Q0 X. J5 w8 t( ~! H. g
Abby        50864700011 M" J! {2 z! {3 ?0 V8 V: t
Bob        5086470002
) m: y9 n7 `4 x1 z! qCharlie        5086470003 先讨论数组,因为电话号码簿中既含有数字,又含有字符串,而数组中只能存放Double类型的数据,所以没有办法用数组直接记录电话号码薄中的内容。 再试试元胞数组,我们在第1行预先声明一个 3 X 3 的元胞数组,然后在2-4行按顺序填放号码簿的内容。$ G3 o# O* B/ q( i! l3 x
% 元胞数组初始化# Q+ H! `" q0 q( h% }
addressBook    = cell(3,1);    % 预分配大小是MATLAB编程的好习惯
# ]5 p  m7 x. |0 Z; z9 J) laddressBook{1} = {'Abby',     '5086470001'};- Y" q3 A9 x6 P- Z% |
addressBook{2} = {'Bob' ,     '5086470002'};# X, o" t' u9 l) N8 ?) Q, D: Q% ?
addressBook{3} = {'Charlie',  '5086470003'};      % @6 `$ Z. N8 G' \; J4 t; ~' W. B" I
需要的时候,可以通过for循环访问其中的内容,比如:7 ^! s, G( r7 P/ E7 [
for iter = 1:length(addressBook)         % 元胞数组的遍历9 ]; a& W; J; w; e4 q2 q* b! n
   addressBook{iter}               % 通过Index访问元胞中的内容  L' q* t9 C; K
end& R: H+ F. l2 H: S& ^; \: w$ M- l$ s
但是按照顺序遍历电话号码簿没有什么实际用处,号码簿的主要功能应该是提供查找的功能才是。比如要想查询Charlie的电话号码,我们希望程序最好可以写成如下这样:
$ m/ C& W4 @( B. n6 p9 ZCharlieNum = addressBook{'Charlie'}     % 提示:这是错误的写法& Y+ S0 g4 w/ V% V1 u4 d
或者
2 l  R( q$ |& l, I( GCharlieNum = addressBook.Charlie        % 提示:这是错误的写法  k+ q9 N0 |7 g. i/ R
但是元胞数组的值只能通过Index去访问内容,不支持如上的访问方式。所以为了找到Charlie的电话号码,程序不得不遍历元胞中的所有内容,取出每一个元胞元素的第一列的字符串做比较,如果名字等于Charlie,则输出电话号码:
* T$ C0 y- O2 ?9 D' x/ [% 使用for循环查找1 i( w. i/ l# R8 {0 w) F
for iter = 1:length(addressBook)
8 D2 g. V/ N& o6 u* n   if strcmp(addressBook{iter}{1},'Charlie'). d# q, U" [7 Q: s  Y$ x1 b2 n& K9 V
        addressBook{iter}{2}              % 如找到则输出电话号码
( T9 Y! d9 `- w7 q1 _5 m7 g4 u      break;
4 K2 }# V, q4 Y; y* B4 _   end. ]6 G& C* r7 |. ^1 ]+ |: L% p
end
# B1 F2 I3 A5 w" a; U1 j当然还有其他的方式来盛放电话号码簿,比如把电话和名字分别存到到两个元胞数组中去
* [7 g1 ?7 W5 H8 I, H+ ^" Enames   = {'Abby','Bob','Charlie'};                 % 用元胞放号码  N1 Q. M6 K/ A5 d4 |! c
numbers = {'5086470001','5086470002','5086470001'}; % 用元胞放名字
8 U& i7 f! H* Y; \ind = find(strcmp(names,'Charlie'));  % strcmp接受向量的输入 返回Logical数组 # ^# o* k# s8 ~
                                      % find紧接着找出逻辑数组中非零元素的Index6 u0 S$ }! J+ L/ R& J6 U" F" Z/ _
numbers{ind}  
" ^* O2 `( Y, S, R8 F1 w其中第3行strcmp接受元胞作为输入,在其中寻找Charlie,find函数将返回Charlie所在的位置,这样的方式比使用for循环要快,但无一例外的是,两种方法都要从头开始遍历一个数组,终究来说是慢的。 除查找性能慢外,使用元胞盛放电话号码簿类型的数据还有其它缺点:
: }, i: o: `  f无法方便的验证重复数据。电话号码簿要求每一个人的名字都是独一无二的,所以在数据录入的时候要防止姓名的重复,但是我们没有其它办法知道某名字是否已经被使用过了,除非在每次输入的时候都对整个元胞里的内容做遍历比较。5 e# X+ i+ `8 s! l0 `7 b" e
无法方便地添加内容。如果电话号码簿中的记录需要不断地增长,但是我们没有办法在一开始就估计出其大概的数量,于是无法有效的预先分配内存,所以添加数据时,一旦超过预先分配的量,MATLAB就要重新分配内存了。
" g1 N7 y6 R5 V) K无法方便地删除内容。如果我们要从元胞中去掉某一记录,可以找到该记录,并把该位置的元胞内容置空,但这并不会自动减小元胞数组的长度,如果 这样的删减操作多了,元胞中会留下很多没有利用的空余位置。
- @. I+ O$ ?# K不方便作为函数的参数,具体原因见struct的局限性.0 ?1 E' i8 [4 `. H. M
最后我们再尝试一下用结构体盛放电话号码簿数据类型,struct的赋值很简单,比如可以直接赋值:4 l& `9 w; D' I4 Q0 ~6 f
% 赋值方法1
' S/ c/ ~$ n! Q4 }  c, m" paddressBook.Abby   = '5086470001';, Y2 Q# @8 G7 `' K8 j+ f9 ^: B
addressBook.Bob  = '5086470002';! [4 N0 {( ?$ d; |8 Z
addressBook.Charlie  = '5086470003';0 O7 M1 X8 o' W
或者:
: }& o  d3 L8 p: I: D% T: d1 H1 i: y% 赋值方法2
- y/ j+ ]' {2 r2 y* j6 k8 U' uaddressBook = struct('Abby','5086470001','Bob','5086470002','Charlie','5086470003')' X3 ~. K% n5 ]8 z+ R
方法1和方法2是等价的。 struct数据类型的查找很方便,比如要查询Charlie的电话号码,直接访问struct中的同名的field即可以了。1 p7 F3 ~+ b* _3 U* q& m
num = addressBook.Charlie  4 L2 Z' y" s( o1 o& j" d8 N$ Y
如果要查询的人名是一个变量, 我们可以使用getfield函数:7 P) h: l2 U- g. h1 B
num = getfield(addressBook,name)  % 其中name是变量   
/ x2 g+ d" G) K6 tstruct盛放电话号码簿类型的数据,查询起来确实比元胞进步了不少,但还是有些不方便的地方。 因为struct的field的名字要求必须是以字母开头,这是一个很大的局限,并不是所有的类似电话号码簿的结构都可以用人名做索引,比如账户号码簿,股票代码等等,他们通常是用数字开头的,比如图Table.2中的数据:; d; x: }% }7 \" F' C1 Y% i* R; \
Table.2 深证股票代码- _1 p4 r% K" x- T" }& u

7 {5 j4 d" f0 u& V股票代码        股票名称
8 e8 }, [  a; V000001        深发展
8 r" W1 Q8 R, f000002        万科
  |. t2 W& l( d6 S( T000004        ST国农 如果我们要求通过股票的代码来查找股票的具体信息,struct无法简单的直接做到。 使用struct的另一个不方便之处在于,当把struct当做函数参数,并且在函数内部需要对该struct做一定的修改时,为了要把修改后的结果返回,我们需要对原来的struct做完全的拷贝,显然如果struct中的数据很多的话,这样做是低效的。比如下面的函数中,addressBook被当做函数的参数传入,在函数中添加了一个新的field, 为了返回更新后的结构,函数内部必须重新构造一个新的struct,也就是s返回给该函数的调用者。: A7 E5 x+ k% ^# W8 y
% 把struct当做函数的参数    " F+ `, [% b8 z- S; G- d1 \: j
function s = modifystruct(s)
0 {1 b* l; U) z  V    s.Dave =  '5086470004';0 E5 a. f; I- T# ]) V- a' o+ _9 K
end& M$ m7 E- p. I% _+ i& c; G  T
用containers.Map来记录电话号码簿' l0 A0 ~) V* t6 I0 X, C
上面一节我们介绍了数组,元胞数组和结构体在模拟电话号码簿这种数据结构时的局限性,这节我们来看怎么用 containers.Map 来盛放电话号码簿中的内容:
+ _2 [/ B5 Y' M9 U0 |- G: _6 JaddressMap = containers.Map;             % 首先声明一个映射表对象变量
3 T. X) `# L! k. zaddressMap('Abby')    = '5086470001';+ C1 J2 L5 V- |
addressMap('Bob')     = '5086470002';+ `: Z$ x% c& `2 Q" A7 y% t: ^
addressMap('Charlie') = '5086470003';  4 |7 w9 H! F) Q0 b2 c
第一行我们声明一个containers.Map的变量(其实是containers.Map类的对象)叫做addressMap,2,3,4行通过提供Key Key Value的方式来给对象赋值,其中单引号内部的值叫做键(Key),等式右边的叫做键值(Key Value)。通过这个结构,我们在MATLAB内存中,建立起了如图Fig.3的映射关系数据结构。
! o( _9 ~# C+ b2 n2 |Fig.3 电话号码簿映射表7 K$ p7 m6 }6 u+ O
Fig.4 Map类的UML 查找addressMap对象中的内容极其方便,比如查找我们想知道Charlie的电话号码,只需:
" _* G" C7 D8 ~( s0 P2 u" U% 查找  
0 p1 Y9 k- f2 A& u$ U$ Knum  = addressMap('Charlie')    B) O4 t8 z" B; {
如果我们要修改Charlie的电话号码,只需 :6 n5 _+ ]1 t; {' n7 c; t: h9 h. P
% 赋值# O$ D7 J3 z: ^1 \, W) X
addressMap('Charlie') = newNum;  
" j9 \. m7 S9 V& b* u: L+ D什么是containers.Map5 V3 z& M! f( p" F# V, z+ ]/ d+ F, W
containers.Map是一个MATLAB的内置类(类是面向对象编程中的一个基本概念,在这里可以把类暂时理解成一种数据类型),所谓内置,就是MATLAB内部实现的,通常这种类的性能更加的优秀。containers.Map其中containers是Package的名称,Map是该Package中的一个类,Map是它的类名。用UML(Unified Modeling Language)的语法把该类表示出来,它的属性包括Count, KeyType,ValueType。它的常用方法包括keys,values,isKey,remove。如图Fig.4所示。$ O/ x# o* t* U/ U0 g: Z
containers.Map的属性和成员方法
3 P" L2 B6 M0 v4 E/ A; O/ M& S- @这节我们介绍containers.Map的属性和成员方法,假设我们这样初始化一个containers.Map对象:4 s/ D0 V8 m0 d* C8 E
% 初始化一个Map对象
6 G# l% y6 ]3 t. P( LaddressMap = containers.Map;! R8 G8 e' A, ]8 z
addressMap('Abby')    = '5086470001';% x1 x! q$ C2 _8 t
addressMap('Bob')     = '5086470002';
8 P4 k+ c' G/ h" [( I" CaddressMap('Charlie') = '5086470003';
0 y( H# b0 c/ t$ N; H, d在命令行中键入该对象的名称,MATLAB会显示该对象属性的基本信息:
9 g! l; g7 ]! C- v>> addressMap
2 P, U- x( V5 s  ~' O. I: X' UaddressMap =
! y. {, e% T, u0 M( b* s7 V4 C  Map with properties:
, |) w; T# C& J- K# a$ f        Count: 3          % MAP 中映射对的数目
) f8 f. O4 v3 n      KeyType: char       % MAP 中键的类型
7 J) T- R/ P2 S6 g& L    ValueType: any        % MAP 中键值的类型   / w3 L. q$ G* Z1 m+ S
其中Count 表示Map对象中映射对的数目。按照规定,键的类型一定要是字符类型,不能是其它数据类型,而键值的类型可以是MATLAB中的任意类型:数组,元胞,结构体,MATLAB对象,甚至JAVA对象等等。因为键值的类型可以是任何MATLAB类型,所以 containers.Map 是MATLAB中极其灵活的数据类型。 成员方法keys 用来返回对象中所有的键:
/ |* D( h8 Z5 k5 w>> addressMap.keys
6 _5 a$ |, R2 J' @, f. y' Pans =
1 E& U/ z0 t& f5 K; d4 ~+ q    'Charlie'    'Abby'    'Bob'  4 G* q' D+ b0 H" k, y
成员方法values用来返回对象中的所有键值9 h6 \0 \9 n& Y
>> addressMap.values4 _- \2 ?. d! O4 y2 t8 ~
ans = 3 W6 Z3 C, p2 @6 Q7 l7 G3 Y
    '5086470003'    '5086470001'    '5086470002'  
3 A7 ^6 q* o) w4 U6 @* q& U5 B- b: p# Nremove用来移除对象中的一个键-键值对,经过下面的操作之后,该对象中的Count的值变成了2。
9 [; _8 n3 v3 c2 \: [>> addressMap.remove('Charlie')) A9 h5 [- M( r+ q  `( D* P3 D7 |
ans =
9 _) j  `  y  u4 Z$ k& f0 |  Map with properties:9 x! A$ S& }2 S1 [" k1 K; g; K9 n
        Count: 2          % 映射对数目减少
& Y: w' Q& ]3 [8 I3 |% ]( |' Z* o      KeyType: char
7 }" C5 \6 y1 B% r0 X  A    ValueType: any
- y0 g: H  L; Y  6 W  A. v  M. E
isKey成员方法用来判断一个map对象中是否已经含有一个键值,比如:4 B" p5 n: c$ D
>> addressMap.isKey('Abby')
+ D9 ]" O! M9 M$ A1 @7 P, Q5 Mans =
$ p5 ~* k* L. E- u) P' r     1
8 ~( M1 n1 R* ?3 E>> addressMap.isKey('abby')    % 键是大小写敏感的
, p% M- l# j- Qans =
8 f6 M- K) P( t7 k9 D     0  : h- {5 q+ t$ s/ R" C2 F; U
isKey的功能是查询,类似之前提到的find和strcmp函数合起来使用的例子。
- \9 I* T. L5 X3 G7 }containers.Map的特点8 x$ W+ f  K" F
containers.Map可以不断地扩张不需预分配# d. j( D* S6 m0 n
使用数组和元胞数组作为数据容器,通常要预先分配容器的大小。否则每次扩充容器,MATLAB都会重新分配内存,并且拷贝原来数组中的数据到新分配的内存中去。映射表是一个可以灵活扩充的容器,并且不需要预分配,每次往里面添加内容不会引起MATLAB重新分配内存。 我们可以通过提供两个元胞来构造映射表对象,比如下面我们构造一个机票簿数据结构:
1 d! L! \$ X1 r3 r3 s: h% 映射表的初始化方法1, ^! v8 U& u* P! N* @" {
ticketMap = containers.Map( {'2R175', 'B7398', 'A479GY'}, ...0 M4 G$ ?. T' H7 w7 v8 J$ D
                            {'Abby', 'Bob, 'Charlie'});
+ H  G" k$ A! c! f; A  l4 F: ?  
3 D( y/ n, I( B7 |7 d$ a3 E* n( U也可以在程序的一开始,先声明一个对象:9 ~. C; e; W+ y" [: e: v
% 映射表的初始化方法2  
2 T0 r1 o( u! U5 L$ M>> ticketMap = containers.Map
) M3 [& `; V0 g+ A/ Z  e# K8 v然后在计算的过程中慢慢的向表中插入数据:
! {2 Y" d) w8 g* r1 |# P% 映射表的初始化方法2  
2 O+ M: X# k6 j2 y* t' [7 [$ ~" X>> ticketMap['2R175'] = 'Abby';
  c; U: Y. ~3 f  t...
+ k- q& L/ z* D- q" O. ]>> ticketMap['A479GY'] = 'Charlie;  : B# o0 a3 H" g$ O- Y
containers.Map可以作为参数在函数内部直接修改
7 J4 v% U" c, w( k7 y因为containers.Map是handle类(handle类的介绍参见《MATLAB面向对象编程-从入门到设计模式》第三章:MATLAB的句柄类和实值类),我们还可以方便的将这个对象传给任何MATLAB的函数,并且在函数内部直接修改对象内部的数据,并且不用把它当做返回值输出,比如:
# [! k0 q# K9 w4 S4 f/ y>> modifyMap(ticketMap);  
+ A  ~. [  {' dmodifyMap函数可以写成:1 e" q; w6 g# r( X( x2 w
function modifyMap(ticketMap)   % 该函数没有返回值; d+ N8 L* g# P0 Q7 M+ _
   .....
- m9 z. e8 ]$ p0 ~1 a   ticketMap(NewKey) = newID 7 o: k+ q1 l+ b1 @* Q
end  
0 ~; o0 h- r4 h8 t  p' ?. A7 J注意这里没有把修改的ticketMap当做返回值,在函数中我们直接修改了Map中的数据,这是MATLAB面向对象语言中handle类的特性。
& u, w' Q$ X5 s& B! T1 lcontainers.Map可以增强程序的可读性
5 {0 }4 i3 l" c2 u1 _- h映射表内部可以存储各种类型的数据,并且给它们起一些有意义的名字。具体的例子见元素周期表的例子。 访问和修改这些数据的时候,可以直接使用这些有意义的名字,从而增加程序的可读性。而如果用元胞数组存储这些数据,在访问和修改这些数据的时候, 需要使用整数的Index,程序可读性不够好。8 n& ], I0 j: o( e. a. a. x& J3 ]. C
containers.Map提供对数据的快速查找& x0 C' H  i8 k$ S% d; B  ^
映射表查找的复杂度是常数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都相同。) M7 u: \- ^/ p! i0 [' \2 K7 a
a = 1:1000000;' g' r" k6 P9 Z# Y* M
m = containers.Map(1:1000000,ones(1,1000000));  
  _* T9 D. z- n& ?, \2 f( |" y- ]数组数据结构,使用find函数依次查找从1到(10^7)的整数,在笔者的机器上,耗时28.331901秒) k6 V' Y* p' R8 s: x
% array的查找/ O" z+ F& N$ ^: Q: U& ?
tic3 }1 T  S+ ~  F. k$ E: z  s
for i=1:100000, ; N* ^3 d0 ]$ v  Y
    find(b==i);
; v4 W+ z8 V% T6 mend; . W+ l: }; c4 y# @
toc  + F; Q% R7 `! `  e
% command line结果2 }1 c9 `7 ?, t8 I6 C9 w0 \

2 e' i. t* [+ t. H9 p
9 ?+ p3 e; N1 S, I' ^& Q2 I+ x" R# ?" ^$ t. K
: [& j; ~0 s) Z& F; I0 j; F
Elapsed time is 28.331901 seconds.  
" [9 ?4 N; b5 Fcontainers.Map数据结构,使用键值1到(10^7)进行访问, 在笔者的机器上,耗时只需1.323007秒, 结论是:如果有大量的数据,并且要频繁的进行查找操作,可以考虑使用containers.Map数据结构。(读者可以试试减少容器中元素的个数,观察一下什么情况下,数组的查找会更快一些。)& p2 q4 x1 z+ E7 I# v8 U( u
% 映射表的查找
  R% r0 r% W" T, G6 I1 xtic
; N4 }5 M( W, w: |+ ?0 T( tfor i=1:100000,
" V& E/ X3 Q' a" E. Q    m(i);
& W& S3 b, g6 T# F! `# {end; 5 w! g5 x7 o/ D0 ]' [6 u
toc
" q; K& i9 d- c: P% command line结果      
; o! |1 _% a5 \8 `- B* t3 `2 x- ^/ G9 s

8 E- [. }6 H% A/ |0 S
. U! Q) M, ^' c; y$ C: c
( n) D' e: ]' K$ z$ dElapsed time is 1.323007 seconds.& [4 N" s/ z$ K  X! ~6 q; O, _
谈到在MATLAB中使用高级数据结构,另一种常见的做法是直接使用Java和Python对象,这里顺便比较一下在MATLAB中使用Java的哈希表的效率。再笔者的机器上,耗时3.072889秒.2 }3 g: Q! H! n, ]" A$ z6 E
% JAVA哈希表的查找
' x3 O0 e' s7 N3 l# W% z! _s = java.util.HashSet();" L* s0 g7 e' g1 T% p: d' W$ H; z4 r* {
for i=1:100000, s.add(i); end   5 F( H1 e0 Q6 t! s4 `6 L
tic; ; ]8 U2 [6 L! g: \, T7 u6 M( C3 g4 ^
for i=1:100000,
1 G6 I2 Y' @: @% c8 b$ |4 q    s.contains(i); " b2 E: y/ S  I2 F7 B8 O5 K; i+ P
end; ' p4 @6 y" q' B& p! \
toc
7 P: k" _6 ~# ^. T8 Z% command line结果      
! t& Z* l. K& v7 {, P- P) Q* B7 K, V1 G& F; [, j3 M
/ \0 {: d4 F- C( K9 D7 Z) u* Q, O) V

3 V% B: p/ V4 ~9 n) ?
, E! B/ S5 K$ P: ^
2 p! N1 \& |$ k* D5 J3 p2 f2 ^! f! s5 Q9 i
Elapsed time is 3.072889 seconds.0 f# {/ f: {* h$ ?, m6 d: A
containers.Map的使用实例, r" v' X9 q0 n0 ~8 I6 q
用来盛放元素周期表0 C$ ]% b/ W5 D' g
工程计算中可以使用这种键-键值的例子有很多。比如物理化学计算中可以用映射表来盛放元素周期表中的内容:0 W( A/ N9 j) {
>> ptable = containers.Map;6 H* H3 E# X, z6 d# v0 P3 `
>> ptable('H')  = 1 ;; g: m7 I6 ]7 ~; E; X
>> ptable('He') = 2 ;  ! k5 x) Y. K& L8 _7 }
其中键是原子符号,而键值是原子量。因为键值的类型不限,所以键值本身还可以是对象,比如Atom类对象,其中包涵更多有用的内容:
) c+ @$ `! W/ |" D0 G! Z% }0 z% 类文件Atom.m& Q& U  i) L/ b4 Y, F" X% s
classdef Atom < handle0 ]  v7 W  B" {
  properties& F2 x, F# e) S, M9 k- w
      atomicNumber
7 T: |/ p( E8 U5 y' q      fullName2 d- t+ J! F' _1 a
  end
( A: K1 `( G* ^+ w" T) s  u  methods8 p9 Z3 K- y' ]* x) v( M4 W
    function obj = Atom(num,name)& j& P1 _3 t/ e
       obj.atomicNumber = num ;
, m) y7 K9 d  a3 s+ W0 z& C& \& L       obj.fullName = name2 b, T* V, i/ U" y% H0 B
    end
) u7 e: z7 O, k2 l4 H  end
$ h4 i2 x) q2 N+ G9 Mend  " w7 Z) h( [) S0 ^. Z) n
\end{Verbatim} 于是:
/ F8 F$ h0 v- C1 T' Y; }9 }% 键值可以是Atom对象                   # _* i( J# i: _  F* Z/ Z
>> ptable('H') = Atom(1,'hydrogen');5 g* D9 K0 s+ L- a# O
>> ptable('H') = Atom(2,'helium');  
' i1 \) d4 h/ }7 Y- c  h* H用来实现快速地检索
' ^  P. B: o9 b" D这个问题来自ilovematlab一个网友的提问:
3 _1 l4 R) u4 s( w& `" R大家好,最近遇到一个问题,要构建20000次以上的三元素向量,且保证每次不重复构建,该向量元素值在2000―3000之间不定数,目前采用的方法是建立一历史档案矩阵A,构建一个存储一行,A大小行数持续增加。每次在构建出一新的向量时对该向量与历史档案矩阵每行进行对比,最终确定是否构建过,若没构建过,重新构建。算法显示该检查程序运算时间在执行20000次以上时,会超过200S的运行时间。想力争减小该时间,求方法。 多谢!8 X9 r- ]" t' r) |: P+ N! w" x
这位网友的问题不在于如何构建这些向量,而是如何保证每次不重复的构建。他一开始想出的解决方法是用矩阵A来存储历史档案,每建立一个新的向量,和该历史档案已有的内容做比较,如果在档案中没有存档,则构建。这样设计的问题是,如他所述,当A中存储的向量变多之后,每次检查该向量是否之前被创建过的时间加长。 这是一个典型的可以用映射表来解决的问题,把线性的复杂度转成常数的复杂度。他可以给每个向量设计一个独立无二的ID,比如直接转数字成id : num2str(vector)
* R) z7 f8 j: d. E: l4 X' ^0 U% 构建6 k2 C. y% b6 D5 V( J- v: }, x
mydictionary = containers.Map3 j( r9 V9 U1 i+ J' Z8 u4 z- F$ |

- Y# U1 H3 e: ^1 I+ l) _) p9 g/ Y0 t% 插入数据
. Y; ]- b% r8 o: H$ l0 e: Oid = num2str(vector) ; % 比如vector = [1 2 3];' G4 T* v* w) g: q
mydictionary('some_id') = vector ;  + c+ _* ?' j3 c
之后的检索也是通过vector得到独一无二的ID,然后通过isKey来判断该键值是否已经被加入到MAP中去了。* N( e5 d6 M8 u5 Q, i2 _
some_otherID = some_other_vector ;$ ^, P3 S' A) N- w( r
% 验证是否已经构建给过4 O8 \0 ]. r1 s+ q
if mydictinary.isKey('some_otherID')& b1 A- O% M) B9 t6 N  U% N
      % 做要做的工作5 a+ [0 y( r5 {6 `
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 16:13 , Processed in 0.203125 second(s), 23 queries , Gzip On.

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

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

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