二进制的一些技巧与运用

Hello world!我是猿码叔叔,一个 4 年 java 开发经验的北漂。目前正在努力学习算法。

如果你有过一些开发经验,想简单了解一下二进制在 java 语言中的的基础运用与原理,那么本篇很适合你继续往下阅读。

一、介绍二进制

说起二进制,我们不得不从 0 开始。假设现在让我们数数,一般我们数到 9 的下一个数就会进十,变成 10。而二进制,就是数到 2 变成 10。任何进制包括  8 或 16 进制的进制原理都和这个类似。

我们用计算机存储的数据,都会使用二进制进行存储,也就是 n 个 0 与 n 个 1 进行组合。主要因为二进制的数制系统相较于其他进制只有 0 和 1 两个标识,简单且方便计算机识别与处理。

二、二进制的运算符

下面简单介绍这些运算符。

按位与(And)( & )

按位与的运算原理,若把 1 看作 true 的话与逻辑运算符 && 很像。当两个数的二进制同位都为 1 时,运算为 1,否则为 0。

(1 & 1) = (01 & 01) = 01

(1 & 0) = (01 & 00) = 00

(2 & 1) = (10 & 01) = 00

按位或(Or)( | ) 

按位或与逻辑运算的 || 很像。当两个数的二进制同位只要存在1个或2个 1 时,运算都为 1,否则为 0。

(1 | 1) = (01 | 01) = 01

(1 | 0) = (01 | 00) = 01

(2 | 1) = (10 | 01) = 11

按位异或(Xor)( ^ ) 

按位异或就是当两个数的二进制同位进行差值取绝对值。

(1 ^ 1) = (01 ^ 01) = 00

(1 ^ 0) = (01 ^ 00) = 01

(2 ^ 1) = (10 ^ 01) = 11

取反( ~ ) 

取反,顾名思义,就是将二进制每位上的 0 变为 1,1 变为 0。

(~2) = (~10) = 11111111111111111111111111111101

(~3) = (~11) = 11111111111111111111111111111100

左移( << ) 

左移,顾名思义,就是将二进制中右侧的第一个 1 到左侧的部分向左移动几个单元,也可以说是在二进制后补几个 0

(1 << 1) = (01 << 1) = 10

(9 << 3) = (1001 << 3) = 1001000

(2 << 3) = (10 << 3) = 10000

右移( >> ) 

右移,与左移相反,右移若没有位数可以移动,就是 0

(1 >> 1) = (01 >> 1) = 00

(9 >> 3) = (1001 >> 3) = 01

(2 >> 3) = (10 >> 3) = 00

 三、二进制的实际应用(占位)

为了更充分的利用刚刚学到的二进制运算符原理,下面我们直接进入今天的主题,用二进制都能干啥?

二进制如果利用得当,可以很大程度的提高程序的运行效率。

以下内容,均是围绕“占位”展开。所谓占位,就是二进制位 0 表示没有被占位,1 表示被占位。比如 1 的二进制 00000000000000000000000000000001,表示右往左第 1 位被占位。

1、字符串系列

  • 字符串与数字进行转换。

对于阿斯克码值中,数字范围与种类相对较小的就是英文的大小写字符。大小写分别为 26 种,也就是 A ~ Z 与 a ~ z。那么对于一个字符串来说,如果纪录这串字符中都使用了哪些字母,我们可以使用二进制来解决。

字符串:abcdefg - 0123456 - 1111111 - 127

解释:对于小写字符,a 可以看为 0,z 可以看作 25,最高不超过 int 类型的二进制最大位的31位。因此,这里不考虑原字符串的字母排列顺序以及每个字母的使用频次,那么使用 int 来存储所有使用过的字符完全足够。也就是说,127 这个数字告诉我们,它的二进制 0 到 6 位都是 1,那么通过阿斯克码转换,可以知道这个字符串使用了 a ~ g 这 7 个字符

代码:

    public int charToNum(String str) {
        int mask = 0;
        for (int c : str.toCharArray()) {
            mask |= (1 << c - 'a');
        }
        return mask;
    }

  • 两个字符串比较是否存在使用了相同的字符

基于前面的字符串转数字的技巧,我们可以更进一步的使用这种方式,来比较两个英文字符串是否存在相同字符。

字符串:hello vs world

              hello -  00000000100100010010000 - 18576

              world - 10000100100100000001000 - 4343816

解释:直观可以看到有 2 处的 1 处在同位,意味着存在 2 种冲突的字符

代码:

    public boolean conflict(String s1, String s2) {
        int a = charToNum(s1), b = charToNum(s2);
        return (a & b) != 0;
    }

 这里我们使用按位与(&)可以巧妙的解决两个数字的相同二进制位是否存在都为 1 的情况。

  • 求得两个字符中存在几种冲突的字符

知道了是否冲突,如果更进一步需要知道存在几种冲突,我们可以考虑枚举二进制串中存在几个 1 即可。这里需要思考:使用哪些二进制运算符,以及如果一次性无法计算出某些结果,我们该如何组合使用二进制运算符,并设计一个程序来帮我们求得结果。单个二进制运算符的运算能力有限,因此当我们对二进制运算符掌握的足够牢靠,以及有相当够的经验,很多新的问题也可以尝试去使用二进制运算符解决。就好比当我们只知道加法时,对于 6 个 6 相加,我们需要一个一个的累加求得结果,而不是 6 乘以 6。

代码:

    public int countConflicts(String s1, String s2) {
        int binary = charToNum(s1) & charToNum(s2);
        int mask = 1, cnt = 0;
        // 31 是二进制 int 最大长度
        for (int i = 0; i < 31; ++i) {
            if ((binary & mask) != 0) { ++cnt; }
            mask <<= 1;
        }
        return cnt;
    }
  • 字符串与二进制的一些思考 

当然,int 类型的二进制位最大支持 31 位,更大的可以选择 long 类型的 63 位了。对于字符串转数字,只要计算的维度的取值范围不超过 63,我们都可以采取二进制的方式去进行程序设计。汉字不像英文字符仅有 26 个,但汉字的拼音确是由 26 个英文字母组成,如果你想象力足够丰富,是可以设计出意想不到的程序的。

2、数字系列

  • 数字使用二进制机制压缩

我们经常接触的可以使用二进制压缩的固定范围的数字类型有日期的月份天数

30天月份:4、6、9、11

压缩后:2640

二进制:101001010000

下面我们可以尝试一个案例。判断一个长度为 10 的日期字符串是否合法

代码:

    public static boolean dateValidation(String dateStr) {
        char[] cs = dateStr.toCharArray();
        int n = cs.length;
        int[] dateArr = new int[3];
        for (int i = 0, x = 0; i < 10; ++i) {
            if (i != 4 && i != 7) {
                dateArr[x] = dateArr[x] * 10 + (cs[i] - '0');
            }
            if (i == 4 || i == 7) { ++x; }
        }
        Date d = new Date();
        int year = d.getYear() + 1900;
        int month = d.getMonth() + 1;
        if (dateArr[0] > year || year - dateArr[0] > 2) {
            return false;
        }
        if (dateArr[1] > month || dateArr[1] < 1) {
            return false;
        }
        // {1 3 5 7 8 10 12} 31
        // {4 6 9 11} 30
        // {2} 平年 28 | 闰年 29
        boolean leap = (year % 100 == 0 && year % 400 == 0) || (year % 100 != 0 && year % 4 == 0);
        int maskOf30 = 2640; // maskOf31 = 5546
        if (dateArr[2] < 1 || dateArr[2] > 31 || ((1 << dateArr[1]) & maskOf30) != 0 && dateArr[2] > 30) {
            return false;
        }
        if (dateArr[1] == 2 && !leap && dateArr[2] > 28) {
            return false;
        }
        return dateArr[1] != 2 || !leap || dateArr[2] <= 29;
    }

代码片段中的 ((1 << dateArr[1]) & maskOf30) != 0,其实就是判断当前月份是否是 30 天的月份。如果你不这么写,也不用 API,采用 hash 表或 boolean 数组也是一个不错的办法,但没有做到极致。

3、类别数量超出 63 怎么压缩

        以下情况讨论只对常量作用最大,因为变量存在不确定性,很难抽象出具体的变化规律。

        我们知道,仅使用一个数字压缩对象的类型最多只能是 63,也就是对象的类型数量只能是小于等于 63 的。因为 Java 语言的所有数据类型中最大的二进制位数就是 63。 那大于 63 的怎么解决?比如全国有 293 个地级市,我们怎么压缩?

我们知道每个地级市都有对应的编号,这些编号都是唯一的,那么使得这个编号整体具有唯一性的可能只有该数字那么几个数位上的数字起着决定作用,举个栗子

a:   300012

b:   300015

c:   300017

 以上 3 个数字对编号唯一性起决定作用的只有从右往左的第一个数位。也有可能出现更多数位的情况。根据这几个影响唯一性数位,我们对每个地级市编号重新进行从 0 - n 编号。下面以 2 个数位为标准重建编号

    public int[][] remakeIndex(String[] codes, int x, int y) {
        int n = codes.length;
        int[][] indices = new int[9][9];
        int idx = 0;
        for (String c : codes) {
            indices[c.charAt(x) - '0'][c.charAt(y) - '0'] = idx++;
        }
        return indices;
    }

 重新建好编号后,我们取编号时,只需要根据原编号对应数位上的字符去获取新编号即可。

获取这些新的编号,我们要使用位移来压缩这些新的编号。假设这些新的编号最大为 80,显然超过了 63,此时我们创建一个数组,以 63 为基础,可以分为超过 63 和未超过 63 两个部分,那么这个数组的长度就是 2。

对于更大编号的压缩,可以根据这个原理去实现我们想要的程序。

    /**
     * @param compress 压缩数组
     * @param no 编号
     * @param base 以 63 为主
     */
    public void compressNo(long[] compress, int no, int base) {
        compress[no / base] |= 1L << (no % base);
    }

以上是我在算法学习中对二进制的一些技巧分享,欢迎指正,一起共勉学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 基于MATLAB的二进制数字调制与解调信号的仿真,是指利用MATLAB软件进行数字信号调制和解调的仿真实验。通过该实验,可以深入了解数字信号的调制和解调原理,掌握数字信号处理的基本方法和技巧,提高数字信号处理的实际应用能力。具体实验内容包括二进制振幅移键调制(BASK)、二进制频移键调制(FSK)、二进制相移键调制(PSK)等。通过仿真实验,可以模拟不同调制方式下的信号波形、频谱特性、误码率等参数,从而对数字信号调制和解调技术有更深入的理解和掌握。 ### 回答2: 二进制数字调制与解调信号是现代通信系统中常用的一种数字信号传输方式。MATLAB作为一种功能强大的数学软件,自然也被广泛应用于数字信号处理和通信系统仿真。本文将从二进制数字调制和解调的原理、MATLAB仿真的方法和实验步骤等方面进行详细介绍。 1. 二进制数字调制和解调的原理 在数字通信中,数据是以二进制比特序列的形式传输的,数字调制的目的就是将二进制序列转换成为调制信号,便于在传输中传递。同时,在接收端需要对调制信号进行解调,将其转换回原始的二进制数字信号。 常用的数字调制方式有ASK(Amplitude Shift Keying),FSK(Frequency Shift Keying),PSK(Phase Shift Keying)等。其中ASK调制方式是通过改变载波的振幅来编码信息信号。FSK调制方式是通过改变载波的频率来编码信息信号。PSK调制方式是通过改变载波的相位来编码信息信号。 在解调过程中,对于ASK调制方式,可以通过检测信号的振幅变化来得到二进制数字信号。对于FSK调制方式,可以通过检测信号的频率变化来得到二进制数字信号。对于PSK调制方式,可以通过检测信号相位的变化来得到二进制数字信号。 2. MATLAB仿真的方法 在MATLAB中,可以利用Simulink构建数字调制和解调系统的模型。具体步骤如下: (1) 在Simulink中选择合适的模块,如Sine Wave Generator、Pulse Generator和Adder等,构建ASK、FSK或PSK调制系统的模型。 (2) 根据调制方式的特点,设置好输入参数,如载波频率和振幅等。 (3) 在解调系统中,利用demod函数进行解调处理,得到二进制数字信号。 (4) 绘制输出信号的图像,并分析输出信号的性能指标,如误码率等。 3. 实验步骤 (1) 构建ASK调制系统模型,设置载波频率为1kHz,信号周期为10ms,调制深度为0.5。 (2) 设置Pulse Generator模块来产生二进制数字信号序列。输入序列为10101010。 (3) 合并二进制数字信号和ASK载波信号,得到ASK调制信号。 (4) 绘制ASK调制信号的幅度谱和波形图。 (5) 构建ASK解调系统模型。利用demod函数进行解调处理,得到原始的二进制数字信号。 (6) 绘制解调后的输出信号的波形图。 (7) 分析误码率等性能指标。 类似的,可以按照相应的步骤构建FSK和PSK调制系统的模型,并进行仿真实验。通过使用MATLAB进行仿真,可以直观地了解数字调制与解调的原理和性能,对提高数字信号处理技能和实际应用具有很大的帮助。 ### 回答3: 二进制数字调制与解调信号的仿真是一种用于模拟数字通信系统的技术,可以帮助研究人员更好地理解数字通信系统的原理和性能。matlab是一种功能强大的数学计算软件,可以用于模拟各种数字通信系统中的调制和解调过程。 在二进制数字调制中,数字信号被转换为一系列二进制位,然后用一种调制方法将其转换为一系列模拟信号。常见的调制方法包括振幅移位键控(ASK)、频移键控(FSK)、相移键控(PSK)和正交幅度调制(QAM)等。 在matlab中,可以使用信号处理工具箱中提供的函数来模拟这些调制方法。例如,使用“comm.ASKModulator”函数来模拟ASK调制,使用“comm.FSKModulator”函数来模拟FSK调制,使用“comm.PSKModulator”函数来模拟PSK调制,使用“comm.RectangularQAMModulator”函数来模拟QAM调制等。 在二进制数字解调中,接收到的信号需要经过一系列处理才能恢复出原始的数字信号。常见的解调方法包括包络检波、相干检波和最大似然序列检测等。 在matlab中,可以使用信号处理工具箱中提供的函数来模拟这些解调方法。例如,可以使用“comm.ASKDemodulator”函数来模拟ASK解调,使用“comm.FSKDemodulator”函数来模拟FSK解调,使用“comm.PSKDemodulator”函数来模拟PSK解调,使用“comm.RectangularQAMDemodulator”函数来模拟QAM解调等。 在进行二进制数字调制与解调信号的仿真时,通常需要考虑噪声的影响,因为在真实的通信系统中,接收到的信号往往会受到各种噪声的干扰。因此,可以使用matlab中提供的噪声函数来模拟这些干扰。 总之,基于matlab的二进制数字调制与解调信号的仿真是一种非常有价值的研究工具,可以帮助研究人员深入了解数字通信系统的原理和性能,从而为更好地设计和优化数字通信系统提供支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿码叔叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值