彻底弄懂base64的编码与解码原理

news2024/9/21 4:35:17

背景

base64的编码原理网上讲解较多,但解码原理讲解较少,并且没有对其中的内部实现原理进行剖析。想要彻底了解base64的编码与解码原理,请耐心看完此文,你一定会有所收获。

涉及算法与逻辑运算概念

在探究base64编码原理和解码原理的过程中,我们首先需要了解下面会用到的算法和逻辑运算的概念,这样才能真正的吃透base64的编码原理和解码原理,体会到其中算法的精妙,甚至是在思考的过程中得到意想不到的收获。不清楚base64编码表和ascII编码表的同学可直接前往文末查看。

短除法

短除法运算方法是先用一个除数除以能被它除尽的一个质数,以此类推,除到商是质数为止。
通过短除法,十进制数可以不断除以2得到多个余数。最后,将余数从下到上进行排列组合,得到二进制数,我们以字符n对应的ascII编码110为例。

    110 / 2  = 55...0
    55  / 2  = 27...1
    27  / 2  = 13...1
    13  / 2  = 6...1
    6   / 2  = 3...0
    3   / 2  = 1...1
    1   / 2  = 0...1

将余数从下到上进行排列组合,得到字符n对应的ascII编码110转二进制为1101110,因为一字节对应8位(bit), 所以需要向前补0补足8位,得到01101110。其余字符同理可得。

按权展开求和

按权展开求和, 8位二进制数从右到左,次数是0到7依次递增, 基数*底数次数,从左到右依次累加,相加结果为对应十进制数。我们以二进制数01101110转10进制为例:

(01101110)2 = 0 * 20 + 1 * 21 + 1 * 22 + 1 * 23 + 0 * 24 + 1 * 25 + 1 * 26 + 0 * 27

位概念

二进制数系统中,每个0或1就是一个位(bit,比特),也叫存储单元,位是数据存储的最小单位。其中8bit就称为一个字节(Byte)。

移位运算符

移位运算符在程序设计中,是位操作运算符的一种。移位运算符可以在二进制的基础上对数字进行平移。按照平移的方向和填充数字的规则分为三种:<<(左移)、>>(带符号右移)和>>>(无符号右移)。我们在base64的编码和解码过程中操作的又是正数,所以仅使用<<(左移)、>>(带符号右移)两种运算符。

  1. 左移运算:是将一个二进制位的操作数按指定移动的位数向左移动,移出位被丢弃,右边移出的空位一律补0。

  2. 右移运算:是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃,左边移出的空位一律补0,或者补符号位,这由不同的机器而定。在使用补码作为机器数的机器中,正数的符号位为0,负数的符号位为1。

我们用大白话来描述左移位,一共有8个座位,坐了8个人,在8个座位不动的情况下,现在我让这8个人往左挪2个座位,于是最左边的两个人站了起来,没有座位坐,而最右边空出来了两个座位。移位操作就相当于站起来的人出局,留出来的空位补0.

    // 左移
    01101000 << 2 -> 101000(左侧移出位被丢弃) -> 10100000(右侧空位一律补0)
    // 右移
    01101000 >> 2 -> 011010(右侧移出位被丢弃) -> 00011010(左侧空位一律补0)

与运算、或运算

与运算、或运算都是计算机中一种基本的逻辑运算方式。

  1. 与运算:符号表示为&。运算规则:两位同时为“1”,结果才为“1”,否则为0

  2. 或运算:符号表示为|。运算规则:两位只要有一位为“1”,结果就为“1”,否则为0

什么是base64编码

Base64编码是将字符串以每3个8比特(bit)的字节子序列拆分成4个6比特(bit)的字节(6比特有效字节,最左边两个永远为0,其实也是8比特的字节)子序列,再将得到的子序列查找Base64的编码索引表,得到对应的字符拼接成新的字符串的一种编码方式。

每3个8比特(bit)的字节子序列拆分成4个6比特(bit)的字节的拆分过程如下图所示:

图片

base64

为什么base64编码后的大小是原来的4/3倍

因为6和8的最大公倍数是24,所以3个8比特的字节刚好可以拆分成4个6比特的字节,38 = 64。计算机中,因为一个字节需要8个存储单元存储,所以我们要把6个比特往前面补两位0,补足8个比特。如下图所示:

图片

很明显,补足后所需的存储单元为32个,是原来所需的24个的4/3倍。现在大家明白为什么base64编码后的大小是原来的4/3倍了吧。

为什么命名为base64呢?

因为6位(bit)的二进制数有2的6次方个,也就是二进制数(00000000-00111111)之间的代表0-63的64个二进制数。

不是说一个字节是用8位二进制表示的吗,为什么不是2的8次方?

因为我们得到的8位二进制数的前两位永远是0,真正的有效位只有6位,所以我们所能够得到的二进制数只有2的6次方个。

Base64字符是哪64个?

Base64的编码索引表,字符选用了"A-Z、a-z、0-9、+、/" 64个可打印字符来代表(00000000-00111111)这64个二进制数。即

    let base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

编码原理

我们不妨自己先思考一下,要把3个字节拆分成4个字节可以怎么做?你的实现思路和我的实现思路有哪个不同,我们之间又会碰出怎样的火花?

流程图

图片

流程图

思路

分析映射关系:abc -> xyzi。我们从高位到低位添加索引来分析这个过程

  • x: (前面补两个0)a的前六位 => 00a7a6a5a4a3a2

  • y: (前面补两个0)a的后两位 + b的前四位 => 00a1a0b7b6b5b4

  • z: (前面补两个0)b的后四位 + c的前两位 => 00b3b2b1b0c7c6

  • i: (前面补两个0)c的后六位 => 00c5c4c3c2c1c0通过上述的映射关系,我们很容易得到下面的实现思路:

  1. 将字符对应的ascII编码转为8位二进制数

  2. 将每三个8位二进制数进行以下操作

    • 将第一个数右移位2位,得到第一个6位有效位二进制数

    • 将第一个数 & 0x3之后左移位4位,得到第二个6位有效位二进制数的第一个和第二个有效位,将第二个数 & 0xf0之后右移位4位,得到第二个6位有效位二进制数的后四位有效位,两者取且得到第二个6位有效位二进制

    • 将第二个数 & 0xf之后左移位2位,得到第三个6位有效位二进制数的前四位有效位,将第三个数 & 0xC0之后右移位6位,得到第三个6位有效位二进制数的后两位有效位,两者取且得到第三个6位有效位二进制

    • 将第三个数 & 0x3f,得到第四个6位有效位二进制数

  3. 将获得的6位有效位二进制数转十进制,查找对应base64字符

我们以hao字符串为例,观察base64编码的过程,我们将上面转换通过代码逻辑分析实现吧。

代码实现

// 输入字符串
let str = 'hao'
// base64字符串
let base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
// 定义输入、输出字节的二进制数
let char1, char2, char3, out1, out2, out3, out4, out
// 将字符对应的ascII编码转为8位二进制数
char1 = str.charCodeAt(0) & 0xff // 104  01101000
char2 = str.charCodeAt(1) & 0xff // 97  01100001
char3 = str.charCodeAt(2) & 0xff // 111  01101111
// 输出6位有效字节二进制数
6out1 = char1 >> 2 // 26  011010
out2 = (char1 & 0x3) << 4 | (char2 & 0xf0) >> 4 // 6  000110
out3 = (char2 & 0xf) << 2 | (char3 & 0xc0) >> 6 // 5  000101
out4 = char3 & 0x3f // 47 101111

out = base64EncodeChars[out1] + base64EncodeChars[out2] + base64EncodeChars[out3] + base64EncodeChars[out4] // aGFv

算法剖析

  1. out1: char1 >> 2
    01101000 -> 00011010
    
  2. out2 = (char1 & 0x3) << 4 | (char2 & 0xf0) >> 4
    // 且运算
    01101000        01100001
    00000011        11110000
    --------        --------
    00000000        01100000
    
    // 移位运算后得
    00000000        00000110
    
    // 或运算
    00000000
    00000110
    --------
    00000110
    

第三个字符第四个字符同理

整理上述代码,扩展至多字符字符串

// 输入字符串
let str = 'haohaohao'
// base64字符串
let base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

// 获取字符串长度
let len = str.length
// 当前字符索引
let index = 0
// 输出字符串
let out = ''
while(index < len) {
    // 定义输入、输出字节的二进制数
    let char1, char2, char3, out1, out2, out3, out4
    // 将字符对应的ascII编码转为8位二进制数
    char1 = str.charCodeAt(index++) & 0xff // 104  01101000
    char2 = str.charCodeAt(index++) & 0xff // 97  01100001
    char3 = str.charCodeAt(index++) & 0xff // 111  01101111
    // 输出6位有效字节二进制数
    out1 = char1 >> 2 // 26  011010
    out2 = (char1 & 0x3) << 4 | (char2 & 0xf0) >> 4 // 6  000110
    out3 = (char2 & 0xf) << 2 | (char3 & 0xc0) >> 6 // 5  000101
    out4 = char3 & 0x3f // 47 101111

    out = out + base64EncodeChars[out1] + base64EncodeChars[out2] + base64EncodeChars[out3] + base64EncodeChars[out4] // aGFv
}

原字符串长度不是3的整倍数的情况,需要特殊处理

    ...
    char1 = str.charCodeAt(index++) & 0xff // 104  01101000
    if (index == len) {
        out2 = (char1 & 0x3) << 4
        out = out + base64EncodeChars[out1] + base64EncodeChars[out2] + '=='
        return out
    }
    char2 = str.charCodeAt(index++) & 0xff // 97  01100001
    if (index == len) {
        out1 = char1 >> 2 // 26  011010
        out2 = (char1 & 0x3) << 4 | (char2 & 0xf0) >> 4 // 6  000110
        out3 = (char2 & 0xf) << 2
        out = out + base64EncodeChars[out1] + base64EncodeChars[out2] + base64EncodeChars[out3] + '='
        return out
    }
    ...

全部代码

function base64Encode(str) {
    // base64字符串
    let base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

    // 获取字符串长度
    let len = str.length
    // 当前字符索引
    let index = 0
    // 输出字符串
    let out = ''
    while(index < len) {
        // 定义输入、输出字节的二进制数
        let char1, char2, char3, out1, out2, out3, out4
        // 将字符对应的ascII编码转为8位二进制数
        char1 = str.charCodeAt(index++) & 0xff
        out1 = char1 >> 2
        if (index == len) {
            out2 = (char1 & 0x3) << 4
            out = out + base64EncodeChars[out1] + base64EncodeChars[out2] + '=='
            return out
        }
        char2 = str.charCodeAt(index++) & 0xff
        out2 = (char1 & 0x3) << 4 | (char2 & 0xf0) >> 4 
        if (index == len) {
            out3 = (char2 & 0xf) << 2
            out = out + base64EncodeChars[out1] + base64EncodeChars[out2] + base64EncodeChars[out3] + '='
            return out
        }
        char3 = str.charCodeAt(index++) & 0xff
        // 输出6位有效字节二进制数
        out3 = (char2 & 0xf) << 2 | (char3 & 0xc0) >> 6
        out4 = char3 & 0x3f

        out = out + base64EncodeChars[out1] + base64EncodeChars[out2] + base64EncodeChars[out3] + base64EncodeChars[out4]
    }
    return out
}
base64Encode('haohao') // aGFvaGFv
base64Encode('haoha') // aGFvaGE=
base64Encode('haoh') // aGFvaA==

解码原理

逆向推导,由每4个6位有效位的二进制数合并成3个8位二进制数,根据ascII编码映射到对应字符后拼接字符串

思路

分析映射关系 xyzi -> abc

  • a: x后六位 + y第三、四位 => x5x4x3x2x1x0y5y4

  • b: y后四位 + z第三、四、五、六位 => y3y2y1y0z5z4z3z2

  • c: z后两位 + i后六位 => z1z0i5i4i3i2i1i0

  1. 将字符对应的base64字符集的索引转为6位有效位二进制数

  2. 将每四个6位有效位二进制数进行以下操作

    1. 第一个二进制数左移位2位,得到新二进制数的前6位,第二个二进制数 & 0x30之后右移位4位,或运算后得到第一个新二进制数

    2. 第二个二进制数 & 0xf之后左移位4位,第三个二进制数 & 0x3c之后右移位2位,或运算后得到第二个新二进制数

    3. 第二个二进制数 & 0x3之后左移位6位,与第四个二进制数或运算后得到第二个新二进制数

  3. 根据ascII编码映射到对应字符后拼接字符串

代码实现

// base64字符串
let str = 'aGFv'
// base64字符集
let base64CharsArr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('')
// 获取索引值
let char1 = base64CharsArr.findIndex(char => char==str[0]) & 0xff // 26  011010
let char2 = base64CharsArr.findIndex(char => char==str[1]) & 0xff // 6  000110
let char3 = base64CharsArr.findIndex(char => char==str[2]) & 0xff // 5  000101
let char4 = base64CharsArr.findIndex(char => char==str[3]) & 0xff // 47  101111
let out1, out2, out3, out
// 位运算
out1 = char1 << 2 | (char2 & 0x30) >> 4
out2 = (char2 & 0xf) << 4 | (char3 & 0x3c) >> 2
out3 = (char3 & 0x3) << 6 | char4
console.log(out1, out2, out3)
out = String.fromCharCode(out1) + String.fromCharCode(out2) + String.fromCharCode(out3)

遇到有用'='补过位的情况时

function base64decode(str) {
    // base64字符集
    let base64CharsArr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('')
    let char1 = base64CharsArr.findIndex(char => char==str[0])
    let char2 = base64CharsArr.findIndex(char => char==str[1])
    let out1, out2, out3, out
    if (char1 == -1 || char2 == -1) return out
    char1 = char1 & 0xff
    char2 = char2 & 0xff
    let char3 = base64CharsArr.findIndex(char => char==str[2])
    // 第三位不在base64对照表中时,只拼接第一个字符串
    if (char3 == -1) {
        out1 = char1 << 2 | (char2 & 0x30) >> 4
        out = String.fromCharCode(out1)
        return out
    }
    let char4 = base64CharsArr.findIndex(char => char==str[3])
    // 第三位不在base64对照表中时,只拼接第一个和第二个字符串
    if (char4 == -1) {
        out1 = char1 << 2 | (char2 & 0x30) >> 4
        out2 = (char2 & 0xf) << 4 | (char3 & 0x3c) >> 2
        out = String.fromCharCode(out1) + String.fromCharCode(out2)
        return out
    }
    // 位运算
    out1 = char1 << 2 | (char2 & 0x30) >> 4
    out2 = (char2 & 0xf) << 4 | (char3 & 0x3c) >> 2
    out3 = (char3 & 0x3) << 6 | char4
    console.log(out1, out2, out3)
    out = String.fromCharCode(out1) + String.fromCharCode(out2) + String.fromCharCode(out3)
    return out
}

解码整个字符串,整理代码后

function base64decode(str) {
    // base64字符集
    let base64CharsArr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('')
    let i = 0
    let len = str.length
    let out = ''
    while(i < len) {
        let char1 = base64CharsArr.findIndex(char => char==str[i])
        i++
        let char2 = base64CharsArr.findIndex(char => char==str[i])
        i++
        let out1, out2, out3
        if (char1 == -1 || char2 == -1) return out
        char1 = char1 & 0xff
        char2 = char2 & 0xff
        let char3 = base64CharsArr.findIndex(char => char==str[i])
        i++
        // 第三位不在base64对照表中时,只拼接第一个字符串
        out1 = char1 << 2 | (char2 & 0x30) >> 4
        if (char3 == -1) {
            out = out + String.fromCharCode(out1)
            return out
        }
        let char4 = base64CharsArr.findIndex(char => char==str[i])
        i++
        // 第三位不在base64对照表中时,只拼接第一个和第二个字符串
        out2 = (char2 & 0xf) << 4 | (char3 & 0x3c) >> 2
        if (char4 == -1) {
            out = out + String.fromCharCode(out1) + String.fromCharCode(out2)
            return out
        }
        // 位运算
        out3 = (char3 & 0x3) << 6 | char4
        console.log(out1, out2, out3)
        out = out + String.fromCharCode(out1) + String.fromCharCode(out2) + String.fromCharCode(out3)
    }
    return out
}
base64decode('aGFvaGFv') // haohao
base64decode('aGFvaGE=') // haoha
base64decode('aGFvaA==') // haoh

上述解码核心是字符与base64字符集索引的映射,网上看到过使用AccII编码索引映射base64字符索引的方法

let base64DecodeChars = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1]
// 
let char1 = 'hao'.charCodeAt(0) // h -> 104
base64DecodeChars[char1] // 33 -> base64编码表中的h

由此可见,base64DecodeChars对照accII编码表的索引存放的是base64编码表的对应字符的索引。

总结

说起Base64编码可能有些奇怪,因为大多数的编码都是由字符转化成二进制的过程,而从二进制转成字符的过程称为解码。而Base64的概念就恰好反了,由二进制转到字符称为编码,由字符到二进制称为解码。Base64 是一种数据编码方式,可做简单加密使用,我们可以改变base64编码映射顺序来形成自己独特的加密算法进行加密解密。

编码表

图片

图片

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

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

相关文章

macrodata数据集在Python统计建模和计量经济学中的应用

目录 一、数据介绍二、应用三、statsmodels 统计模块四、使用 statsmodels 统计模块分析 macrodata.csv 数据集参考 一、数据介绍 macrodata.csv是一个示例数据集&#xff0c;通常用于统计分析和计量经济学中的教育和训练目的。这个数据集通常包括以下列&#xff1a; year&am…

17.3 实现无管道反向CMD

WSASocket无管道反向CMD&#xff0c;与无管道正向CMD相反&#xff0c;这种方式是在远程主机上创建一个TCP套接字&#xff0c;并绑定到一个本地地址和端口上。然后在本地主机上&#xff0c;使用WSASocket函数连接到远程主机的套接字&#xff0c;并将标准输入、输出和错误输出重定…

深入 Meven:构建杰出的软件项目的完美工具

掌握 Meven&#xff1a;构建更强大、更智能的应用程序的秘诀 Maven1.1 初识Maven1.1.1 什么是Maven1.1.2 Maven的作用 02. Maven概述2.1 Maven介绍2.2 Maven模型2.3 Maven仓库2.4 Maven安装2.4.1 下载2.4.2 安装步骤 03. IDEA集成Maven3.1 配置Maven环境3.1.1 当前工程设置3.1.…

JDBC相关记录

JDBC&#xff1a;Java DadaBase Connectivity 即Java语言连接数据库。 本质&#xff1a;JDBC是SUN公司制定的一套接口&#xff08;interface&#xff09;。 作用&#xff1a;不同的数据库有自己独特设计原理&#xff0c;JDBC的可以让Java程序员关注业务本身&#xff0c;而不需要…

Programming abstractions in C阅读笔记:p181-p183

《Programming Abstractions In C》学习第61天&#xff0c;p181-p183总结。 一、技术总结 1.linear search algorithm 2.lexicographic order(字典顺序) 3.binary search algorithm(二分查找算法) /** 1.二分查找也应用了递归的思想。* 2.这里的代码只是demo*/ #include &…

17.2 实现无管道正向CMD

WSASocket 无管道正向CMD&#xff0c;使用WSASocket函数创建一个TCP套接字&#xff0c;并绑定到一个本地地址和端口上。然后使用CreateProcess函数创建一个新的CMD进程&#xff0c;并将标准输入、输出和错误输出重定向到套接字的句柄上。这样&#xff0c;客户端可以通过网络连接…

应用开发平台集成工作流系列之16——办理意见设计与实现

背景 流程任务流转过程中&#xff0c;各环节的处理&#xff0c;会填写处理意见。 Camunda自带了相关的功能&#xff0c;但功能过于简陋&#xff0c;问题较多&#xff0c;今天来说说这一块。 自带功能的问题 如使用Camunda官方自身的办理意见相关功能&#xff0c;会遇到两个问…

【C++进阶(九)】C++多态深度剖析

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 多态 1. 前言2. 多态的概念以及定义3. 多态的实…

Generator异步解决方案详解

一&#xff1a;三种常见的异步解决方案 Promise&#xff1a;链式编程async&#xff1a;使用 async 去修饰函数&#xff0c;然后使用 await 去等待成功Generator&#xff1a;使用 * 修饰函数&#xff0c;然后使用 yield 去等待成功 通俗来讲&#xff0c;Generator 类似于 Promi…

【Java 进阶篇】Java XML组成部分:理解XML的结构

XML&#xff08;可扩展标记语言&#xff09;是一种常用于存储和交换数据的标记语言。了解XML的结构和组成部分对于有效处理XML数据至关重要。在本篇博客中&#xff0c;我们将深入探讨XML的组成部分&#xff0c;以及如何使用Java来处理和操作XML数据。 什么是XML&#xff1f; …

预测宝可梦武力值、分类宝可梦

regression case 股票预测 无人车看到的各种sensor 影像镜头看到马路上的东西作为输入&#xff0c;输出就是方向盘角度等等的操纵策略 scalar 标量 这个是热力图&#xff0c;相当于你的XYZ但是Z用颜色表示了 closed-form solution 闭合解 learning rate事先定好的数值 在lin…

Vue中的v-for指令是用来做什么的?

在Vue中,v-for是一个用于渲染列表或集合的指令。它通过迭代数据源中的每个元素,生成对应的DOM节点或组件实例,并将它们渲染到页面上。 v-for指令的基本语法如下: <template><div><ul><li v-for="item in items" :key="item.id"…

基于ssm流浪动物领养救助系统

摘要 基于SSM&#xff08;Spring SpringMVC MyBatis&#xff09;的流浪动物领养救助系统是一个用于管理和帮助流浪动物领养、救助的信息化平台。该系统旨在提供一种便捷、高效的方式&#xff0c;以协调和改善流浪动物的生活&#xff0c;并促进社会各界的参与和支持。以下是该…

重生奇迹mu“荣誉之城”勇者大陆

曾经&#xff0c;不少重生奇迹mu玩家讨论最经典的新人出生地&#xff0c;有的说是仙踪林&#xff0c;有的则是说勇者大陆&#xff0c;最后在重生奇迹mu网站上面&#xff0c;以投票的方式最终得出一个答案&#xff0c;那就是勇者大陆&#xff0c;游戏里面当之无愧的荣誉之城&…

c语言程序设计——题目:将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5。

题目&#xff1a;将一个正整数分解质因数。例如&#xff1a;输入90,打印出902*3*3*5。 程序分析&#xff1a;对n进行分解质因数&#xff0c;应先找到一个最小的质数k&#xff0c;然后按下述步骤完成 (1)如果这个质数恰等于&#xff08;小于的时候&#xff0c;继续执行循环&…

C#,数值计算——分类与推理Phylo_nj的计算方法与源程序

1 文本格式 using System; using System.Collections.Generic; namespace Legalsoft.Truffer { public class Phylo_nj : Phylagglom { public double[] u; public override void premin(double[,] d, int[] nextp) { i…

spring6-国际化:i18n | 数据校验:Validation

文章目录 1、国际化&#xff1a;i18n1.1、i18n概述1.2、Java国际化1.3、Spring6国际化1.3.1、MessageSource接口1.3.2、使用Spring6国际化 2、数据校验&#xff1a;Validation2.1、Spring Validation概述2.2、实验一&#xff1a;通过Validator接口实现2.3、实验二&#xff1a;B…

AI只需26秒,就可以设计一款会走路的机器人

由西北大学、麻省理工学院和佛蒙特大学组成的一支科研团队首次开发出一种可以完全自行设计机器人的 AI 算法。 这一 AI 算法不仅运行速度快&#xff0c;还可在个人计算机上运行&#xff0c;并从头开始设计全新的结构。只需告诉AI“我们想要一个可穿越陆地的机器人”&#xff0c…

【Java 进阶篇】Java XML快速入门:理解、解析和生成XML

XML&#xff08;可扩展标记语言&#xff09;是一种常用于存储和交换数据的标记语言&#xff0c;而Java是一种强大的编程语言&#xff0c;它具有处理XML的能力。在本篇博客中&#xff0c;我们将探讨XML的基础知识&#xff0c;学习如何在Java中解析和生成XML文档&#xff0c;以及…

一次OOM故障分析

一、前言 昨天门店POS系统发生了一次因为OOM引起的Down机事件&#xff0c;本文我们就来讲一下故障排查和解决问题过程。 二、故障发生 吃完中饭正在休息&#xff0c;业务方说POS的后台管理系统进行库存盘点出错&#xff0c;截图过来的报错信息里有&#xff1a;连接POS前台系…