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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x

* S! Y7 e; ]% b3 I目录:1 m' J+ \- V. W- l3 P

- s' B9 \1 w2 v3 O, Ncontainers.Map简介
, r/ x3 z8 D3 Q7 L数组,元胞和结构体的局限性$ V5 N' z+ ?% {+ H! x
什么是containers.Map
3 O$ X+ }' H' ]2 n2 l( `containers.Map的属性和成员方法
' @' c& s3 t% u- V0 [containers.Map的特点
& B& M+ ~* O! j! o; A; [' q  wcontainers.Map可以不断地扩张不需预分配
4 A6 Z. s) i8 T. R; R; `1 Rcontainers.Map可以作为参数在函数内部直接修改
/ _* S! G% \9 U$ X7 O" h8 ~containers.Map可以增强程序的可读性
: }' m  ?+ e/ x' F2 o  icontainers.Map提供对数据的快速查找
1 N) b; `& n# Ucontainers.Map的使用实例" k1 V9 T' e, D# c* w2 W
用来盛放元素周期表
- s- ^/ ]$ F* f9 g. D用来实现快速地检索
. h$ F; C" z( n3 Y5 i' lMATLAB常用基本数据类型有:整型,浮点型,字符型,函数句柄,元胞数组和结构体数组。除了这些基本数据类型,MATLAB还有很多其它的数据类型不为人熟悉,这些数据类型在编程中也非常有用。MATLAB高级数据类型系列旨在向大家介绍它们:比如 containers.Map,tables,enumeration和time series等等,它们为什么有用,用来解决什么问题,并且怎样在科学工程计算使用。本篇首先介绍 containers.Map 数据类型。+ i2 E8 x4 P2 k& m; F' L0 v
containers.Map简介
* U7 X5 X4 ]$ u/ t4 m" N# yMATLAB中最具代表性的高级数据类型是containers.Map,我们可以把它叫做映射表。它和函数映射有些类似,比如函数映射的定义是:! a9 ^/ M- Y: Z7 k/ c0 R
F(x) = Y( D# B5 m3 `$ B# R' q
针对每一个X,都有一个唯一的Y与之对应,反之不一定。如图Fig.1所示。和函数映射相似,映射表用来形容键(Key)和键值(Key Value)之间的一一对应关系。每个Key都是独一无二的,且只能对一个Key Value。如图Fig.2所示。; O! `( A0 a. I  j& o# {; V
Fig.1 函数映射关系
* U' z( q- t0 |8 l" ]Fig.2 containers.Map类的映射示意图1 W8 s' R; F( C
数组,元胞和结构体的局限性$ e6 w& s$ Q) g( Z0 w! G
开始我们先介绍一下数组,元胞数组和结构体的局限性,为什么有的时候这些基本的数据类型无法满足程序的要求,换句话说,我们为什么需要 containers.Map 数据结构。假设要用MATLAB来记录电话号码簿中数据,比如表Table.1所示:
. R1 k# ?" S, d" i( B; ^0 QTable.1 电话号码簿4 r5 q; j) B& n

5 m9 p* x7 @' G3 D姓名        电话号码
2 k1 }$ x$ _0 o# R1 U% Z' r& bAbby        5086470001
8 o! P* M1 J4 J1 |6 hBob        5086470002
1 m$ o# C# x0 {; W8 U2 uCharlie        5086470003 先讨论数组,因为电话号码簿中既含有数字,又含有字符串,而数组中只能存放Double类型的数据,所以没有办法用数组直接记录电话号码薄中的内容。 再试试元胞数组,我们在第1行预先声明一个 3 X 3 的元胞数组,然后在2-4行按顺序填放号码簿的内容。( j. h, Y4 B2 [, `7 d
% 元胞数组初始化; N; x. f* H" U' o  f( y2 W
addressBook    = cell(3,1);    % 预分配大小是MATLAB编程的好习惯
6 y/ B/ I" v( O' [+ FaddressBook{1} = {'Abby',     '5086470001'};2 Q. |% j* S  W7 }6 F7 G
addressBook{2} = {'Bob' ,     '5086470002'};
" E$ j" E3 n4 E9 I2 V, m& eaddressBook{3} = {'Charlie',  '5086470003'};      1 E9 ^, l; d5 B1 w
需要的时候,可以通过for循环访问其中的内容,比如:$ H+ `) y1 p3 d  h
for iter = 1:length(addressBook)         % 元胞数组的遍历  s$ j" [+ f3 H% w" K: M: e8 L
   addressBook{iter}               % 通过Index访问元胞中的内容
( X& I0 ^6 z9 H* s6 ^5 X! S+ R- M# X4 P& Xend
+ d9 z* l* i# S4 F9 u但是按照顺序遍历电话号码簿没有什么实际用处,号码簿的主要功能应该是提供查找的功能才是。比如要想查询Charlie的电话号码,我们希望程序最好可以写成如下这样:0 e! }( T+ b# z, Q) R0 g
CharlieNum = addressBook{'Charlie'}     % 提示:这是错误的写法7 u' }& d# I+ h8 l- }" [
或者
7 B* {7 e, B7 c5 k& f. NCharlieNum = addressBook.Charlie        % 提示:这是错误的写法
8 x+ @& o& w. k3 Q  R( x9 l3 p但是元胞数组的值只能通过Index去访问内容,不支持如上的访问方式。所以为了找到Charlie的电话号码,程序不得不遍历元胞中的所有内容,取出每一个元胞元素的第一列的字符串做比较,如果名字等于Charlie,则输出电话号码:5 }5 {+ `9 j* k3 d# h
% 使用for循环查找3 D7 A! Z7 ^& R& P( P
for iter = 1:length(addressBook)1 t. G% x0 o) L0 [' ~) T- V4 m, L
   if strcmp(addressBook{iter}{1},'Charlie')( k; T) V- u2 y( Z
        addressBook{iter}{2}              % 如找到则输出电话号码
+ @, n) |! R# a% B% ?4 q4 l      break;
& G$ n  z+ I  O: j   end5 J  p) S0 D. h5 W
end) Q3 Q  T$ _" I5 B' E# P* v4 C1 e
当然还有其他的方式来盛放电话号码簿,比如把电话和名字分别存到到两个元胞数组中去4 K+ a2 J- A4 Y# I6 \
names   = {'Abby','Bob','Charlie'};                 % 用元胞放号码
) D: R- I  h, L) \) pnumbers = {'5086470001','5086470002','5086470001'}; % 用元胞放名字
( l1 g" Z$ r- h% U% C; `6 Cind = find(strcmp(names,'Charlie'));  % strcmp接受向量的输入 返回Logical数组
; B' H+ w1 e& r2 b5 O' S" M, s                                      % find紧接着找出逻辑数组中非零元素的Index
6 b) U0 g% W* a& Z) bnumbers{ind}  5 w8 R* a7 U- h% W
其中第3行strcmp接受元胞作为输入,在其中寻找Charlie,find函数将返回Charlie所在的位置,这样的方式比使用for循环要快,但无一例外的是,两种方法都要从头开始遍历一个数组,终究来说是慢的。 除查找性能慢外,使用元胞盛放电话号码簿类型的数据还有其它缺点:
# [4 p5 m( y6 R* L) o无法方便的验证重复数据。电话号码簿要求每一个人的名字都是独一无二的,所以在数据录入的时候要防止姓名的重复,但是我们没有其它办法知道某名字是否已经被使用过了,除非在每次输入的时候都对整个元胞里的内容做遍历比较。( P1 f% u: o/ t* B/ `( L6 O; A% V
无法方便地添加内容。如果电话号码簿中的记录需要不断地增长,但是我们没有办法在一开始就估计出其大概的数量,于是无法有效的预先分配内存,所以添加数据时,一旦超过预先分配的量,MATLAB就要重新分配内存了。! D& B) f) ^# M/ [9 x0 m3 Z
无法方便地删除内容。如果我们要从元胞中去掉某一记录,可以找到该记录,并把该位置的元胞内容置空,但这并不会自动减小元胞数组的长度,如果 这样的删减操作多了,元胞中会留下很多没有利用的空余位置。9 N/ Y9 n; p& l' p0 T
不方便作为函数的参数,具体原因见struct的局限性.
0 j/ w0 v" W: U8 A- w最后我们再尝试一下用结构体盛放电话号码簿数据类型,struct的赋值很简单,比如可以直接赋值:0 m, ]8 H3 D7 o. X5 i
% 赋值方法1# `6 }8 F" p1 o& h- }) \
addressBook.Abby   = '5086470001';
$ [7 Z! h, }1 J) l& raddressBook.Bob  = '5086470002';
! S. F5 u9 ~8 d9 v9 z3 w$ d* p8 XaddressBook.Charlie  = '5086470003';
) I2 h( o& F! V) G5 B或者:
& C  ^8 @2 P, g1 N2 a3 D% V% 赋值方法23 D2 T% F7 y7 x# _# V% G8 M
addressBook = struct('Abby','5086470001','Bob','5086470002','Charlie','5086470003')
+ o& Q4 R' I& A+ r2 g  s方法1和方法2是等价的。 struct数据类型的查找很方便,比如要查询Charlie的电话号码,直接访问struct中的同名的field即可以了。' h4 V* C7 I( a9 ]4 `+ O, W
num = addressBook.Charlie  
" E2 b% i% V% c8 q/ x' S. [7 @如果要查询的人名是一个变量, 我们可以使用getfield函数:6 C) A, A& k% ~  M
num = getfield(addressBook,name)  % 其中name是变量   # W1 P/ u/ c+ P+ }( r
struct盛放电话号码簿类型的数据,查询起来确实比元胞进步了不少,但还是有些不方便的地方。 因为struct的field的名字要求必须是以字母开头,这是一个很大的局限,并不是所有的类似电话号码簿的结构都可以用人名做索引,比如账户号码簿,股票代码等等,他们通常是用数字开头的,比如图Table.2中的数据:
. f& h9 R5 K* t& x8 i& G1 hTable.2 深证股票代码
( y* N9 J! [! T& X) I- T- ^3 P1 L
股票代码        股票名称& C6 C0 S3 G' E: z- j! E1 @
000001        深发展
1 W" M2 V. r# k9 f; b6 {  v' `" X000002        万科
; Z% F( ?: w9 L; U0 k3 Z000004        ST国农 如果我们要求通过股票的代码来查找股票的具体信息,struct无法简单的直接做到。 使用struct的另一个不方便之处在于,当把struct当做函数参数,并且在函数内部需要对该struct做一定的修改时,为了要把修改后的结果返回,我们需要对原来的struct做完全的拷贝,显然如果struct中的数据很多的话,这样做是低效的。比如下面的函数中,addressBook被当做函数的参数传入,在函数中添加了一个新的field, 为了返回更新后的结构,函数内部必须重新构造一个新的struct,也就是s返回给该函数的调用者。
* f* n. S) N% Y. a6 ?% 把struct当做函数的参数    4 U( ^6 r% q! W. _  b% ]
function s = modifystruct(s)
3 o/ ~+ J) F/ R+ J8 W    s.Dave =  '5086470004';  _$ w* P' J! F" P; k7 K
end/ l0 i7 q" p) e# n( `2 f
用containers.Map来记录电话号码簿
/ Z5 ]; t2 M9 m5 s+ w* Y) h上面一节我们介绍了数组,元胞数组和结构体在模拟电话号码簿这种数据结构时的局限性,这节我们来看怎么用 containers.Map 来盛放电话号码簿中的内容:/ v; i- O9 m1 m. \4 p1 N0 N
addressMap = containers.Map;             % 首先声明一个映射表对象变量
0 T+ X0 i- D6 }" A9 t, |addressMap('Abby')    = '5086470001';& p% [6 T0 S% H9 g$ h
addressMap('Bob')     = '5086470002';
  w( o* H, ?8 F4 yaddressMap('Charlie') = '5086470003';  
  Y& G4 u+ D( g& Z$ \8 u8 q7 M5 l第一行我们声明一个containers.Map的变量(其实是containers.Map类的对象)叫做addressMap,2,3,4行通过提供Key Key Value的方式来给对象赋值,其中单引号内部的值叫做键(Key),等式右边的叫做键值(Key Value)。通过这个结构,我们在MATLAB内存中,建立起了如图Fig.3的映射关系数据结构。! B. e0 i7 B1 O! Y% I( e: {% {/ }
Fig.3 电话号码簿映射表
- w2 j  l* Z* ], zFig.4 Map类的UML 查找addressMap对象中的内容极其方便,比如查找我们想知道Charlie的电话号码,只需:
( G6 z0 X# m6 T5 M; x% 查找  $ G, }+ A1 i  B" L
num  = addressMap('Charlie')  4 t# v# E+ t# J4 t1 O6 R
如果我们要修改Charlie的电话号码,只需 :  J- B7 s( |4 w# a
% 赋值
, O6 M. j# k" J7 I6 Q# S2 F' ^) B- naddressMap('Charlie') = newNum;  . [0 u! k4 _  S; Q/ R$ G2 V1 R
什么是containers.Map
* U) {2 F8 D) ]3 v8 gcontainers.Map是一个MATLAB的内置类(类是面向对象编程中的一个基本概念,在这里可以把类暂时理解成一种数据类型),所谓内置,就是MATLAB内部实现的,通常这种类的性能更加的优秀。containers.Map其中containers是Package的名称,Map是该Package中的一个类,Map是它的类名。用UML(Unified Modeling Language)的语法把该类表示出来,它的属性包括Count, KeyType,ValueType。它的常用方法包括keys,values,isKey,remove。如图Fig.4所示。6 A/ u% S7 b( Q' Q# }& S
containers.Map的属性和成员方法; P1 \3 E% c* Q; E6 J8 }$ b
这节我们介绍containers.Map的属性和成员方法,假设我们这样初始化一个containers.Map对象:# M' S$ v* c6 v
% 初始化一个Map对象: G5 f9 ^& X9 a/ Z
addressMap = containers.Map;. Q0 w7 l6 t7 p  p; b# g' f
addressMap('Abby')    = '5086470001';
7 \* z5 m/ @5 \# c2 }8 A4 [addressMap('Bob')     = '5086470002';
/ h1 R% }1 `( P+ H4 @* c/ y, BaddressMap('Charlie') = '5086470003';
9 q! S* k- {+ l4 B' F0 r: p( f在命令行中键入该对象的名称,MATLAB会显示该对象属性的基本信息:
. T5 v5 m6 o. I! A* Q>> addressMap
5 i3 t/ C" f  q+ caddressMap =1 ~9 |2 h$ g7 g- n* K8 F1 M9 E
  Map with properties:0 X; t: w9 l! L3 }
        Count: 3          % MAP 中映射对的数目$ g' k7 v8 D+ r. E5 G( ?/ V5 X! G, @
      KeyType: char       % MAP 中键的类型 4 Q, M/ e( n! ?9 ]$ i
    ValueType: any        % MAP 中键值的类型   9 |, u3 W, }. b! O0 k
其中Count 表示Map对象中映射对的数目。按照规定,键的类型一定要是字符类型,不能是其它数据类型,而键值的类型可以是MATLAB中的任意类型:数组,元胞,结构体,MATLAB对象,甚至JAVA对象等等。因为键值的类型可以是任何MATLAB类型,所以 containers.Map 是MATLAB中极其灵活的数据类型。 成员方法keys 用来返回对象中所有的键:
. U- K- i4 e' Q) `3 B6 x>> addressMap.keys! c, I' y4 _$ ^" P. G1 d
ans =
0 H; u: R* [3 v/ x$ [2 P+ J    'Charlie'    'Abby'    'Bob'  
% J9 U* S4 P" P' w成员方法values用来返回对象中的所有键值
3 V& M9 a9 t, Q% q" X( U8 M$ U>> addressMap.values
8 L$ Y2 Z/ a. cans = ( k4 m" D% V" T% n+ {& Y4 l% z. ^
    '5086470003'    '5086470001'    '5086470002'  : i* _% [, R% F7 U
remove用来移除对象中的一个键-键值对,经过下面的操作之后,该对象中的Count的值变成了2。5 b0 C  i# e3 j7 E: F9 O
>> addressMap.remove('Charlie'): N( ]2 y5 u( X( y2 V: o
ans =
# }8 |% J& z7 n2 E& i2 B2 x  Map with properties:
" ]4 z+ i6 V5 l8 [* ^& c        Count: 2          % 映射对数目减少
; \9 o; E/ e5 S/ V# B# e* U      KeyType: char9 u/ U; u( Y3 _) v% W
    ValueType: any
7 x1 B6 q6 {. w9 j; S  6 t7 T" c1 R4 ^
isKey成员方法用来判断一个map对象中是否已经含有一个键值,比如:( V& r# Y& v  z. n  }
>> addressMap.isKey('Abby')
- J3 |& L1 h9 o- V; jans =, S, l' ]4 b3 _6 j) ~1 K
     1, F9 u6 c* z# ]' F. m# m
>> addressMap.isKey('abby')    % 键是大小写敏感的8 s' |* }1 }$ ?2 [" b) W6 }. b1 B
ans =
7 j7 W4 T: k# m& m2 p     0  
9 l) Q& J- {0 B& p( @+ N$ FisKey的功能是查询,类似之前提到的find和strcmp函数合起来使用的例子。  \0 ?  z. E7 ?( H4 ?' b- f
containers.Map的特点
' m) `0 z) Q/ h' N  ^3 I" Qcontainers.Map可以不断地扩张不需预分配) j9 d  Z2 L  Z; k5 y% ^8 p
使用数组和元胞数组作为数据容器,通常要预先分配容器的大小。否则每次扩充容器,MATLAB都会重新分配内存,并且拷贝原来数组中的数据到新分配的内存中去。映射表是一个可以灵活扩充的容器,并且不需要预分配,每次往里面添加内容不会引起MATLAB重新分配内存。 我们可以通过提供两个元胞来构造映射表对象,比如下面我们构造一个机票簿数据结构:
5 U# [2 H; g& c1 c( B: K% 映射表的初始化方法1# _1 N2 O$ P* y
ticketMap = containers.Map( {'2R175', 'B7398', 'A479GY'}, ...# U6 w) E) _- d+ n
                            {'Abby', 'Bob, 'Charlie'});: z; ]3 m& ~5 R2 k, @3 N
  5 L4 t/ O3 |5 l5 m' A
也可以在程序的一开始,先声明一个对象:% t+ D! a( S+ K/ [! K8 l5 i
% 映射表的初始化方法2  
& x- G/ W( T/ o) C1 z>> ticketMap = containers.Map + I+ f# W2 X% t$ ~. a. A+ ?+ q1 @6 l
然后在计算的过程中慢慢的向表中插入数据:
) d' E4 m4 A; h! U$ I5 y- ~% 映射表的初始化方法2  " g+ ]4 t3 l. P1 C0 G
>> ticketMap['2R175'] = 'Abby';
( n0 A1 M8 w; a7 z3 W( i' a6 ?...# ?0 ~/ ?7 T' r, P6 C
>> ticketMap['A479GY'] = 'Charlie;  
5 `1 w1 x6 j+ \! w0 A! Lcontainers.Map可以作为参数在函数内部直接修改, Q" j: [! X' Z4 T3 N# w5 U' j+ A# m
因为containers.Map是handle类(handle类的介绍参见《MATLAB面向对象编程-从入门到设计模式》第三章:MATLAB的句柄类和实值类),我们还可以方便的将这个对象传给任何MATLAB的函数,并且在函数内部直接修改对象内部的数据,并且不用把它当做返回值输出,比如:
; i4 T1 q& }  S3 H- K" f' L. d; G, U>> modifyMap(ticketMap);  
# d1 i4 t0 S) d) l; ~+ [modifyMap函数可以写成:2 A- a! g$ I) w, m
function modifyMap(ticketMap)   % 该函数没有返回值
1 {( N* h7 L) ?9 y" ]   .....$ I( P/ H- C- J' A2 a; ?
   ticketMap(NewKey) = newID
  a& G: k. D- e1 Iend  
9 h1 c5 A: @9 z4 L注意这里没有把修改的ticketMap当做返回值,在函数中我们直接修改了Map中的数据,这是MATLAB面向对象语言中handle类的特性。: h! m% P" z- X
containers.Map可以增强程序的可读性' k% M: G0 D3 d9 B3 {
映射表内部可以存储各种类型的数据,并且给它们起一些有意义的名字。具体的例子见元素周期表的例子。 访问和修改这些数据的时候,可以直接使用这些有意义的名字,从而增加程序的可读性。而如果用元胞数组存储这些数据,在访问和修改这些数据的时候, 需要使用整数的Index,程序可读性不够好。  p% }+ h" y/ C/ F1 k7 h
containers.Map提供对数据的快速查找  N- E6 y. a; X# ~% |
映射表查找的复杂度是常数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都相同。' N" w- O: O! i0 n# p" ]1 H
a = 1:1000000;
4 L3 n0 ?; d( e) C! o, mm = containers.Map(1:1000000,ones(1,1000000));  
" n7 `- |5 ^5 y, Z数组数据结构,使用find函数依次查找从1到(10^7)的整数,在笔者的机器上,耗时28.331901秒
6 }9 j& G' [9 O1 M6 u% array的查找
$ Q7 F# a6 U" Q# B' Ftic, M# T/ ^% Q; u) h' _
for i=1:100000,
9 G  m4 \' S- ^6 c5 K    find(b==i);
& f) o5 m" p1 A8 pend; / j! `; e# p5 x5 {3 C
toc  , X8 @5 r" \+ l' y) q+ s
% command line结果2 e, Q' S" d* R2 w9 t8 i

9 E' ~8 P7 v" W# n6 P* l3 V
9 s+ a# V! _9 e9 R3 b3 l
# m" b* w/ I5 \- j; w: r& J; m; X
" Q7 ^9 ?' ~5 A2 |- I. c: lElapsed time is 28.331901 seconds.  
& Y" {' ]( Q# I$ x; m) v6 @; Lcontainers.Map数据结构,使用键值1到(10^7)进行访问, 在笔者的机器上,耗时只需1.323007秒, 结论是:如果有大量的数据,并且要频繁的进行查找操作,可以考虑使用containers.Map数据结构。(读者可以试试减少容器中元素的个数,观察一下什么情况下,数组的查找会更快一些。)
# R8 G; O" \- P& v4 `$ [7 l  n% 映射表的查找' O) o. O1 ~6 d  X, a* \2 q1 k
tic; U' J+ t% }& i
for i=1:100000,
, F# A6 p. a4 z  s) C1 I8 s    m(i);) @4 {* j" ~3 [9 o) V% T
end; ; m  s* `! F- Z* V$ Q8 W3 C
toc$ G% g! _9 G8 d0 |
% command line结果      / w/ J- |. v9 d. ?8 ~( C+ ]
2 _$ F: m( U, i7 F8 X! ?5 b5 h, k

% r, T/ h# M# m6 f3 c* h: U. H. s  ~7 R2 x' F# {
' |2 K. o. M2 ]% \& a
Elapsed time is 1.323007 seconds.4 r3 ^6 H& y1 ^5 d/ f
谈到在MATLAB中使用高级数据结构,另一种常见的做法是直接使用Java和Python对象,这里顺便比较一下在MATLAB中使用Java的哈希表的效率。再笔者的机器上,耗时3.072889秒.
+ u* f- h  B3 q0 i% JAVA哈希表的查找- T6 ?. x  I% A
s = java.util.HashSet();
- ?  e1 V- A7 Efor i=1:100000, s.add(i); end   5 G8 `, W  l' I
tic; , m, J3 K/ r% L: a8 ^, a8 @
for i=1:100000,
7 |& V6 o( v3 V3 @1 J    s.contains(i);
* l- w) ~4 @  G; f2 {end;   E# j: |% e1 g. j! V
toc
7 r, A, f7 o' i/ O% P- u" C% command line结果      
  G, \: n# k  r4 S  F
2 {& o) l# c& w  i) O' b/ g) R
6 r1 M% f9 V" r: N
% ^- `2 f1 \; n* [# Q
$ O( y7 {; V0 O: {% Z( H$ h( L
' Y( I# h; ]6 r. g7 v
% X( I: x6 E' E! [( b& WElapsed time is 3.072889 seconds.1 v% {2 \+ v' b, |( y; c# r
containers.Map的使用实例
$ X' h$ r5 U' {% O# b9 Z: D用来盛放元素周期表
" i! _, X" {" g. t+ v工程计算中可以使用这种键-键值的例子有很多。比如物理化学计算中可以用映射表来盛放元素周期表中的内容:
" U; f$ w) M* N% f. r! G>> ptable = containers.Map;
3 d5 ~  `& B( Z( j2 Q* O7 C9 ~>> ptable('H')  = 1 ;: Z( x3 R/ r7 _
>> ptable('He') = 2 ;  3 r2 ]5 E* v, x. z: Q! Y
其中键是原子符号,而键值是原子量。因为键值的类型不限,所以键值本身还可以是对象,比如Atom类对象,其中包涵更多有用的内容:* M6 f2 d$ h; c
% 类文件Atom.m
0 i2 w, k; E3 f$ A5 qclassdef Atom < handle
% q, D6 U) M. U8 k+ @- C0 s  properties) X% i( o( F! D2 C( S
      atomicNumber
& y, _2 j' B6 k/ L' s- E' m      fullName* s# r0 |( j; Y7 h
  end, A0 u6 Z& K* t# t# \
  methods- w3 K5 Z4 q7 c& ~/ \/ Y
    function obj = Atom(num,name)
  b1 @1 ~- y' z2 a/ F       obj.atomicNumber = num ;* x% w& i+ Z) w1 F5 O9 U0 M( ?
       obj.fullName = name# b5 G; C) P- [- k$ b- s6 ^. y/ e& F
    end" U$ w' ~0 i8 N
  end' f  p0 S3 F3 D7 [" j( k
end  
$ g3 p& C9 k* J5 R\end{Verbatim} 于是:2 `. C6 E! R7 F6 s% i' R
% 键值可以是Atom对象                  
9 [$ \4 \3 f7 W- l; p% ?2 U$ c% `>> ptable('H') = Atom(1,'hydrogen');8 C3 T( v! o* U6 E- g; D
>> ptable('H') = Atom(2,'helium');  ) V7 F+ ~: ?. H
用来实现快速地检索
+ i; ]; C' j& U7 w, Q7 f. a2 \- y这个问题来自ilovematlab一个网友的提问:) d) W% v, i  h6 v# b
大家好,最近遇到一个问题,要构建20000次以上的三元素向量,且保证每次不重复构建,该向量元素值在2000―3000之间不定数,目前采用的方法是建立一历史档案矩阵A,构建一个存储一行,A大小行数持续增加。每次在构建出一新的向量时对该向量与历史档案矩阵每行进行对比,最终确定是否构建过,若没构建过,重新构建。算法显示该检查程序运算时间在执行20000次以上时,会超过200S的运行时间。想力争减小该时间,求方法。 多谢!
- Z3 R9 Q; |7 g2 f. w这位网友的问题不在于如何构建这些向量,而是如何保证每次不重复的构建。他一开始想出的解决方法是用矩阵A来存储历史档案,每建立一个新的向量,和该历史档案已有的内容做比较,如果在档案中没有存档,则构建。这样设计的问题是,如他所述,当A中存储的向量变多之后,每次检查该向量是否之前被创建过的时间加长。 这是一个典型的可以用映射表来解决的问题,把线性的复杂度转成常数的复杂度。他可以给每个向量设计一个独立无二的ID,比如直接转数字成id : num2str(vector)
$ ~( }- A& z" e* O# E, U% 构建9 a9 N1 E" j7 P! s
mydictionary = containers.Map" z7 D! U* f; c* F, F+ r
8 x: k0 l2 k2 p# F- ?% H9 f0 A
% 插入数据3 u2 U* l+ k# |) m2 \: L
id = num2str(vector) ; % 比如vector = [1 2 3];
9 j; A7 S4 Z5 Q. Amydictionary('some_id') = vector ;  2 U% U: E1 [0 U! Z2 a0 N! L
之后的检索也是通过vector得到独一无二的ID,然后通过isKey来判断该键值是否已经被加入到MAP中去了。
7 t( m8 N7 h, {! E3 Usome_otherID = some_other_vector ;
! G% ^! h# w; `7 C8 M% 验证是否已经构建给过
0 l* p9 N$ |! `( oif mydictinary.isKey('some_otherID')
& C0 G+ t( u9 J# o      % 做要做的工作
' V! I, {9 Q- d# o: Yend  
  • 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-8-12 09:32 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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