JS

关于矩阵的创建

今天做了一个 leetcode 的 59 螺旋矩阵题目,该题在最开始需要创建一个矩阵。GPT 给的方式如下

1
const matrix = Array.from({ length: n }, () => Array(n).fill(0));

这个代码片段使用了 ES6 的 Array.from()方法,该方法接受一个可迭代对象(通常是一个类数组对象)作为参数,并返回一个新数组。其中第二个参数是一个映射函数,用于将每个元素映射到新数组中的元素。

这个代码片段中,{ length: n }是一个对象,它表示创建一个长度为 n 的数组。然后,Array(n)是一个数组,它表示创建一个包含 n 个 0 的数组。最后,fill(0)是一个数组方法,它将数组中的每个元素设置为 0。

我过去创建矩阵通常使用如下方法

1
const matrix = new Array(n).fill(0).map(() => new Array(n).fill(0));

这个代码片段首先创建了一个长度为 n 的数组,每个元素都被设置为 0。然后,它使用 map()方法将每个元素转换为一个新的数组,并使用 fill(0)方法将每个数组中的元素设置为 0。

这里在使用第二种方法时,很容易出现共享引用的错误.

错误示例:

1
const result = new Array(n).fill(new Array(n).fill(0));

这里 fill 用的是同一个子数组引用,导致所有行指向同一个数组。
修改其中一个子数组会影响其他行:

1
2
3
4
5
6
7
8
result[0][0] = 1;
console.log(result);
// 输出:
// [
// [1, 0, 0],
// [1, 0, 0],
// [1, 0, 0]
// ]

总结
Array.from 推荐: 更安全且代码简洁,避免引用共享问题。
fill + map: 同样有效,适合对 map 方法较为熟悉的开发者。
切记,不能用 fill 填充同一个子数组引用,否则会产生意外的共享引用问题。

map 和 parseInt 函数的使用

1
2
const arr = ['1', '2', '3'].map(parseInt);
console.log(arr);

今天在地铁上刷视频看到一个很有意思的题目,随笔记录一下,和大家分享。
乍一看这个题目,感觉非常简单,对一个字符串数组进行map处理,处理函数是parseInt,输出结果应该就是[1, 2, 3].
然而当我们尝试运行这段代码会发现,真实的输出结果是[1, NaN, NaN].

那为什么会出现这么奇怪的现象呢?
这里有两个关键点,mapparseInt,我们需要深入且准确地弄清楚这两个函数的工作原理。才能解决这个问题。

map

Array 实例的 map() 方法创建一个新数组,其中填充了对调用数组中的每个元素调用提供的函数的结果。

使用语法如下

1
map(callbackFn[, thisArg])
  • callbackFn: 要为数组中的每个元素执行的函数。其返回值将作为单个元素添加到新数组中。使用以下参数调用该函数:
    • element: 数组中正在处理的当前元素
    • index: 数组中正在处理的当前元素的索引
    • array: 调用map()的数组
  • thisArg: 可选。执行回调时使用的 this 值。(指定callbackFn函数内使用 this)

parseInt

parseInt()函数解析字符串参数并返回指定基数(数学数字系统中的基数)的整数.]
其实就是返回指定进制的整数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
console.log(parseInt('123'));
// 123 (default base-10)
console.log(parseInt('123', 10));
// 123 (explicitly specify base-10)
console.log(parseInt(' 123 '));
// 123 (whitespace is ignored)
console.log(parseInt('077'));
// 77 (leading zeros are ignored)
console.log(parseInt('1.9'));
// 1 (decimal part is truncated)
console.log(parseInt('ff', 16));
// 255 (lower-case hexadecimal)
console.log(parseInt('0xFF', 16));
// 255 (upper-case hexadecimal with "0x" prefix)
console.log(parseInt('xyz'));
// NaN (input can't be converted to an integer)

使用语法如下:

1
parseInt(string[, radix])
  • string: 以整数开头的字符串.此参数的前导空格将被忽略

    • 如果第一个非空白字符不能转换为基数范围内的数字,直接返回NaN
    • 对于字符串的符号(+/-),parseInt可以理解,正常输出,不作处理.原来是正数就是正数,原来是负数就是负数.
    • 如果parseInt遇到不是指定基数中的数字字符,它将忽略该字符和所有后续字符,并返回到该点为止解析的整数值.例如,尽管1e3在技术上对整数进行编码(并且将被parseFloat()正确解析为整数1000),但parseInt("1e3",10)返回1,因为e不是以10为基数的有效数字.由于.也不是数字,因此返回值将始终为整数(也就是说,如果来解析小数,遇到小数点就会自动停止并返回,所以如果可以成功返回,返回值一定是整数)
  • radix: 可选。一个介于236之间的整数,表示字符串基数(进制).

    • 如果非零且超出[2,36]的范围,则函数将始终返回NaN

      1
      2
      3
      4
      5
      6
      7
      8
      9
      console.log(parseInt('213',1));
      console.log(parseInt('213',-1));
      console.log(parseInt('213',37));

      /**
      * NaN
      * NaN
      * NaN
      */
    • 如果未提供,或者值变为0,NaN,或Infinity(undefined被强制转换为NaN),则将根据字符串的值推断基数.注意,并不是默认为10

      • 如果输入的字符串(删除了前导空格和可能的+/-符号)以0x0X(0,后面跟小写或大写的x)开头,则基数假定为16,字符串的其余部分将被解析为16进制数.

      • 如果输入字符串以任何其他值开头,则基数为10(十进制)

        注意:其他前缀(如0b)在数字字面量中有效,但是在parseInt()中视为普通数字.parseInt()不会将以0字符开头的字符串视为八进制.parseInt()识别的唯一前缀是十六进制值的0x0X.如果缺少radix,其他所有内容都被解析为十进制值.可以使用Number()BigInt()来解析这些前缀.

    • 如果为16,则可以在可选符号字符(+/-)后选择性地为字符串添加0x0X前缀.

    • 对于大于10的基数,英文字母表的字母表示大于9的数字.例如,对于十六进制数,使用AF.这些字母不区分大小写

在非字符串上使用parseInt()

parseInt()在处理非字符串和高基数时可以产生有趣的结果;例如,36(使所有字母数字字符都是有效的数字进制,即0-9,和26个字母,总共36个)

1
2
3
4
5
console.log(parseInt(null,36));
console.log(parseInt(undefined,36));

// 1112745
// 86464843759093

我们应尽可能避免用parseInt处理非整数的字符串

所以回到我们的题目,为什么会有下面的输出呢?

1
2
3
const arr = ['1', '2', '3'].map(parseInt);
console.log(arr);
// [1, NaN, NaN]

这是因为我们把parseInt作为了map的回调函数.那么传递给parseInt的两个参数就是itemindex

所以实际的处理相当于如下所示

1
arr = [parseInt('1', 0),parseInt('2',1), parseInt('3',2)];

对于第一个元素,传递的radix为0,字符串'1'也不是以0x0X开头,所以这里采用十进制转换.结果为数字1

对于第二个元素,传递的radix为1,这是一个非法的进制,(因为合法的进制是2-36的一个整数),所以直接返回NaN

对于第三个元素,传递的radix是2,表示解析为二进制.当开始解析后,遇到第一个字符'3'就不是二进制中的一个合法字符(二进制只有01),所以直接停止,返回NaN.

CSS

包含块

最近学着学着发现,css才是最难的.CSS的英文全称是Cascading Style Sheets,翻译过来就是层叠样式表.
也就是说多种选择器之间会互相影响覆盖,每次莫名其妙地某个元素直接就消失了,所以,如果不能准确透彻地理解CSS样式的计算规则,就会经常出现莫名其妙的bug.
比如我们经常写的如下代码

1
2
3
4
.box {
width: 100%;
height: 70%;
}

请问我们这里的100%和70%是相对于谁的来计算的百分比呢?
很多同学可能会想当然的说,当然是父元素了,这还用说吗?
然而事实并非如此.

CSS里面有两大重点.
属性计算视觉格式化模型
对于视觉格式化模型,我们要清楚,以下内容

  • 元素的width,height,margin,padding的百分比都是相对于包含块的
  • 元素的left,right, top, bottom的百分比都是相对于包含块的

那么如何来确定包含块呢?
包含块如何确定,完全取决于元素的position属性的值

  1. 如果position属性是static(默认值),relative,或stick,包含块由最近的上级元素的内容盒的边缘形成,该元素可以是块容器(例如inline-block,block,list-itme element)或建立格式上下文(例如表格,网格,或块容器本身)
  2. 如果position属性是absolute,包含块由最近的上级元素的定位static的父元素的内边距盒的边缘构成.
  3. 如果position属性是fixed,包含块由视口(viewport)的边缘构成(在连续媒体的情况下),如果在分页媒体的情况下,则有页面区域的边缘构成.
  4. 如果position属性是absolutefixed,则包含块也可能由具有以下任何一项的最近上级元素的内边距盒边缘构成.
    • filter,backdrop-filter,transformperspective的值不是none
    • 包含layout,strict,paint,或content
    • container-type的值不是normal
    • will-change的值包含一个属性,其非初始值形成一个包含块(例如filtertransform)
    • content-visibility的值是auto

注意: 根元素<html>所在的包含块是一个被称为初始包含块的矩形.它的尺寸是视口(viewport)的尺寸.

我们来看下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.container {
background-color: pink;
}
.box {
width: 70%;
height: 80%;
background-color: skyblue;
}
</style>
</head>
<body>
<div class="container">
<div class="box"></div>
</div>
</body>
</html>

在这个例子中,box的高度是0.因为所有元素的属性默认值是static,所以我们依据第一条规则,box的百分比是相对于最近的上级元素的内容盒边缘形成的.即container.但是container没有明确设置height,所以为默认值auto由子元素撑开.所以这里无从参照,只能为0.

当我们给container设置一个高度后,即可成功显示

hexo 个人博客

关于图片引用问题

文章图片本地和远程不能同时显示问题

在本地 typora 中使用绝对路径插入的图片部署到网站中会无法正常显示,解决方式如下

  1. 在 Typora 的偏好设置中的图片设置中,设置如下图

    这样我们在本地只需要直接在 Typora 中粘贴图片,就会将图片复制到当前 markdown 文件同级的同名目录下。

  2. 在 hexo 项目的根目录的配置文件_config.yml中,把post_asset_folder属性设置为ture.

    这样当我们在 hexo 中使用命令hexo new xxxx的时候,就会同时创建一个同名目录,用来存放静态资源

  3. 安装hexo-asset-img插件

    1
    pnpm add hexo-asset-img

    安装完这个插件后,使用相对路径引入的图片在部署到站点后也可以显示.

文章的 top_img 和 cover 路径问题

文章中图片的问题解决了以后,在文章页面中 top_img 和 cover 图片路径的问题就出现了。

我们看下面这个例子,在经过上一步的设置后,已经在同级目录下有一个同名文件夹用来存放静态资源.这里我们在 OPPO23 秋文件夹中保存了一张top.png图片

然后在文章页的Front-Formatter中将封面图cover和顶部图top_img均设置为此图.

然后我们在本地使用hexo clean && hexo generate && hexo server命令运行博客

我们可以看到封面图cover可以成功显示,没有问题.

但是当我们点到文章页时,会发现top_img并没有成功显示

明明是一样的路径配置,但是一个正常,另一个不正常.很有可能是 hexo 以及我们安装的插件综合作用下,covertop_img的路径处理方式不同.

为了验证我们的猜想,我们使用浏览器开发者工具分别查看两图片对应的元素,并对获取图片请求进行抓包分析.

对于cover我们可以看到,它是一个嵌套在<a>标签里的一个<img>标签.

我们点击当前来源,跳转到图片资源位置,没有问题。

我们再来看一下top_img图片。

我们可以看到它并不是<img>,而是用 CSS 的background-image属性来设置的。这里难以看出问题,我们对网络请求进行抓包。

我们可以看到是一个 404 错误,说明找不到资源,就是路径出错了。观察请求头地址,我们不难发现,2024/12/08/OPPO23秋这一部分内容写了两次.说明 hexo 在处理文章的top_img时,很有可能本来就会加上前面的内容,我们的插件之类的又产生了干扰.具体原因还不得而知,不过问题已经可以解决了.

通过上述分析,我们可以推测,top_img不显示,很可能是因为我们使用了相对路径,框架在此处的路径处理出了一些问题,所以我们尝试改用绝对路径来引入.

在项目的source文件夹下新建一个img目录,里面复制我们的top.png

将文章中tom_img路径改为绝对路径/img/top.png(因为 hexo 会把 source 文件当作根目录来建站,所以建站后img文件夹相当于在根目录下,使用/img即可访问)

清理 hexo,重新生成,发现顶部图成功显示!

已知两个一维模式类别的类概率密度函数为

先验概率 $p(w_1)=0.6,p(w_2)=0.4$

(1)求 Bayes 最小错误率判决函数;

(2)求总错误概率 p;

(3)判断样本{x1=1.35,x2=1.45,x3=1.55,x4=1.65}各属于哪一类?

在字符检测中,假定类型$w_1$为字符,类型$w_2$为非字符,已知先验概率$P(w_1)=0.6,P(w_2)=0.4$,现在有两个待识样本$x_1,x_2$,其类概率密度分别为:

(1)试用贝叶斯最小误判概率准则判决两个样本各属于哪一个类型;

(2)如果$\lambda{12}$表示属于$w_1$类判决$w_2$造成的损失,正确判断的损失$\lambda{11}=\lambda{22}=0$,如果$\lambda{12}=4$,试用贝叶斯最小风险准则判决两个样本均属于第一类,误判损失$\lambda{21}$应该如何设计?请分析两种分类结果的异同及原因

随机变量x服从Erlang概率密度函数:

其中$u(x)$是单位阶跃函数

给定N个测量值$x_1,…,x_N$,计算$\theta$的最大似然估计

给定数据样本 X = {3, 4, 5, 5, 6, 12, 14, 14, 15, 16, 17,18},采用 Parzen 窗估计在 5 和 14 处的密度函数 p(x),窗宽 $h_N=4$。试分别计算出采用方窗 和正态窗( $\mu = 0, \sigma = 1 $)估计结果。

给定两类数据样本$w_1=[-3, -2, -1, -1, 0, 1, 1, 2, 2, 3], w_2=[1, 2, 3, 3, 4, 5, 5, 6, 6, 7]$,试用最小错误率贝叶斯决策判别观测样本 x=2 属于哪一类?此处两类 出现的概率$p(w_1)$和$p(w_2)$ 相等,采用方窗函数$\phi(u) = \begin{cases} 1 \quad \abs{u} \leq 1/2 \ 0 \quad 其他 \end{cases}$ 的 Parzen 窗估计密度方法,窗宽$h_N=5$估计类条件概率。