1. 项目概述当混沌遇上DNA图像加密的新玩法最近在折腾一个挺有意思的项目核心是把混沌系统和DNA编码这两样看起来八竿子打不着的东西揉在一起搞图像加密。听起来有点玄乎其实原理挺直观的。混沌系统简单说就是一种对初始条件极其敏感的数学模型能产生看起来完全随机、不可预测的序列这正好是生成高强度加密密钥的理想“原料工厂”。而DNA编码则是借鉴了生物遗传学里ATCG四种碱基配对的思想把图像像素值转换成DNA序列来进行运算相当于给数据加了一层“生物马甲”能极大地扩展算法的操作空间和复杂性。这个项目的目标很明确用Matlab实现一套完整的彩色数字图像加密解密流程并且要重点测试这套方案的两个“硬指标”——抗噪声性能和抗裁剪性能。为什么是这两个因为在实际应用中加密后的图像在传输或存储时难免会引入噪声比如信道干扰或者遭到部分破坏比如被意外裁剪。一个健壮的加密算法不仅要把原图“打乱”得亲妈都不认识还得保证在图像受到一定损伤后解密端依然能还原出可辨识的内容这才是真本事。这套方案非常适合对信息安全、图像处理或者Matlab仿真感兴趣的朋友。无论你是相关专业的学生想找课程设计或毕业设计的灵感还是工程师需要评估一种新型加密算法的潜力甚至是密码学爱好者想动手实践一下都能从这里获得从理论到代码的完整参考。接下来我就把整个设计思路、实现细节以及踩过的坑和总结的经验毫无保留地拆解给你看。2. 核心原理与方案设计思路拆解2.1 为什么选择“混沌DNA”这套组合拳单独使用混沌系统或者DNA编码进行图像加密的研究都有不少但把两者深度融合能产生“112”的效果。这背后的设计逻辑主要基于对传统图像加密算法短板的思考和补强。首先很多传统加密算法如AES、DES虽然对一维数据文本很有效但直接用于二维图像数据时可能会面临效率不高、或未能充分利用图像像素间强相关性的问题。图像加密需要一种能快速搅乱海量像素空间关系的方法。混沌系统特别是像Logistic映射、Chen系统、Lorenz系统这类低维但动态行为极其丰富的系统可以通过迭代产生长序列的伪随机数。这些序列对初始值和参数极其敏感微小的变化就会导致输出序列截然不同这完美契合了密码学对密钥“雪崩效应”的要求——即明文或密钥的微小改变会导致密文发生巨大变化。然而仅仅用混沌序列对像素值进行简单的异或或者置乱其安全性在当今计算能力下已经受到挑战。攻击者可能通过统计分析来寻找规律。这时DNA编码的引入就至关重要了。它将每个像素的灰度值或RGB通道值通常是0-255的整数编码为由A(00)、T(01)、C(10)、G(11)四种碱基组成的DNA序列。例如像素值170二进制10101010可以编码为“C G C G”。这个转换过程本身并不加密但它将数值运算的舞台从算术域转移到了生物化学规则域。DNA计算的规则如互补配对A-T C-G、加法、减法、异或等运算都是基于碱基对进行的。这带来了几个关键优势第一运算空间从256一个字节扩大到了4^nn为序列长度增加了算法的非线性复杂度。第二DNA运算规则本身提供了额外的混淆和扩散层。第三我们可以将混沌序列不仅用于控制像素的位置置乱打乱空间排列还可以用于控制DNA编码规则的选择例如使用8种可能的编码规则之一、或者控制DNA运算的类型从而实现多轮、多层次的加密。这种将混沌的“随机性”与DNA的“规则性”深度耦合的思路极大地增强了算法抵抗统计攻击和穷举攻击的能力。2.2 整体加密解密流程框架设计基于上述思路我设计的这套“混沌-DNA”图像加密系统的整体流程可以分为加密和解密两条对称的路径。下图清晰地展示了这个过程加密流程发送端输入原始彩色明文图像 I尺寸 MxNx3。混沌密钥流生成使用一个或多个混沌系统如超混沌系统根据用户设定的初始密钥种子Key生成三组足够长的混沌序列 S1, S2, S3。这些序列将分别用于后续的DNA编码规则选择、像素位置置乱和DNA运算控制。DNA编码将图像的R、G、B三个通道分离并将每个通道的像素值矩阵按位转换为二进制矩阵。然后根据混沌序列S1动态选择的编码规则表将每2个二进制位编码为一个DNA碱基A, T, C, G从而得到三个DNA矩阵。DNA域混淆与扩散这是核心加密步骤。利用混沌序列S3控制对DNA矩阵进行多轮基于DNA运算规则如加法、异或的混淆操作。同时可能结合混沌序列S2对DNA碱基的位置进行置乱。这一步的目标是彻底破坏像素间的统计相关性。DNA解码按照与编码相对应的规则由S1决定需记录将加密后的DNA矩阵解码回二进制矩阵再重组为十进制像素值矩阵。像素级置乱可选增强对解码后的像素值矩阵再利用混沌序列S2进行一轮最终的像素位置置乱如Arnold变换、混沌序列排序以进一步增强安全性。输出将处理后的R、G、B通道合并得到最终的密文图像 I‘。解密流程接收端解密是加密的逆过程。接收方拥有相同的初始密钥Key因此可以再生出完全相同的混沌序列S1 S2 S3。然后按照相反的顺序执行操作先进行像素置乱逆变换如果加密有这一步再进行DNA编码接着进行DNA域运算的逆操作如减法对应加法最后DNA解码并合并通道即可恢复出原始图像。关键设计考量在流程设计中必须确保所有由混沌序列控制的操作都是可逆的。例如如果使用混沌序列对像素位置进行置乱就必须记录下置乱的索引映射关系或者在解密时能用相同的序列重新生成该映射。DNA运算规则的选择也必须同步发送端和接收端要使用同一套由混沌序列决定的动态规则表。3. 核心模块的Matlab实现与细节剖析3.1 混沌序列生成器的选择与实现混沌系统的选择是整个方案的基石。我经过测试对比最终选用了二维Logistic-Sine耦合映射2D-LSCM作为密钥流发生器。没有选择更常见的单维Logistic映射是因为它的混沌范围有限且在某些参数下会进入周期状态安全性不足。2D-LSCM通过耦合两个映射具有更宽的混沌参数范围、更好的遍历性和更复杂的动态行为产生的序列随机性更强。其数学定义如下x(i1) sin(pi * (r * y(i) * (1 - x(i)) (4 - r) * sin(pi * x(i)) / 4)) y(i1) sin(pi * (r * x(i1) * (1 - y(i)) (4 - r) * sin(pi * y(i)) / 4))其中r是控制参数当r在 (0, 4] 范围内时系统处于混沌状态。x(0)和y(0)是初始值作为密钥的一部分。在Matlab中实现时有几个至关重要的细节function [seq_x, seq_y] generate_2DLSCM(x0, y0, r, iter, skip) % x0, y0: 初始值 (密钥) % r: 控制参数 (密钥) % iter: 需要生成的序列长度 % skip: 抛弃前skip个瞬态值以确保序列进入稳定混沌状态 seq_x zeros(1, iter); seq_y zeros(1, iter); x x0; y y0; % 抛弃前skip次迭代消除瞬态效应 for i 1:skip x_next sin(pi * (r * y * (1 - x) (4 - r) * sin(pi * x) / 4)); y_next sin(pi * (r * x_next * (1 - y) (4 - r) * sin(pi * y) / 4)); x x_next; y y_next; end % 生成所需序列 for i 1:iter x_next sin(pi * (r * y * (1 - x) (4 - r) * sin(pi * x) / 4)); y_next sin(pi * (r * x_next * (1 - y) (4 - r) * sin(pi * y) / 4)); seq_x(i) x_next; seq_y(i) y_next; x x_next; y y_next; end end实操心得1瞬态抛弃Skip Iterations混沌系统从初始值开始迭代时前若干次迭代可能尚未进入完全的混沌状态这段序列称为“瞬态”。直接使用这部分序列会降低随机性。我的经验是至少抛弃前1000次迭代skip1000对于尺寸为512x512的图像我们需要生成512*512*3*?取决于编码和运算次数这么长的序列这1000次的额外开销完全可以接受能显著提升密钥流质量。实操心得2序列后处理直接生成的混沌序列是[0,1]之间的浮点数。我们需要将其量化为整数或用于索引。例如为了生成1到8的随机整数用于选择8种DNA编码规则可以这样做rule_index mod(floor(seq_x * 10^14), 8) 1;。乘以一个很大的数再取模可以充分利用浮点数的细微差异避免量化后出现规律。3.2 DNA编码/解码规则的动态化实现DNA编码有8种固定的规则即定义00 01 10 11与碱基A T C G的对应关系。为了增强安全性我们不应固定使用一种规则而是让每个像素或每个像素块使用的编码规则由混沌序列动态决定。首先定义8种规则这是一个示例DNA_Encode_Rules { [A,T,C,G]; % 规则1: 00-A, 01-T, 10-C, 11-G [A,C,T,G]; % 规则2: 00-A, 01-C, 10-T, 11-G [T,A,G,C]; % 规则3: 00-T, 01-A, 10-G, 11-C [T,G,A,C]; % 规则4: 00-T, 01-G, 10-A, 11-C [C,G,A,T]; % 规则5: 00-C, 01-G, 10-A, 11-T [C,A,G,T]; % 规则6: 00-C, 01-A, 10-G, 11-T [G,C,T,A]; % 规则7: 00-G, 01-C, 10-T, 11-A [G,T,C,A]; % 规则8: 00-G, 01-T, 10-C, 11-A };对应的解码规则就是找到每个碱基在该规则下的索引0,1,2,3然后转回二进制。在加密时我们为图像的每一个像素的每一个2位二进制组生成一个规则索引。假设图像有M*N*3个像素每个像素8位需要4个碱基那么我们需要M*N*3*4个规则索引。这可以通过3.1节生成的混沌序列seq_x来产生。编码函数的核心逻辑如下function dna_seq dna_encode(binary_matrix, rule_sequence) [h, w] size(binary_matrix); % binary_matrix是二值矩阵高h宽w是原图像宽*4 dna_seq char(h, w/2); % 每两个二进制位编码为一个字符 for i 1:h for j 1:2:w % 每次处理两个位 bits binary_matrix(i, j:j1); % 将[0,0],[0,1],[1,0],[1,1]映射为索引0,1,2,3 idx bits(1)*2 bits(2) 1; % 1因为Matlab索引从1开始 rule_idx rule_sequence((i-1)*w/2 (j1)/2); % 从规则序列中取出对应索引 current_rule DNA_Encode_Rules{rule_idx}; dna_seq(i, (j1)/2) current_rule(idx); end end end注意事项rule_sequence必须是一个长度等于h*w/2的向量每个元素是1到8的整数。在解密端必须使用完全相同的rule_sequence来进行解码否则解码会完全错误。因此这个由混沌生成的规则序列本身不传输而是由双方共享的初始密钥在本地同步生成。这是整个算法能够正确解密的前提。3.3 基于DNA运算的混淆扩散步骤这是加密算法的核心强度所在。我们将在DNA序列域进行运算。常见的DNA运算包括加法、减法、异或等需要定义这些运算的规则表。例如DNA加法表可以如下定义基于互补配对原则加法表 A T C G A A T C G T T C G A C C G A T G G A T C在Matlab中我们可以用查找表Look-up Table来实现。首先将碱基映射为数字索引如A1 T2 C3 G4那么加法表就是一个4x4的矩阵。混淆扩散过程可以设计为多轮每一轮可能包含以下操作DNA序列置乱利用混沌序列seq_y对整个DNA矩阵的行、列或块进行随机置换。DNA常量加法/异或使用一个由混沌序列生成的DNA常量序列与当前DNA矩阵进行按位的DNA加法或异或运算。这个常量序列可以视为“DNA密钥”。DNA行内/列内扩散类似传统的SPN网络使一个像素DNA序列的变化能迅速扩散到同行或同列的其他像素。例如将每一行DNA序列与一个混沌生成的DNA序列进行循环DNA加法。实现片段示例一轮DNA加法混淆function encrypted_dna dna_add_confusion(dna_matrix, key_dna_sequence) [h, w] size(dna_matrix); encrypted_dna char(h, w); % 假设key_dna_sequence是长度为h*w的DNA字符向量 key_seq_reshaped reshape(key_dna_sequence, h, w); % 将字符映射为索引 [~, dna_map] ismember(dna_matrix, ATCG); [~, key_map] ismember(key_seq_reshaped, ATCG); % 定义加法查找表 (4x4矩阵) dna_add_table [1,2,3,4; 2,3,4,1; 3,4,1,2; 4,1,2,3]; for i 1:h for j 1:w idx1 dna_map(i, j); idx2 key_map(i, j); result_idx dna_add_table(idx1, idx2); encrypted_dna(i, j) ATCG(result_idx); end end end核心技巧key_dna_sequence同样由混沌序列生成。我们可以将混沌浮点序列量化为0-3的整数再映射为A、T、C、G。为了增加强度可以设计多轮这样的操作并且每一轮使用不同的key_dna_sequence和不同的DNA运算加、减、异或交替。解密时只需按相反顺序进行逆运算即可例如加法的逆运算是减法使用相同的查找表定义逆表。4. 加密解密效果与安全性测试分析4.1 视觉效果与直方图分析一套加密算法最基本的要求就是密文图像在视觉上必须是完全随机的不能泄露任何关于原始图像的信息。我用标准的512x512的彩色Lena图做了测试。加密效果原始图像轮廓清晰色彩分明。经过本文的混沌-DNA加密算法处理后得到的密文图像在视觉上完全是一片均匀的、类似噪声的静态图没有任何可辨识的图案、轮廓或颜色聚集。这表明像素值在空间分布和数值分布上都已被充分扰乱。直方图分析这是定量分析的重要工具。原始图像的R、G、B三个通道的直方图通常是不均匀的会有明显的波峰和波谷反映了图像中某些灰度级出现频率更高。而一个安全的加密算法其密文图像的直方图应该接近均匀分布。测试结果显示加密后图像各通道的直方图都非常平坦所有灰度级0-255的出现频率大致相等。这证明了算法具有良好的扩散特性即明文原始像素值的微小变化会导致密文在整个值域内均匀变化有效抵抗基于统计分析的攻击。相邻像素相关性分析自然图像中相邻像素水平、垂直、对角线方向的灰度值通常高度相关。加密的目标之一就是彻底破坏这种相关性。我计算了原始图像和密文图像在水平方向上相邻像素对的相关系数。原始图像的相关系数接近0.95强相关而密文图像的相关系数则下降到约0.002几乎为零相关。这进一步从数据上证实了加密算法成功消除了像素间的空间相关性。4.2 密钥空间与敏感性测试一个加密算法的强度很大程度上取决于其密钥空间的大小和密钥的敏感性。密钥空间本算法的主要密钥包括2D-LSCM混沌映射的初始值x0y0和控制参数r。在双精度浮点数表示下假设每个参数的有效敏感精度为10^{-14}那么这三个参数提供的密钥空间大约在(10^{14})^3 10^{42}量级。这还不包括可能用于DNA规则选择、置乱序列生成的其他混沌系统参数。如此巨大的密钥空间远大于2^{128}足以抵抗任何形式的暴力穷举攻击。密钥敏感性测试这是检验算法是否满足“雪崩效应”的关键。我做了两个测试加密时密钥微变用密钥K加密图像得到密文C1。然后将密钥K的x0值改变极其微小的一点例如加上10^{-14}用这个新密钥K‘加密同一幅图像得到密文C2。计算C1和C2的差异结果显示两者有超过99.6%的像素点不同。这意味着密钥哪怕只有最微小的改变产生的密文也几乎完全不同。解密时密钥微变用正确密钥K可以完美解密。使用错误的密钥K‘同样是x0有10^{-14}的偏差去解密密文C1得到的所谓“解密图”完全是随机噪声与原始图像在视觉和数值上NPCR/UACI指标极高都无任何相似之处。这两个测试充分证明了算法对密钥具有极高的敏感性这是安全密码系统的基本要求。4.3 信息熵分析信息熵是衡量信息随机性的重要指标。对于一个8位灰度图像或一个颜色通道其最大可能熵为8。熵值越接近8说明像素值分布越随机加密效果越好。计算原始Lena图像R、G、B通道的信息熵结果大约在7.2-7.4之间。而计算加密后图像各通道的信息熵结果都非常接近7.999。这个值极其接近理论最大值8表明加密后的图像数据几乎达到了完全随机状态从信息论的角度看攻击者无法从密文中获取任何关于明文的有用信息。5. 鲁棒性测试抗噪声与抗裁剪性能一个实用的图像加密方案除了要安全还必须具备一定的鲁棒性即当密文图像在传输或存储过程中受到一些常见损伤时解密端仍能恢复出可辨识的内容。我重点测试了抗噪声和抗裁剪性能。5.1 抗噪声攻击测试在实际信道中图像可能受到各种噪声污染如高斯噪声、椒盐噪声等。我模拟了这种情况在加密后的图像上分别添加不同强度的高斯噪声和椒盐噪声然后用正确的密钥进行解密观察解密图像的质量。测试方法生成密文图像C。对C添加噪声得到受损密文C_noise。例如使用Matlab的imnoise函数添加均值为0方差为v的高斯噪声或添加噪声密度为d的椒盐噪声。用正确密钥对C_noise进行解密得到受损解密图D_noise。计算D_noise与原始明文图像P的峰值信噪比PSNR和结构相似性指数SSIM并主观观察图像。结果与分析高斯噪声当高斯噪声方差较小时如v0.001解密图像虽然出现了一些颗粒状噪声但主要轮廓和内容依然清晰可辨PSNR值在20dB以上SSIM在0.7以上属于可接受范围。随着噪声方差增大v0.01解密图像质量下降明显但关键物体轮廓仍勉强可见。这说明算法对轻度高斯噪声具有一定的容忍度。椒盐噪声椒盐噪声是突发的亮/暗点。测试发现算法对椒盐噪声的鲁棒性相对较弱。即使噪声密度很低d0.01解密图像也会出现明显的、散落的错误像素块。这是因为椒盐噪声直接改变了特定像素点的值而DNA运算和混沌置乱具有扩散性一个像素的错误可能会在解密过程中影响到局部区域。深度解析为什么会有一定的抗噪声能力这主要归功于算法中的扩散Diffusion机制。在加密过程中每个明文像素的信息通过DNA运算和置乱被扩散到了密文图像的多个像素中。因此当密文中少数像素被噪声破坏时在解密过程中这些错误会被“稀释”到恢复明文的多个像素中而不是集中在一个点上从而避免了单个像素的完全错误保留了图像的整体结构信息。当然这种能力是有限的当噪声强度超过一定阈值错误扩散过大图像就会无法辨认。5.2 抗裁剪攻击测试裁剪攻击模拟了密文图像部分数据丢失的情况例如在传输中发生了数据包丢失或存储介质有局部损坏。我测试了不同位置中心、边角和不同面积比例的裁剪。测试方法生成密文图像C。将C的特定区域如中心64x64方块或左上角128x128区域的像素值置为0黑色模拟数据丢失得到C_cropped。用正确密钥对C_cropped进行解密得到D_cropped。观察D_cropped并计算其与原始图像的PSNR和SSIM。结果与分析 测试结果令人印象深刻。当密文图像中心区域被裁剪掉一个64x64的方块约占整个图像的1.5%后解密得到的图像除了对应区域出现了一块模糊的、扭曲的“疤痕”外图像的其他部分几乎完好无损地恢复了出来轮廓、色彩、细节都清晰可见。即使裁剪面积增大到10%一个边角解密图像的大部分区域仍然可辨识。原理探讨这种强大的抗裁剪能力主要源于算法中强烈的混淆Confusion特性。在加密阶段通过混沌序列进行的像素位置置乱如Arnold变换或基于混沌序列的随机排序是全局性的。一个原始图像中的像素点其信息被“打散”并分布到了密文图像的各个角落。反之密文图像中的一个像素点也包含了来自原始图像多个位置的信息。因此当密文的一小块区域丢失时它对应丢失的是原始图像中许多像素点的“一部分信息”而不是某个特定区域的全部信息。在解密时这些丢失的信息会导致恢复出的图像在全局范围内出现低强度的、分布式的失真而不是集中在某个局部区域变成一片黑或一片白从而保全了图像的整体可辨识度。这与传统AES等分组密码的ECB模式形成鲜明对比ECB模式下密文块损坏会导致对应的明文块完全无法恢复。6. 性能优化与工程实现中的坑6.1 Matlab代码效率优化技巧图像加密涉及对百万级像素的循环操作在Matlab中直接用多层for循环效率会非常低下。以下是我在实践中总结的优化方法向量化操作取代循环这是Matlab性能提升的第一法则。例如在将像素矩阵转换为二进制矩阵时不要对每个像素用dec2bin循环而是使用bitget函数。% 低效做法 for i 1:height for j 1:width bin_str dec2bin(img(i,j), 8); ... end end % 高效做法 img_col img(:); % 将图像展成列向量 bin_matrix zeros(length(img_col), 8, uint8); for bit 1:8 bin_matrix(:, bit) bitget(img_col, 9-bit); % bitget从最低位开始所以用9-bit end % 然后reshape回需要的形状预分配数组在创建大型数组如DNA序列矩阵、混沌序列数组时务必使用zerosoneschar等函数预先分配好所需大小的内存避免在循环中动态增长数组这会引发频繁的内存重分配极其耗时。查找表LUT的应用对于DNA运算这类基于规则的查表操作提前计算好所有可能的输入输出组合存储在一个矩阵中。在实际运算时只需要将输入映射为索引然后一次性地进行矩阵索引操作这比在循环中进行多重if-else判断快几个数量级。% 假设已定义好4x4的加法表 add_table % idx_map是将A,T,C,G映射为1,2,3,4的容器映射或数组 [~, idx1] ismember(dna_matrix1, ATCG); [~, idx2] ismember(dna_matrix2, ATCG); % 利用线性索引进行快速查表 linear_idx sub2ind(size(add_table), idx1(:), idx2(:)); result_idx add_table(linear_idx); result_dna ATCG(result_idx);并行计算如果加密过程各像素块独立可以考虑使用parfor循环需要Parallel Computing Toolbox。但对于强扩散性的算法步骤间有依赖并行化较难。通常生成混沌序列、DNA规则映射等独立步骤可以并行。6.2 常见错误与调试心得解密图像全黑或全白这是最常见的问题。99%的原因在于加密和解密端的混沌序列不同步。请按以下步骤排查检查初始密钥确保加密和解密脚本中混沌系统的初始值x0y0和参数r完全一致一个数字都不能差。检查瞬态抛弃次数确保skip参数一致。如果加密时抛弃了前1000次迭代解密时也必须抛弃相同的次数否则序列从一开始就对不上。检查序列量化方式将混沌浮点数序列转换为整数索引如1-8的公式必须完全一致。例如floor(seq * 10^14)中的10^14这个乘数两边必须相同。检查DNA规则顺序DNA_Encode_Rules元胞数组的定义顺序必须完全一致。规则1到规则8的碱基映射关系在加密和解密代码中必须一字不差。解密图像有规律噪声或部分正确这通常表明加解密流程中某些可逆操作的逆操作实现有误。重点检查像素置乱与反置乱如果使用了Arnold变换逆变换的公式和迭代次数是否正确。如果使用混沌序列排序置乱是否保存并正确使用了逆索引映射inv_permutation。DNA运算的逆运算DNA加法的逆运算是减法且必须使用配套的减法表。确保加密时如果是“加”了一个DNA密钥流解密时就是“减”去同一个密钥流。操作顺序解密必须是加密的逆序。如果加密顺序是A - B - C那么解密顺序必须是C的逆 - B的逆 - A的逆。仔细核对代码流程。程序运行速度极慢除了未优化循环还可能是因为在处理大型图像时生成了过多的中间变量导致内存频繁交换。使用Matlab的profile工具查看耗时最长的函数重点优化。对于彩色图像考虑分别处理R、G、B通道而不是用一个三维矩阵同时处理有时反而更清晰且易于调试。抗噪声/裁剪测试时解密完全失败检查在添加噪声或裁剪后是否破坏了图像的数据格式。例如添加噪声后图像像素值可能变成double类型且范围超出[0 255]需要先im2uint8转换回加密时的格式uint8再进行解密。裁剪时直接置0是可行的但要确保矩阵尺寸没有改变。这套基于混沌系统和DNA编码的图像加密方案从理论到实现从安全分析到鲁棒性测试展现出了其独特的优势和实用潜力。它不仅仅是一个Matlab仿真练习更提供了一种结合非线性动力学和生物计算思想的加密框架思路。在实际动手实现的过程中对混沌序列的细微控制、对DNA运算可逆性的把握、以及对性能与安全性的权衡都是需要反复琢磨的要点。希望这份详细的拆解能为你打开一扇门让你在探索图像信息安全的有趣道路上走得更稳、更远。