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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x

7 B0 i6 l. j. x  Y目录:
' N9 T( c/ |3 J( K8 I* o3 Y/ w' t& U( @. F: a) o# \
containers.Map简介
, W5 ^2 A) Z% K. |* |8 i9 r8 z数组,元胞和结构体的局限性1 I5 y8 v+ ^5 m8 c
什么是containers.Map0 G9 S, C0 Z4 Y9 H% \$ ^
containers.Map的属性和成员方法
$ ^7 o. {& Z1 m! n0 k; s6 H9 vcontainers.Map的特点  `$ [7 o2 Z7 J3 V/ e
containers.Map可以不断地扩张不需预分配
4 H  Y* M# @" i; {" Scontainers.Map可以作为参数在函数内部直接修改4 o6 u3 d6 ^( a+ a6 N
containers.Map可以增强程序的可读性: N8 ]" g* E  r) D) \
containers.Map提供对数据的快速查找
8 p+ k7 F6 r# {% E) \containers.Map的使用实例) }& h6 [% M9 M' W
用来盛放元素周期表
9 V; m  `' [# O: ]6 ?0 D  S6 E" O用来实现快速地检索
9 Z9 V& ~, t8 ]MATLAB常用基本数据类型有:整型,浮点型,字符型,函数句柄,元胞数组和结构体数组。除了这些基本数据类型,MATLAB还有很多其它的数据类型不为人熟悉,这些数据类型在编程中也非常有用。MATLAB高级数据类型系列旨在向大家介绍它们:比如 containers.Map,tables,enumeration和time series等等,它们为什么有用,用来解决什么问题,并且怎样在科学工程计算使用。本篇首先介绍 containers.Map 数据类型。
$ T. w0 q1 ^7 }& {containers.Map简介
( W/ G4 ~2 T7 O2 r" l; B9 HMATLAB中最具代表性的高级数据类型是containers.Map,我们可以把它叫做映射表。它和函数映射有些类似,比如函数映射的定义是:' K- [  _: u8 U8 X; ^
F(x) = Y
# _: y. P- b( {" Y) B7 l9 i) Y5 w针对每一个X,都有一个唯一的Y与之对应,反之不一定。如图Fig.1所示。和函数映射相似,映射表用来形容键(Key)和键值(Key Value)之间的一一对应关系。每个Key都是独一无二的,且只能对一个Key Value。如图Fig.2所示。2 w* U& H$ K7 Z6 j, j0 Q& Y
Fig.1 函数映射关系7 H) w* h$ C) g; W
Fig.2 containers.Map类的映射示意图8 ~) `+ W0 o9 g& P* U
数组,元胞和结构体的局限性
  Z" W$ Z. n/ @7 e; p$ w$ _1 l开始我们先介绍一下数组,元胞数组和结构体的局限性,为什么有的时候这些基本的数据类型无法满足程序的要求,换句话说,我们为什么需要 containers.Map 数据结构。假设要用MATLAB来记录电话号码簿中数据,比如表Table.1所示:: Z' e& f8 f# o
Table.1 电话号码簿# o5 n+ }  b6 v8 g
7 O( `& i, R" U/ ^' D* e
姓名        电话号码) x8 W4 y: d, q  v
Abby        5086470001
6 z0 T& N. D( r. _$ zBob        50864700022 Q7 [" e4 c: @9 E. x' v
Charlie        5086470003 先讨论数组,因为电话号码簿中既含有数字,又含有字符串,而数组中只能存放Double类型的数据,所以没有办法用数组直接记录电话号码薄中的内容。 再试试元胞数组,我们在第1行预先声明一个 3 X 3 的元胞数组,然后在2-4行按顺序填放号码簿的内容。' D0 t  h# A9 Q, \; {) P
% 元胞数组初始化1 X- s/ G; t. E2 @
addressBook    = cell(3,1);    % 预分配大小是MATLAB编程的好习惯
/ k: I, c* w. b/ i  YaddressBook{1} = {'Abby',     '5086470001'};; u- C6 l$ l+ L! z; _' ?$ q
addressBook{2} = {'Bob' ,     '5086470002'};
% P4 Z# C! f9 waddressBook{3} = {'Charlie',  '5086470003'};      
% f* s4 j+ D9 j1 W: v需要的时候,可以通过for循环访问其中的内容,比如:
/ n  G2 O' e* ufor iter = 1:length(addressBook)         % 元胞数组的遍历
4 K( ~( w6 ~: B$ |$ ?' u' f   addressBook{iter}               % 通过Index访问元胞中的内容
' g4 k% [) {- |7 kend
+ B. f2 b& @: S9 I$ M但是按照顺序遍历电话号码簿没有什么实际用处,号码簿的主要功能应该是提供查找的功能才是。比如要想查询Charlie的电话号码,我们希望程序最好可以写成如下这样:. W7 q4 Y( U4 N! L
CharlieNum = addressBook{'Charlie'}     % 提示:这是错误的写法& }) r8 t: c$ U+ P" N( Z% y
或者- b" ~% H" P) q- J$ v3 @
CharlieNum = addressBook.Charlie        % 提示:这是错误的写法3 z" }+ x. T" j
但是元胞数组的值只能通过Index去访问内容,不支持如上的访问方式。所以为了找到Charlie的电话号码,程序不得不遍历元胞中的所有内容,取出每一个元胞元素的第一列的字符串做比较,如果名字等于Charlie,则输出电话号码:$ X( o  l, D3 n$ g- X* r
% 使用for循环查找8 w9 G$ s, y1 N
for iter = 1:length(addressBook)
7 A8 y0 g2 h3 J8 U+ p: V, ]# f& u' h   if strcmp(addressBook{iter}{1},'Charlie')
& }% \2 n) @2 y% s6 @        addressBook{iter}{2}              % 如找到则输出电话号码) o- g* t3 T. x8 `6 ^# Z
      break;$ q- a5 D$ F1 A6 U, Q
   end
" t6 _  _1 {, F8 b! ]+ Dend# @0 O) G; k, m; ^( B. w
当然还有其他的方式来盛放电话号码簿,比如把电话和名字分别存到到两个元胞数组中去
2 I4 R; R- ?2 F; v, Inames   = {'Abby','Bob','Charlie'};                 % 用元胞放号码
, ^" a8 R9 {) W! Snumbers = {'5086470001','5086470002','5086470001'}; % 用元胞放名字
4 d) K: u# h1 L2 X6 n; G( U0 Find = find(strcmp(names,'Charlie'));  % strcmp接受向量的输入 返回Logical数组
8 V" x; `2 w/ G+ k/ z" Z1 D                                      % find紧接着找出逻辑数组中非零元素的Index0 R! J$ X* e6 s! A
numbers{ind}  # ~8 b. l# F7 i6 \+ Z# h0 p
其中第3行strcmp接受元胞作为输入,在其中寻找Charlie,find函数将返回Charlie所在的位置,这样的方式比使用for循环要快,但无一例外的是,两种方法都要从头开始遍历一个数组,终究来说是慢的。 除查找性能慢外,使用元胞盛放电话号码簿类型的数据还有其它缺点:8 w1 Y, M8 o& r; F
无法方便的验证重复数据。电话号码簿要求每一个人的名字都是独一无二的,所以在数据录入的时候要防止姓名的重复,但是我们没有其它办法知道某名字是否已经被使用过了,除非在每次输入的时候都对整个元胞里的内容做遍历比较。7 Z. w, Z( T  o! A" g
无法方便地添加内容。如果电话号码簿中的记录需要不断地增长,但是我们没有办法在一开始就估计出其大概的数量,于是无法有效的预先分配内存,所以添加数据时,一旦超过预先分配的量,MATLAB就要重新分配内存了。3 M8 w3 T3 f0 n! v& Q' q- l
无法方便地删除内容。如果我们要从元胞中去掉某一记录,可以找到该记录,并把该位置的元胞内容置空,但这并不会自动减小元胞数组的长度,如果 这样的删减操作多了,元胞中会留下很多没有利用的空余位置。
) \4 K+ ?6 ]& t& \! ^  Q: i不方便作为函数的参数,具体原因见struct的局限性.
! b  D% C- t8 V3 H最后我们再尝试一下用结构体盛放电话号码簿数据类型,struct的赋值很简单,比如可以直接赋值:( H* P. n/ B4 c2 l4 Y/ l9 l* I
% 赋值方法1
+ r$ k1 n8 @( }! SaddressBook.Abby   = '5086470001';
  U$ `$ Z* j( [addressBook.Bob  = '5086470002';
9 v/ M+ K  R$ z! M; `addressBook.Charlie  = '5086470003';
' X. j9 H- ~8 B; y2 s' V/ s或者:
# ]7 }$ Y0 B4 L% 赋值方法2/ c/ ]% ]4 c" s2 P4 U, z
addressBook = struct('Abby','5086470001','Bob','5086470002','Charlie','5086470003')+ o& e6 }9 A! i) [
方法1和方法2是等价的。 struct数据类型的查找很方便,比如要查询Charlie的电话号码,直接访问struct中的同名的field即可以了。
3 U5 {, ]' r( T$ Wnum = addressBook.Charlie  
- x6 w: e; `1 t/ c: w9 y& H如果要查询的人名是一个变量, 我们可以使用getfield函数:
* r/ q" |! g, ~+ s! C8 Cnum = getfield(addressBook,name)  % 其中name是变量   
2 y2 h5 l) l$ mstruct盛放电话号码簿类型的数据,查询起来确实比元胞进步了不少,但还是有些不方便的地方。 因为struct的field的名字要求必须是以字母开头,这是一个很大的局限,并不是所有的类似电话号码簿的结构都可以用人名做索引,比如账户号码簿,股票代码等等,他们通常是用数字开头的,比如图Table.2中的数据:
3 M) N: I# J+ ?8 W4 K  [1 e" pTable.2 深证股票代码3 d6 }6 b8 `& Q5 p! v% S

: H# W6 C5 w2 l股票代码        股票名称
# X4 F% T% O: D- C# ~' Q. j/ c000001        深发展" H( H% k; B. `2 c
000002        万科1 t( D; j3 Z2 _  S6 ^! D. x
000004        ST国农 如果我们要求通过股票的代码来查找股票的具体信息,struct无法简单的直接做到。 使用struct的另一个不方便之处在于,当把struct当做函数参数,并且在函数内部需要对该struct做一定的修改时,为了要把修改后的结果返回,我们需要对原来的struct做完全的拷贝,显然如果struct中的数据很多的话,这样做是低效的。比如下面的函数中,addressBook被当做函数的参数传入,在函数中添加了一个新的field, 为了返回更新后的结构,函数内部必须重新构造一个新的struct,也就是s返回给该函数的调用者。
2 ^) Q: ~7 s' w& g5 h* O9 `% I! F% 把struct当做函数的参数    " @0 m& J: X) P" u. w" @% U' j  D# H! _
function s = modifystruct(s)3 F- O1 x: g9 X' I7 O! G: @7 q
    s.Dave =  '5086470004';7 X% K5 ]: v- z* |$ K
end
; O9 W9 o* T7 r2 G6 C用containers.Map来记录电话号码簿
4 P) ^7 z- Y9 R+ C) g上面一节我们介绍了数组,元胞数组和结构体在模拟电话号码簿这种数据结构时的局限性,这节我们来看怎么用 containers.Map 来盛放电话号码簿中的内容:8 ^: n( j8 N( d' N5 |7 o" r: z7 ~
addressMap = containers.Map;             % 首先声明一个映射表对象变量9 ^2 o4 z5 R9 I9 ?; D, D
addressMap('Abby')    = '5086470001';" z* m& ~% a( F  h
addressMap('Bob')     = '5086470002';& k6 [, U+ m7 X4 V& }
addressMap('Charlie') = '5086470003';  
& z- ^: Y* N. x% y/ G" P" e; G第一行我们声明一个containers.Map的变量(其实是containers.Map类的对象)叫做addressMap,2,3,4行通过提供Key Key Value的方式来给对象赋值,其中单引号内部的值叫做键(Key),等式右边的叫做键值(Key Value)。通过这个结构,我们在MATLAB内存中,建立起了如图Fig.3的映射关系数据结构。
9 R. g4 Q6 {4 G2 FFig.3 电话号码簿映射表: A$ V. k1 c8 p. E; t
Fig.4 Map类的UML 查找addressMap对象中的内容极其方便,比如查找我们想知道Charlie的电话号码,只需:2 B. [9 }7 N) _" j7 B6 O
% 查找  
" `" @# ~3 A: L- {num  = addressMap('Charlie')  : c  R! U- t! G* v" z7 @& e
如果我们要修改Charlie的电话号码,只需 :# p2 U3 u6 m7 r4 t1 K8 V. A5 _
% 赋值
9 I8 \  d; L& e. G3 X: uaddressMap('Charlie') = newNum;  : Y2 @: U7 [- v5 p5 {. I0 B
什么是containers.Map6 h8 L* g. p# Y* s! 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所示。
8 W) U) m# ^% U& r% n# Z) gcontainers.Map的属性和成员方法5 {. p8 o/ O& e7 ^
这节我们介绍containers.Map的属性和成员方法,假设我们这样初始化一个containers.Map对象:
/ \$ i. C/ C4 ]% 初始化一个Map对象1 f( u1 \% l: u5 T5 J2 H
addressMap = containers.Map;1 z1 D6 s) u7 q* q1 J5 q9 w
addressMap('Abby')    = '5086470001';
' v/ K/ ?6 ]$ K# K5 s' \& H: DaddressMap('Bob')     = '5086470002';
5 a$ i; C. }: e: L! [addressMap('Charlie') = '5086470003';
+ |* r5 @2 r- c8 k/ S  X在命令行中键入该对象的名称,MATLAB会显示该对象属性的基本信息:2 e' N0 u6 m6 B4 r8 Y9 U0 s! n
>> addressMap
) b5 z' u" ~( p- ?4 Z4 `addressMap =
% R9 H: U* B/ Y4 y; e5 r, r  Map with properties:
* j( E# n4 m& ~1 W4 U5 R        Count: 3          % MAP 中映射对的数目8 ?- |( U2 b5 V, U
      KeyType: char       % MAP 中键的类型 $ k6 E' n7 ]! |8 T4 }
    ValueType: any        % MAP 中键值的类型   
: g7 T. n$ k( t7 g3 v0 ?其中Count 表示Map对象中映射对的数目。按照规定,键的类型一定要是字符类型,不能是其它数据类型,而键值的类型可以是MATLAB中的任意类型:数组,元胞,结构体,MATLAB对象,甚至JAVA对象等等。因为键值的类型可以是任何MATLAB类型,所以 containers.Map 是MATLAB中极其灵活的数据类型。 成员方法keys 用来返回对象中所有的键:1 M8 `: I+ o& f# A0 k9 [
>> addressMap.keys$ L+ E4 v6 U2 i% q5 z
ans =
4 Z% G8 Y  I- Q$ ?7 p, x. y/ r    'Charlie'    'Abby'    'Bob'  
( i9 W2 d. g0 q; Z成员方法values用来返回对象中的所有键值" C6 U2 n* t9 T  R1 j
>> addressMap.values6 B  ?4 {' U7 L% j. [
ans =
0 ?- `- p% b/ [. O2 \% m    '5086470003'    '5086470001'    '5086470002'  7 B- b  H3 {+ U! q0 j
remove用来移除对象中的一个键-键值对,经过下面的操作之后,该对象中的Count的值变成了2。0 _) Q( q2 V5 ?% B
>> addressMap.remove('Charlie')
9 `2 \7 B- _1 I* j: z% A( w1 Eans =
% Y8 _0 g, u- E/ f# P+ [' @2 }  Map with properties:) W/ E: w# l+ R7 C
        Count: 2          % 映射对数目减少
/ N. w( J3 c$ W% x2 |      KeyType: char
$ K' [- i9 e% A' L, |* `- a    ValueType: any
* a% [, I5 d! m. F* N  # W( k9 l$ B, w/ w7 Y; l; I
isKey成员方法用来判断一个map对象中是否已经含有一个键值,比如:  [8 [5 W9 U# b) ~( J
>> addressMap.isKey('Abby'), y% u6 \7 P2 `- a. G4 |3 c* k
ans =% e0 F6 h2 ~9 ~9 b1 I
     14 T$ i' T; g5 h7 K, i9 t
>> addressMap.isKey('abby')    % 键是大小写敏感的+ G7 n5 a3 U: I; M% h0 V
ans =, j6 b* i* `* j* M
     0  ; @' t& @1 Z% \7 @6 T' ]% Y
isKey的功能是查询,类似之前提到的find和strcmp函数合起来使用的例子。' W. {, z) ^% X1 q5 |9 B9 e# _
containers.Map的特点
% B, i) x, o3 jcontainers.Map可以不断地扩张不需预分配
/ {- y- H6 y! L+ j+ W5 d! ~% l$ f4 z使用数组和元胞数组作为数据容器,通常要预先分配容器的大小。否则每次扩充容器,MATLAB都会重新分配内存,并且拷贝原来数组中的数据到新分配的内存中去。映射表是一个可以灵活扩充的容器,并且不需要预分配,每次往里面添加内容不会引起MATLAB重新分配内存。 我们可以通过提供两个元胞来构造映射表对象,比如下面我们构造一个机票簿数据结构:, D0 |# {2 T1 C
% 映射表的初始化方法14 `, _' Z* j$ ~6 R3 ~  F3 f
ticketMap = containers.Map( {'2R175', 'B7398', 'A479GY'}, ...
; p+ [! b2 r4 j5 b                            {'Abby', 'Bob, 'Charlie'});
/ e  d6 `5 w& T# k% Y6 j4 B% [; r  
! O2 C+ R( H! c" i也可以在程序的一开始,先声明一个对象:1 ^; Z1 o3 D/ P9 j1 M8 }
% 映射表的初始化方法2  
8 G# Y8 P# q  D( q  y8 I" F>> ticketMap = containers.Map
" w: S, |! W9 E然后在计算的过程中慢慢的向表中插入数据:' j, O4 q! `0 P4 q
% 映射表的初始化方法2  
( e8 v' T& A5 I8 P9 I+ L3 K>> ticketMap['2R175'] = 'Abby';
7 D- D9 |2 i; [6 b7 _% O: L...8 K8 q! a9 E  R) N8 Q. _
>> ticketMap['A479GY'] = 'Charlie;  ) q0 r* l6 K: ?1 y
containers.Map可以作为参数在函数内部直接修改
& ^' V/ ^/ |3 {- B$ N0 E! v3 S  A因为containers.Map是handle类(handle类的介绍参见《MATLAB面向对象编程-从入门到设计模式》第三章:MATLAB的句柄类和实值类),我们还可以方便的将这个对象传给任何MATLAB的函数,并且在函数内部直接修改对象内部的数据,并且不用把它当做返回值输出,比如:, h& {5 ?* Q5 a; ^9 x- T# s/ b9 |+ y
>> modifyMap(ticketMap);  
2 |6 M7 @8 V$ T3 K" PmodifyMap函数可以写成:
5 w6 R9 R* ^2 a% a1 k# Q& c! t  _function modifyMap(ticketMap)   % 该函数没有返回值
) ]* J0 C& o$ j  K7 u   .....
6 h1 B5 h% l6 f* }3 H. m6 ]   ticketMap(NewKey) = newID 7 k3 V( Y+ b1 l: x  M3 J. a
end  
8 S# R3 \  |. y4 b7 E2 }" O注意这里没有把修改的ticketMap当做返回值,在函数中我们直接修改了Map中的数据,这是MATLAB面向对象语言中handle类的特性。
2 g+ O9 b2 n( c: acontainers.Map可以增强程序的可读性; H9 m1 j+ t1 K! f2 p* N/ w
映射表内部可以存储各种类型的数据,并且给它们起一些有意义的名字。具体的例子见元素周期表的例子。 访问和修改这些数据的时候,可以直接使用这些有意义的名字,从而增加程序的可读性。而如果用元胞数组存储这些数据,在访问和修改这些数据的时候, 需要使用整数的Index,程序可读性不够好。
& Y7 `) o9 E- lcontainers.Map提供对数据的快速查找: ^4 w% k- J5 g' Z3 L# v7 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都相同。
! A$ F# z9 ~4 ca = 1:1000000;
& [1 Q1 U) y$ N$ j1 Hm = containers.Map(1:1000000,ones(1,1000000));  
: X/ t5 X& p1 e; S数组数据结构,使用find函数依次查找从1到(10^7)的整数,在笔者的机器上,耗时28.331901秒) p# [7 V$ u8 J3 B
% array的查找
4 e1 X9 G( V% h4 @- m! ]/ x8 I# G. rtic
: J1 I  S4 y5 s. ~$ b# jfor i=1:100000, ( `" W; E" ]5 M' S- j8 P
    find(b==i);
# s& d* o# b0 I1 {8 Aend;
  y. g" Q! }7 W: L0 @toc  ! _' u$ \  F; z
% command line结果. @+ `+ Y9 I3 n  K  H$ L" `# z
( y/ U& G3 ~' h3 X9 ~
# {. P- _1 }) j1 ?  u
, d" H) A. S, ]$ e# p

9 [! K% j1 I+ {* DElapsed time is 28.331901 seconds.  
' i$ T4 _7 ]" x" b* J! U/ U5 L" Xcontainers.Map数据结构,使用键值1到(10^7)进行访问, 在笔者的机器上,耗时只需1.323007秒, 结论是:如果有大量的数据,并且要频繁的进行查找操作,可以考虑使用containers.Map数据结构。(读者可以试试减少容器中元素的个数,观察一下什么情况下,数组的查找会更快一些。): j! J% V! ~8 t9 n* a
% 映射表的查找
* K. w, @0 a! D9 dtic
  n" E; y% ]9 g: l, M0 N: wfor i=1:100000, 6 G, D2 k1 E- l
    m(i);
9 k; W' b$ d! y8 Q- `end; . Q; I6 c# P# T9 D- I. I  m& Q) `1 r
toc
9 l% U! X3 C+ v* n- S/ s0 G% command line结果      
9 L* @8 M7 `( g& w3 L0 N! i& I, w' ?4 `

  F1 ?3 t0 f5 U3 n, [! U6 O: W, k7 B+ d
& y& W+ z; P- ^2 a5 `
Elapsed time is 1.323007 seconds.
2 N: a/ O# x0 k7 o8 C# N谈到在MATLAB中使用高级数据结构,另一种常见的做法是直接使用Java和Python对象,这里顺便比较一下在MATLAB中使用Java的哈希表的效率。再笔者的机器上,耗时3.072889秒.4 e/ N( q8 ^, g. ?4 w3 k
% JAVA哈希表的查找& P; {& _, I4 o7 |& [
s = java.util.HashSet();
( M5 y8 {8 g! g# j4 K7 X; Ufor i=1:100000, s.add(i); end   7 T- S4 a% D6 Y: a8 X9 r
tic; 4 e: J/ P2 w9 @% r" Z# r) n
for i=1:100000,
; I9 t$ g! p. b$ S. }4 U    s.contains(i);
- }6 p' {. Q" l0 i: `# Fend;
/ v* n0 q' H6 w1 X, d! ?9 Rtoc
$ ^* x& R* A& `6 E  n% command line结果      ( W" W5 x' _& D
( {4 }3 a) f1 B( z; T" L" T

& A; B+ ^0 P( s7 _
# G4 L$ `; r/ \( a3 G6 ]$ E. t# ?) T$ [+ c4 @

  c. S( q8 r% i+ K2 D3 L7 \% a$ T" y( g) v! j6 o  _
Elapsed time is 3.072889 seconds.
( p+ ^1 s- @& q8 L( A2 i: S# y* qcontainers.Map的使用实例2 C+ X5 ]/ Q! ?1 j
用来盛放元素周期表
3 b( f5 g, u0 @3 T0 Z# C工程计算中可以使用这种键-键值的例子有很多。比如物理化学计算中可以用映射表来盛放元素周期表中的内容:
/ H" l9 B7 i' J) ~, c4 _>> ptable = containers.Map;
" h* D/ e( n, K2 b: o1 l>> ptable('H')  = 1 ;: ~& l0 N# G8 H+ i9 |, F/ u
>> ptable('He') = 2 ;  
8 u( {, d9 B6 i. Q/ `8 r. w其中键是原子符号,而键值是原子量。因为键值的类型不限,所以键值本身还可以是对象,比如Atom类对象,其中包涵更多有用的内容:
7 n  ?. p% @+ V$ s- p6 w% 类文件Atom.m
  m* V1 @7 H5 Sclassdef Atom < handle
8 h% R; p! Z$ s4 J0 d2 x& c  C, q  properties
- P4 U8 \6 T1 ~* q5 V$ Z8 G      atomicNumber( p. p  R& U) T; h  ^6 p
      fullName
7 d8 |" Y$ ]# e! i' X2 {7 c  end
) w1 [1 m3 T: X( r2 N6 V  methods
3 o5 _! O  a" y8 x. h/ e0 X    function obj = Atom(num,name)
; Y7 |) ~( I# }( r* Q# f       obj.atomicNumber = num ;
' A2 Y$ b' X* f# o* [8 t8 e. i' D       obj.fullName = name5 h5 _, _7 k  k; @
    end, ], H$ l9 ]. F1 X, I' Z
  end; c+ I, ~* f( N2 v7 Q, X
end    B! r  w; C' F) I
\end{Verbatim} 于是:+ Y! w  ], ]* \: o9 E( e
% 键值可以是Atom对象                   ) A* ?9 H* A8 }2 D0 P) R9 E
>> ptable('H') = Atom(1,'hydrogen');" S+ p  n1 d, w0 r9 B
>> ptable('H') = Atom(2,'helium');  - a- H. U; y5 c0 h
用来实现快速地检索3 Z- p1 f1 M' g9 s
这个问题来自ilovematlab一个网友的提问:$ C% E! ?5 G$ R+ d6 G
大家好,最近遇到一个问题,要构建20000次以上的三元素向量,且保证每次不重复构建,该向量元素值在2000―3000之间不定数,目前采用的方法是建立一历史档案矩阵A,构建一个存储一行,A大小行数持续增加。每次在构建出一新的向量时对该向量与历史档案矩阵每行进行对比,最终确定是否构建过,若没构建过,重新构建。算法显示该检查程序运算时间在执行20000次以上时,会超过200S的运行时间。想力争减小该时间,求方法。 多谢!: h9 _. [% G5 C0 [4 r  F- @
这位网友的问题不在于如何构建这些向量,而是如何保证每次不重复的构建。他一开始想出的解决方法是用矩阵A来存储历史档案,每建立一个新的向量,和该历史档案已有的内容做比较,如果在档案中没有存档,则构建。这样设计的问题是,如他所述,当A中存储的向量变多之后,每次检查该向量是否之前被创建过的时间加长。 这是一个典型的可以用映射表来解决的问题,把线性的复杂度转成常数的复杂度。他可以给每个向量设计一个独立无二的ID,比如直接转数字成id : num2str(vector)
9 B7 F& l, ^8 l0 G% 构建
7 [0 b; u/ G' L* A$ k% [mydictionary = containers.Map3 K# \2 o8 ?5 b5 P- I7 l

* h" [8 j) w9 `2 C; \  d% 插入数据
! {3 K9 `/ ^' }id = num2str(vector) ; % 比如vector = [1 2 3];
' \5 l3 G( \0 R( e5 pmydictionary('some_id') = vector ;  2 m5 `, w7 P8 s; E& F
之后的检索也是通过vector得到独一无二的ID,然后通过isKey来判断该键值是否已经被加入到MAP中去了。
& i# c0 N2 l: g* \some_otherID = some_other_vector ;
( |# z0 F! w8 K* `% 验证是否已经构建给过6 C! b4 V, W+ T4 ?4 k/ q' N0 y
if mydictinary.isKey('some_otherID')
. B6 ~# _8 q# t/ A      % 做要做的工作: S& k9 c7 q% f' I3 i4 N* u& u
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 13:15 , Processed in 0.187500 second(s), 23 queries , Gzip On.

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

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

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