位运算详解之异或运算的奇妙操作

位运算详解之异或运算的奇妙操作

    • 一、异或运算的本质与核心性质
      • 1.1 异或运算的定义与逻辑规则
      • 1.2 异或运算的核心代数性质
        • (1)自反性:`a ^ a = 0`
        • (2)恒等性:`a ^ 0 = a`
        • (3)交换律:`a ^ b = b ^ a`
        • (4)结合律:`(a ^ b) ^ c = a ^ (b ^ c)`
        • (5)分配律:`a ^ (b & c) = (a ^ b) & (a ^ c)`
      • 1.3 异或运算的二进制位操作特性
    • 二、异或运算的经典应用场景
      • 2.1 变量交换的优雅实现
      • 2.2 寻找数组中唯一出现一次的数字
        • 问题描述(LeetCode 136)
        • 异或解法
        • 原理剖析
      • 2.3 异或运算在加密解密中的应用
        • 简易异或加密算法
        • 加密原理
    • 三、异或运算的高级算法技巧
      • 3.1 寻找数组中出现奇数次的两个数字
        • 问题描述(LeetCode 260)
        • 异或解法
        • 解题思路
      • 3.2 异或运算与线性反馈移位寄存器(LFSR)
        • 原理说明
      • 3.3 异或运算与布隆过滤器
        • 核心逻辑
    • 四、异或运算的硬件实现与性能分析
      • 4.1 异或门的电路设计
      • 4.2 异或运算的性能优势
      • 4.3 异或运算与其他运算的性能对比
    • 五、异或运算的边界情况与注意事项
      • 5.1 异或运算的陷阱
        • (1)异或交换的限制
        • (2)异或加密的安全隐患
      • 5.2 异或运算的优化实践
        • (1)利用异或实现快速归零
        • (2)异或运算替代条件判断
    • 六、异或运算的前沿应用
      • 6.1 异或在量子计算中的应用
      • 6.2 异或在区块链中的应用

异或运算(XOR)以其独特的逻辑特性和高效的运算效率,在算法设计、数据加密、硬件电路设计等领域应用广泛,从交换变量的优雅实现到破解"只出现一次的数字"这类经典算法题,异或运算总能以简洁而精妙的方式解决看似复杂的问题。本文我就将深入剖析异或运算的本质、核心性质及一系列令人称奇的骚操作,带您进入这种神奇的位运算。

一、异或运算的本质与核心性质

1.1 异或运算的定义与逻辑规则

异或运算(XOR)是一种二元位运算,其逻辑规则是:当两个二进制位相异时结果为1,相同时结果为0。用符号"^"表示,运算规则可表示为:

  • 0 ^ 0 = 0
  • 0 ^ 1 = 1
  • 1 ^ 0 = 1
  • 1 ^ 1 = 0

从逻辑表达式看,异或运算等价于"不进位加法",这一特性使其在硬件加法器设计中扮演关键角色。例如,十进制数5(二进制101)和3(二进制011)的异或运算过程如下:

  101
^ 011
------110  // 结果为6

1.2 异或运算的核心代数性质

异或运算具有一系列优美的代数性质,这些性质是其奇妙应用的理论基础:

(1)自反性:a ^ a = 0

任意数与自身异或结果为0,这一性质常用于数据归零操作。例如:

int a = 5;
a ^= a;  // a的值变为0
(2)恒等性:a ^ 0 = a

任何数与0异或结果保持不变,这是异或加密的基础性质。例如:

int a = 5;
int b = a ^ 0;  // b的值仍为5
(3)交换律:a ^ b = b ^ a

异或运算满足交换律,运算顺序不影响结果:

int a = 3, b = 5;
int xor1 = a ^ b;
int xor2 = b ^ a;
System.out.println(xor1 == xor2);  // 输出true
(4)结合律:(a ^ b) ^ c = a ^ (b ^ c)

多个异或运算可任意结合,这在密码学和数据校验中至关重要:

int a=1, b=2, c=3;
int xor1 = (a ^ b) ^ c;
int xor2 = a ^ (b ^ c);
System.out.println(xor1 == xor2);  // 输出true
(5)分配律:a ^ (b & c) = (a ^ b) & (a ^ c)

异或对与运算满足分配律,这一性质在布尔代数化简中有用:

int a=5, b=3, c=2;
int left = a ^ (b & c);
int right = (a ^ b) & (a ^ c);
System.out.println(left == right);  // 输出true(需具体计算验证)

1.3 异或运算的二进制位操作特性

从二进制位视角看,异或运算具有以下关键特性:

  • 位翻转:若想翻转某个二进制位(0变1或1变0),只需与1异或:a ^ 1会翻转a的最低位
  • 位保留:若想保留某个二进制位,只需与0异或:a ^ 0保持a的对应位不变
  • 无进位加法:两个二进制位的异或结果等价于不考虑进位的加法结果,这是半加器的设计原理

二、异或运算的经典应用场景

2.1 变量交换的优雅实现

传统的变量交换需要借助临时变量,而异或运算可在不使用临时变量的情况下完成交换,这是其最广为人知的奇妙应用:

public class XorSwap {public static void main(String[] args) {int a = 5, b = 3;System.out.println("交换前: a=" + a + ", b=" + b);// 异或交换三步曲a ^= b;  // a = a ^ b = 5 ^ 3 = 6 (110)b ^= a;  // b = b ^ (a ^ b) = 3 ^ 6 = 5 (101)a ^= b;  // a = (a ^ b) ^ b = 6 ^ 5 = 3 (011)System.out.println("交换后: a=" + a + ", b=" + b);}
}

原理分析:利用异或的自反性和交换律,通过三次异或操作实现交换。该方法的时间复杂度为O(1),空间复杂度为O(1),但需注意:

  1. 不能用于同一变量交换(如swap(a, a)
  2. 在处理浮点数时需先转换为整数类型

2.2 寻找数组中唯一出现一次的数字

问题描述(LeetCode 136)

给定一个整数数组,除了某个元素外,其余元素均出现两次,找出这个只出现一次的元素。

异或解法
public class SingleNumber {public int singleNumber(int[] nums) {int result = 0;for (int num : nums) {result ^= num;}return result;}public static void main(String[] args) {SingleNumber solution = new SingleNumber();int[] nums = {2, 2, 1, 3, 3};System.out.println(solution.singleNumber(nums));  // 输出1}
}
原理剖析
  • 由于异或运算满足交换律和结合律,且a ^ a = 0a ^ 0 = a
  • 数组中出现两次的数字异或后结果为0,最终剩下的就是只出现一次的数字
  • 时间复杂度:O(n),空间复杂度:O(1),这是该问题的最优解法

2.3 异或运算在加密解密中的应用

简易异或加密算法

异或加密是最基础的对称加密方式,利用"加密密钥异或明文=密文"和"密文异或密钥=明文"的特性:

public class XorEncryption {// 加密函数public static byte[] encrypt(byte[] data, byte[] key) {byte[] result = new byte[data.length];for (int i = 0; i < data.length; i++) {result[i] = (byte) (data[i] ^ key[i % key.length]);}return result;}// 解密函数(与加密函数相同)public static byte[] decrypt(byte[] encrypted, byte[] key) {return encrypt(encrypted, key);}public static void main(String[] args) {String plainText = "Hello XOR Encryption!";byte[] data = plainText.getBytes();byte[] key = "密钥".getBytes();byte[] encrypted = encrypt(data, key);byte[] decrypted = decrypt(encrypted, key);System.out.println("明文: " + plainText);System.out.println("密文: " + new String(encrypted));System.out.println("解密: " + new String(decrypted));}
}
加密原理
  • 异或加密的安全性基于"一次一密"原则,若密钥是随机且长度与明文相同,则具有理论上的绝对安全性
  • 实际应用中需注意:
    1. 密钥的随机性和保密性至关重要
    2. 相同密钥加密相同明文会产生相同密文,存在被统计分析的风险
    3. 该算法对二进制数据(如图像、音频)同样有效

三、异或运算的高级算法技巧

3.1 寻找数组中出现奇数次的两个数字

问题描述(LeetCode 260)

给定一个整数数组,其中恰好有两个元素出现奇数次,其余元素均出现偶数次,找出这两个元素。

异或解法
public class TwoSingleNumbers {public int[] singleNumber(int[] nums) {// 第一步:所有数异或得到a^bint xorResult = 0;for (int num : nums) {xorResult ^= num;}// 第二步:找到异或结果中最低位的1int lowestOne = xorResult & (-xorResult);// 第三步:分组异或int[] result = new int[2];for (int num : nums) {if ((num & lowestOne) == 0) {result[0] ^= num;} else {result[1] ^= num;}}return result;}public static void main(String[] args) {TwoSingleNumbers solution = new TwoSingleNumbers();int[] nums = {1, 2, 1, 3, 2, 5};int[] result = solution.singleNumber(nums);System.out.println("出现奇数次的两个数: " + result[0] + " 和 " + result[1]);}
}
解题思路
  1. 所有数异或得到a ^ b(a和b是两个目标数)
  2. 找到a ^ b中最低位的1,该位表明a和b在该位不同
  3. 根据该位将数组分为两组,分别异或得到a和b
  4. 时间复杂度:O(n),空间复杂度:O(1)

3.2 异或运算与线性反馈移位寄存器(LFSR)

线性反馈移位寄存器是生成伪随机数的重要器件,而异或运算是其核心运算单元:

public class LFSR {private int state;private int tap;// 初始化LFSR状态和抽头位置public LFSR(int initialState, int tapPosition) {state = initialState;tap = 1 << tapPosition;}// 生成一个随机位public int nextBit() {// 计算反馈位(抽头位置异或)int feedback = (state & tap) != 0 ? 1 : 0;// 右移一位,并将反馈位放入最高位state = (state >> 1) | (feedback << 31);return state & 1;}// 生成n位随机数public int next(int n) {int result = 0;for (int i = 0; i < n; i++) {result = (result << 1) | nextBit();}return result;}public static void main(String[] args) {LFSR lfsr = new LFSR(12345, 31);  // 初始状态和抽头位置System.out.println("随机数: " + lfsr.next(16));}
}
原理说明
  • LFSR通过异或运算实现反馈逻辑,产生周期性的伪随机序列
  • 抽头位置的选择决定了随机序列的周期和随机性
  • 异或运算的高效性使其非常适合硬件实现,广泛应用于通信领域的伪随机序列生成

3.3 异或运算与布隆过滤器

布隆过滤器是一种空间效率高的概率性数据结构,异或运算在其哈希函数组合中发挥作用:

public class XorBloomFilter {private boolean[] bits;private int size;private int hashFuncs;public XorBloomFilter(int capacity, double errorRate) {// 计算位数组大小和哈希函数数量size = (int) (-capacity * Math.log(errorRate) / (Math.log(2) * Math.log(2)));hashFuncs = (int) (size / capacity * Math.log(2));bits = new boolean[size];}// 插入元素public void add(String element) {for (int i = 0; i < hashFuncs; i++) {int hash = xorHash(element, i);bits[hash % size] = true;}}// 检查元素是否存在public boolean contains(String element) {for (int i = 0; i < hashFuncs; i++) {int hash = xorHash(element, i);if (!bits[hash % size]) {return false;}}return true;}// 异或哈希函数private int xorHash(String str, int seed) {int hash = seed;for (char c : str.toCharArray()) {hash ^= (hash << 5) + (hash >> 2) + c;}return hash;}
}
核心逻辑
  • 通过多个异或哈希函数生成不同的哈希值,提高布隆过滤器的准确性
  • 异或操作的混合特性有助于减少哈希冲突
  • 相比传统哈希函数,异或哈希在硬件实现上更高效

四、异或运算的硬件实现与性能分析

4.1 异或门的电路设计

异或运算在硬件层面由异或门实现,其电路结构如下:

     A\XOR门   Y/B

异或门的逻辑表达式为:Y = A ⊕ B = A'B + AB',其中A和B是输入,Y是输出。在CMOS电路中,异或门由6个晶体管组成,是最基础的逻辑门之一。

4.2 异或运算的性能优势

在现代处理器中,异或运算具有以下性能特点:

  • 指令周期短:异或运算在x86架构中对应XOR指令,其CPI(Clock Per Instruction)通常为1
  • 零延迟特性:某些处理器(如ARM)中,异或运算可实现零延迟结果
  • 功耗低:相比乘法、除法等运算,异或运算的功耗可忽略不计
  • 并行性好:SIMD指令集(如SSE、AVX)支持同时对多个数据进行异或运算

4.3 异或运算与其他运算的性能对比

在Java中,异或运算与其他位运算的性能测试(测试1亿次运算):

public class PerformanceTest {public static void main(String[] args) {int a = 0x12345678, b = 0x87654321;// 异或运算性能测试long startXor = System.nanoTime();for (int i = 0; i < 100000000; i++) {int x = a ^ b;}long endXor = System.nanoTime();// 与运算性能测试long startAnd = System.nanoTime();for (int i = 0; i < 100000000; i++) {int x = a & b;}long endAnd = System.nanoTime();// 或运算性能测试long startOr = System.nanoTime();for (int i = 0; i < 100000000; i++) {int x = a | b;}long endOr = System.nanoTime();System.out.println("异或运算耗时: " + (endXor - startXor) + "纳秒");System.out.println("与运算耗时: " + (endAnd - startAnd) + "纳秒");System.out.println("或运算耗时: " + (endOr - startOr) + "纳秒");}
}

测试结果分析(不同机器可能有差异):

  • 异或运算耗时通常与与、或运算相近,均属于高效位运算
  • 相比算术运算(如加法、乘法),位运算的耗时约为其1/10到1/5
  • 在加密、哈希等场景中,大量使用异或运算可显著提升性能

五、异或运算的边界情况与注意事项

5.1 异或运算的陷阱

(1)异或交换的限制
int a = 5;
a ^= a;  // a变为0,这是正确的
int b = 5, c = 5;
b ^= c ^= b ^= c;  // 这行代码在Java中行为未定义,不可靠

在Java中,复合赋值表达式的运算顺序是从左到右,但中间结果的存储顺序未定义,因此连续异或赋值可能导致意外结果。

(2)异或加密的安全隐患
byte[] data = "敏感信息".getBytes();
byte[] key = "弱密钥".getBytes();
byte[] encrypted = encrypt(data, key);

使用弱密钥(如重复、短长度)会导致加密失效,异或加密必须配合强密钥管理策略。

5.2 异或运算的优化实践

(1)利用异或实现快速归零
int a = 0x1234;
a ^= a;  // 比a=0更高效?

在某些处理器中,a ^= a可能比a=0生成更高效的机器码,但现代编译器通常会将两者优化为相同指令。

(2)异或运算替代条件判断
// 传统方式
int x = a > b ? a : b;
// 异或优化(仅适用于特定场景)
int diff = a - b;
int sign = (diff >> 31) & 1;
int x = a - diff * sign;

这种优化利用了异或的位操作特性,但代码可读性降低,需谨慎使用。

六、异或运算的前沿应用

6.1 异或在量子计算中的应用

在量子计算中,异或运算对应量子非门(X门)和受控非门(CNOT门),是构建量子电路的基础门之一。CNOT门的量子电路表示如下:

┌───┐
│ X │
└─┬─┘│▼

其中控制位决定目标位是否执行异或操作,这是实现量子纠缠和量子算法的关键组件。

6.2 异或在区块链中的应用

在区块链的哈希算法中,异或运算用于混合不同数据的哈希值,例如在Merkle树的构建过程中:

public class MerkleTree {private String[] hashes;public MerkleTree(String[] data) {// 计算叶子节点哈希hashes = new String[data.length];for (int i = 0; i < data.length; i++) {hashes[i] = hash(data[i]);}// 构建Merkle树buildTree();}private void buildTree() {int level = hashes.length;while (level > 1) {int nextLevel = (level + 1) / 2;String[] newHashes = new String[nextLevel];for (int i = 0; i < nextLevel; i++) {int left = i * 2;int right = Math.min(left + 1, level - 1);newHashes[i] = hash(hashes[left] + "^" + hashes[right]); // 异或混合哈希值}hashes = newHashes;level = nextLevel;}}private String hash(String data) {// 实际应用中使用SHA-256等安全哈希算法return data;}
}

异或运算在哈希值混合中能有效破坏输入与输出的线性关系,增强哈希函数的抗碰撞性。

That’s all, thanks for reading!
觉得有用就点个赞、收进收藏夹吧!关注我,获取更多干货~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/87059.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Element Plus 去除下拉菜单周黑边

问题&#xff1a; 如上图所示&#xff0c;当鼠标移入&#xff08;hover&#xff09;和点击时就会围绕一圈黑色边框&#xff0c;但通过本文的方案 100% 完美解决。 解决方案: :deep(:focus-visible) {outline: none; } 备用方案 :deep(.el-tooltip__trigger:focus-visible) …

React Native 项目实战 —— 记账本应用开发指南

React Native 项目实战 —— 记账本应用开发指南 项目概述&#xff1a;本文将指导您使用 React Native 开发一个简单的记账本应用&#xff0c;帮助用户记录收入和支出。核心内容&#xff1a;我们将分析功能模块、设计接口、划分组件结构、管理数据流、实现页面跳转&#xff0c…

从 PPO、DPO 到 GRPO:大语言模型策略优化算法解析

从 PPO、DPO 到 GRPO&#xff1a;大语言模型策略优化算法解析 背景与简介 大语言模型&#xff08;LLM&#xff09;的训练通常分为预训练和后训练两个阶段。预训练阶段&#xff0c;模型在海量文本上学习下一词预测的能力&#xff1b;后训练阶段&#xff0c;我们希望进一步对齐…

React中使用Day.js指南

文章目录 引言什么是Day.js&#xff1f;Day.js的核心特性 安装和基础配置安装Day.js基础导入和使用 在React中的基础使用1. 显示格式化日期2. 实时时钟组件 常用插件配置1. 相对时间插件2. 高级格式化插件3. 时区处理插件 实战案例&#xff1a;博客文章时间组件高级应用场景1. …

【系统设计【1】】系统设计面试方法论:从0到百万用户的需求到架构的推演

文章目录 一、系统设计面试的底层逻辑&#xff1a;从需求到架构的推演&#xff08;一&#xff09;需求澄清&#xff1a;界定问题边界&#xff08;二&#xff09;分层设计&#xff1a;从单节点到分布式的演进1. Web层&#xff1a;无状态化与负载均衡2. 数据层&#xff1a;数据库…

京津冀城市群13城市空间权重0-1矩阵

京津冀城市群13城市空间权重0-1矩阵 1、数据说明&#xff1a;京津冀13个城市&#xff1a;北京市、保定市、沧州市、承德市、邯郸市、衡水市、廊坊市、秦皇岛市、石家庄市、唐山市、邢台市、张家口市、天津市、 2、指标解释&#xff1a;空间权重矩阵是一种用于表征空间表达式的…

七大技术路线解析:自动驾驶如何被数据重新定义

自动驾驶技术从实验室的算法验证走向大规模量产应用&#xff0c;是一场充满挑战的征程。这段征程的核心驱动力&#xff0c;不仅是芯片和传感器的升级&#xff0c;更是一场关于数据的“喂养”竞赛——从简单的像素标注到多模态大模型的理解&#xff0c;数据需求的演变悄然推动着…

计网复习知识(16)传输层及其协议功能

目录 考研大纲 1.传输层概述 端口号 有连接/无连接传输 可靠/不可靠传输 2.UDP协议 2.1 udp数据报 2.2 udp检验 3.TCP协议 3.1 TCP协议的框架梳理 3.2 TCP报文段**** 3.3 三次握手与四次挥手 三次握手 四次挥手 3.4 可靠传输与流量控制 流量控制&#xff1a;滑动…

每天一个前端小知识 Day 1

语义化 HTML&#xff08;Semantic HTML&#xff09; 1. 什么是语义化 HTML&#xff1f; 语义化 HTML 指的是使用符合内容含义的标签&#xff0c;而不仅仅为了布局或样式。例如&#xff1a; <article>…</article> <nav>…</nav> <header>…&l…

在docker中部署mysql

部署 MySQL&#xff08;端口 9006&#xff09; 1. 创建数据目录 mkdir -p ~/qihuang/mysql/data2. 启动 MySQL 容器 docker run -d \--name mysql-qihuang \-p 9006:3306 \-v ~/qihuang/mysql/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORDroot \-e MYSQL_DATABASEqihuangdb…

JavaScript基础-事件对象

一、前言 在前端开发中&#xff0c;用户与页面的交互行为&#xff08;如点击按钮、输入文本、滚动页面等&#xff09;都会触发相应的事件。而这些事件发生时&#xff0c;浏览器会自动创建一个 事件对象&#xff08;Event Object&#xff09;&#xff0c;它包含了当前事件的所有…

蓝桥杯_染色_bfs_Java

临时抱抱佛脚&#xff0c;太浮躁了&#xff0c;蓝桥杯已经快1个半月没做题了。 本人比较菜&#xff0c;感觉这个时间节点也只能把暴力题给尽量多做做&#xff0c;找找做题手感&#xff0c;其他就纯凭运气了吧。T-T。 题目 问题描述 小蓝有一个 n 行 m 列的白色棋盘, 棋盘的每一…

MySQL 究极奥义·动态乾坤大挪移·无敌行列转换术

导入大SQL文件 [mysqld] # 大批量导入优化 bulk_insert_buffer_size1G max_allowed_packet1G innodb_autoextend_increment512M innodb_buffer_pool_size4G innodb_log_buffer_size4G innodb_log_file_size4G动态行列转换 DROP TABLE IF EXISTS tb_score;CREATE TABLE tb_sco…

Excel大厂自动化报表实战(互联网金融-数据分析周报制作中)

这是Excel大厂自动化报表实战第三期--互联网金融-数据分析周报制作中 数据资源已经与这篇博客捆绑&#xff0c;有需要者可以下载通过网盘分享的文件&#xff1a;2.4自动化报表-8月成交数据.xlsx&#xff0c;2.4自动化报表-8月获客数据.csv等2个文件 链接: https://pan.baidu.c…

langchain从入门到精通(七)——利用回调功能调试链应用 - 让过程更透明

1. Callback 功能介绍 Callback 是 LangChain 提供的回调机制&#xff0c;允许我们在 LLM 应用程序的各个阶段使用 hook &#xff08;钩子&#xff09;。钩子的含义也非常简单&#xff0c;我们把应用程序看成一个一个的处理逻辑&#xff0c;从开始到结束&#xff0c;钩子就是在…

如何使用Postman做接口自动化测试

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 本文适合已经掌握 Postman 基本用法的读者&#xff0c;即对接口相关概念有一定了解、已经会使用 Postman 进行模拟请求等基本操作。 工作环境与版本&#xff1a; …

ELK日志文件分析系统——E(Elasticsearch)

目录 基本概念 一、架构设计 二、核心原理 三、关键特性 四、应用意义 部署步骤 ‌一、环境准备‌ ‌二、安装 Elasticsearch‌ ‌三、关键配置&#xff08;elasticsearch.yml&#xff09;‌ ‌四、启动与验证‌ ‌五、集群扩展&#xff08;新增节点&#xff09;‌ …

融智学教育观及其数学公式体系凝练汇总

摘要&#xff1a;本文系统阐述了邹晓辉教授的融智学教育观&#xff0c;通过原创数学公式体系构建了人机协同教育模型。核心内容包括&#xff1a;认知本体论&#xff08;文明智慧当量方程&#xff09;、方法论&#xff08;七遍通训练算子&#xff09;、生态位控制论&#xff08;…

互联网大厂Java求职面试:AI大模型应用实践中的架构挑战与实战

互联网大厂Java求职面试&#xff1a;AI大模型应用实践中的架构挑战与实战 引言 在当今技术飞速发展的时代&#xff0c;AI大模型已成为企业数字化转型的重要引擎。无论是内容生成、智能客服、个性化推荐&#xff0c;还是知识图谱构建和语义理解&#xff0c;大模型的应用场景正在…

龟兔赛跑算法(Floyd‘s Cycle-Finding Algorithm)寻找重复数

龟兔赛跑算法&#xff08;Floyd’s Cycle-Finding Algorithm&#xff09;寻找重复数 问题描述 给定一个长度为 N1 的数组 nums&#xff0c;其中每个元素的值都在 [1, N] 范围内。根据鸽巢原理&#xff0c;至少有一个数字是重复的。请找出这个重复的数字。 要求&#xff1a; …