普通视图
Vue 团队“王炸”新作!又一打包工具发布!
Promise 引入全新 API!效率提升 300%!
科技爱好者周刊(第 345 期):HDMI 2.2 影音可能到头了
这里记录每周值得分享的科技内容,周五发布。
本杂志开源,欢迎投稿。另有《谁在招人》服务,发布程序员招聘信息。合作请邮件联系(yifeng.ruan@gmail.com)。
封面图
湖北宣恩县的竹筏夜游,两岸灯火辉煌。(via)
HDMI 2.2 影音可能到头了
今年一月,有一个消息,大家可能没关注,那就是 HDMI 接口发布了2.2版。
今天说说这件事,我冒着打脸的风险,猜测一下,这个标准可能到头了。
HDMI 接口是最常见的影音接口,每个人应该都接触过,电视机和显示器一般都用它连接信号源。
这个接口一直在升级,每次升级都会提高信号的传输速率。
- HDMI 1.0-1.2(2002):4.9Gbps
- HDMI 1.3-1.4(2006):10.2Gbps
- HDMI 2.0(2013):18Gbps
- HDMI 2.1(2017):48Gbps
- HDMI 2.2(2025):96Gbps
从上表可以看到,HDMI 接口的每次升级,信号的传输速率都会翻倍。这是为了应对现在的视频,越来越高清,数据量越来越大。
这一次,从2.1版升级到2.2版,速率从 48Gbps 加大到 96Gbps。
96Gbps 这个速率,大到吓人。
这是什么概念?最快的家庭网络现在是万兆网络(10Gbps),一般人根本不需要这么快,而 HDMI 2.2 比它还快10倍!
这是最快的影音接口,即使目前最高清晰度的视频,它可以同时传输多部,还有多余的带宽。
问题是,你用不到它。4K + 60帧 + HDR10 或杜比视界效果的视频,只需要 HDMI 2.0 就能实时传输。
8K + 60帧的视频,要用到 HDMI 2.1,而 HDMI 2.2 支持 12K + 60帧的视频。
可是,人们真的会在电视机上观看 12K 分辨率的视频吗?
大家可能听说过一个词"视网膜分辨率"(retina display),指的是人眼能够感知的最高分辨率。
超过这个分辨率,人眼就感知不到清晰度的提高,再提高分辨率就毫无意义。
2010年,乔布斯在发布 iPhone 4 时,提出了这个概念。他说,正常距离25-30厘米时,肉眼感觉不出 iPhone 4 屏幕的像素点。
视网膜分辨率跟两个因素有关:观看距离,屏幕大小。
屏幕越大,距离越近,所需要的分辨率越高;屏幕越小,距离越远,所需要的分辨率越低。
国外有人计算过,普通的家庭影院,坐在距离3.6米的地方,观看 12K 视频,屏幕可以大到550英寸,也能达到视网膜分辨率。
550英寸相当于宽12米,高6.8米,也就是广告牌大小!
换句话说,小于550英寸的屏幕,根本不需要 12K 分辨率。即使用到了,也是浪费,因为肉眼分辨不出这样的清晰度。
所以,普通家庭根本不需要 12K 分辨率。客厅里面,4K 电视机就能达到视网膜分辨率。
现阶段,视频效果的提升,主要已经不是通过分辨率了,而是通过色彩准确度、对比度(动态)等指标了。
音频也是如此。对于一般人的耳朵,CD 音质已经接近立体声的听觉极限,再提高也分辨不出来。它只需要 USB 2.0 (480Mbps) 就可以实时传输。
总之,影音标准在技术上还可以继续提高,但是人类的感官是有极限的。现有的技术已经达到感官的极限了,传输速率再提高,收益只会越来越少。
反正,我想象不出来,如果未来推出 HDMI 的2.3版或3.0版,传输速率将有多大,又是要满足怎样的场景?
科技动态
美国加州通过一项法律,从2026年7月开始,食品不得标识"保质期",而要改成"最佳食用日期"(Best Before)。
因为保质期往往被当作可食用的最后日期,一旦过期,食品就要下架,非常浪费。
实际上,保质期是指该日期之前,口味最佳。
据估计,保质期让美国33%的食物被浪费。这次的新法律,允许过期后食品继续留在货架上,每年可以让加州少浪费7万吨食品,大约节省3亿美元。
2、本周开幕的大阪2025世博会,建造了一个全世界最大的木结构建筑。
那是一个圆形的环,周长2公里,象征世博会参展国之间的团结。
3、升降人形机器人
日本 JR 铁路公司有很多高空电线,维修人员不足,而且高空作业也很麻烦危险。
他们就别出心裁,在升降机上装了一个人形机器人。
这个机器人做成了漫画《机动战士高达》的样子,还特别将摄像头做成了两只眼睛。
底下有操作员,将机器人升到高空,进行作业。所以,它也不是智能的,更像拉线木偶。
4、其他
(1)传统的微软死机"蓝屏"画面,可能改变。
Windows Insider 最新的更新包,将死机画面改成了黑屏(下图),类似于 Windows 更新画面。
(2)Notion 推出邮件服务,目前只能用于连接 Gmail 邮箱。
(3)网站 TLS 加密证书的有效期,将缩短为47天,2026年开始生效。
文章
1、我们正在进入大芯片时代(英文)
大芯片指的是 CPU、GPU、内存封装在一块芯片里面,典型代表是苹果的 M 系列芯片。
这种设计使得 CPU 和 GPU 共享内存,并且有极大的内存带宽,这就是为什么苹果电脑不需要 Nvidia 显卡,也能运行 AI 模型。
2、我放弃了笔记本,只用袖珍电脑 + AR眼镜(英文)
作者介绍他现在外出所携带的电脑设备:一个袖珍电脑,一个移动电源,一副 AR 眼镜和移动键鼠。
3、为什么大写字母的二维码小于小写字母(英文)
作者指出一个有趣的现象,如果把网址全部转成大写字母,再生成二维码,会小于小写网址的二维码(更稀疏)。
4、JS 性能测试的一个技巧(英文)
通常测试 JS 代码的性能时,会使用循环,但是循环本身有开销。作者介绍消除循环开销的一个技巧,就是分成两个循环,前一次循环执行一次代码,后一个循环执行两次代码,后者再减去前者。
5、让你的 RSS/Atom feed 更好看(中文)
一篇简单教程,介绍如何为 XML 格式的 RSS 文件加入样式。(@Spike-Leung 投稿)
6、text-wrap 的新设置(英文)
text-wrap 在 CSS 里面用来控制文本换行。它有两个新设置 text-wrap: pretty 和 text-wrap: balance。
工具
1、yabs.sh
一个用来测试服务器性能的 Bash 脚本,参考这篇教程。
一个跨平台的桌面应用,用来拦截本机的 HTTPS 和 Websocket 通信,进行查看和编辑。
一个跨平台的桌面软件,轻松打开和查看极大体积的文件(可以到 TB 级)。
一个 Chrome 插件,可根据网络请求实时生成 OpenAPI 规范的 API 描述。
一个开源的在线 3D 文件浏览器,支持多种文件格式。
6、Tirreno
开源的访问者分析系统,基于 PHP。
一个本地的声音克隆软件。(@xxx7xxxx 投稿)
8、Lexe
Node.js 应用打包成单个可执行文件,基于轻量级 JS 运行时 LLRT,打包体积小。(@Ray-D-Song 投稿)
9、Treex
开源的命令行工具,用来输出目录的树状结构,有多种输出格式和过滤选项。(@shiquda 投稿)
10、py-xiaozhi
一个使用 Python 实现的小智语音助手,支持语音交互、物联网设备控制、联网音乐播放等功能,无需专用硬件。(@huangjunsen0406 投稿)
AI 相关
1、PureChat
开源的 AI 聊天应用,可以接入 OpenAI、Ollama、DeepSeek 等大模型,基于 Vue3 和 Vite5 开发。(@Hyk260 投稿)
2、Vanna
一个开源的数据库分析 AI,用自然语言向数据库询问,它会自己生成 SQL,并给出运行结果。(@904192063 投稿)
将任意视频/音频,转化为各种风格的文章,可在本地部署。(@hanshuaikang 投稿)
资源
在 3D 地球仪上,显示某个航班的飞行路线。(@foru17 投稿)
2、欧洲夜班火车地图
这个网页的设计制作,让人过目难忘,可以借鉴。点击线路,会有详细信息的弹窗。
3、市场结构入门(Market Structure Primer)
一本英文电子书,向新手介绍美国证券市场的结构,如果想参与美股,可以读读。
生成随机数的波浪机
世界最大的 CDN 服务商 Cloudflare,在世界各地的办公室,各自部署了一个奇怪的技术装置。
这些装置可以生成无法预测的随机数,用于通信加密。
比如,旧金山总部的熔岩灯。
每时每刻,每盏灯的颜色和图案,都在变化,计算机处理后就是不一样的随机数。
伦敦办公室则是"单摆墙",每一盏都在摆动。
奥斯汀办公室是反光片天花板,任何气流都会让反光片转动。
周刊305期详细介绍过这些装置。
上个月,Cloudflare 里斯本办公室开张了,又新增了一个装置。
那是50个波浪机,组成了一面墙。
每个波浪机就是一个透明的长条容器,里面装着蓝色、绿色或者橙色的液体。
电力驱动波浪机的马达,容器每分钟翻转14次,每天超过20,000次。里面的液体随着容器翻转,形成波浪运动。
波浪机背后的反光板,会放大液体流动的光影效果,从而适合用来生成随机数。
文摘
1、韩式可爱
我很喜欢韩国首尔,过去四年已经去过了七次。
我注意到,首尔到处都是可爱的装饰物,令人应接不暇,根本无法逃避。所有东西都弄得像卡通人物。
人们都在大肆购买毛绒玩具。
小吃店的宣传招牌上,卡通章鱼甚至涂了口红,那是你最终会杀死然后吃掉的章鱼。
我觉得,韩国的年轻一代似乎通过拥抱可爱的东西,来应对猖獗的消费和物质主义,以及由此产生的空虚。
如果你把任何事物都塑造成可爱的形象,就能淡化生活的沉闷,避免那些严肃的问题,让消费主义没那么毫无意义了。
我完全接受把任何事物拟人化。但当它变成一种世界观时,就相当令人沮丧了。
在首尔,可爱文化几乎成了一种世界观。韩国年轻人用这种方式,应对没有灵魂的未来。
可爱文化虽然肤浅,但在消费社会却非常合理。更多可爱的毛绒玩具,产生更多的快乐,你购买越多,满足感越大。
一个幸福满足、运转良好的社会,需要有某种形式的信仰和美学,提升生活,超越物质。
但是,试图用可爱来做到这一点,只不过是一种自欺欺人。
言论
1、
科幻电视剧《黑镜》完全是技术悲观主义,只谈技术的风险,不谈技术变革让现在的生活比过去好。它传播对未来的恐惧,让人们惧怕技术变革。
但是,如果我们想要更美好的未来,就只有依靠技术。
-- 英国《卫报》
2、
我的编程风格是"面向痛苦的编程"。什么问题让我痛苦,我就去解决它,最痛苦的,最先解决,不痛苦的,就不去碰它。
这种编程可以确保你始终在做重要的事情,从而极大地降低了风险。
-- 《面向痛苦的编程》
3、
现在的社交媒体,不看重优质内容,而看重互动,看重通过货币化和 AI 吸引更多的流量。
这导致用户会被欺骗,会被诱导产生情绪波动,而看不到真正的创作和真实性。
4、
黑客的学习方法是,开始玩某件东西,然后转头去读文档,继续玩这件东西,再次阅读文档,接着玩下去,再次阅读文档......不断重复,直到搞懂。
-- 黑客手册
往年回顾
轮到硬件工程师吃香了(#298)
不要夸大 ChatGPT(#248)
美国制造是否可能(#198)
微增长时代(#148)
(完)
文档信息
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
- 发表日期: 2025年4月18日
从 0 到 1:开启 Chrome 插件开发的奇妙之旅
[Python3/Java/C++/Go/TypeScript] 一题一解:式子转换 + 哈希表(清晰题解)
方法一:式子转换 + 哈希表
根据题目描述,我们可以得知,对于任意的 $i \lt j$,如果 $j - i \neq \textit{nums}[j] - \textit{nums}[i]$,则 $(i, j)$ 是一个坏数对。
我们可以将式子转换为 $i - \textit{nums}[i] \neq j - \textit{nums}[j]$。这启发我们用哈希表 $cnt$ 来统计 $i - \textit{nums}[i]$ 的出现次数。
遍历数组,对于当前元素 $\textit{nums}[i]$,我们将 $i - cnt[i - \textit{nums}[i]]$ 加到答案中,然后将 $i - \textit{nums}[i]$ 的出现次数加 $1$。
最后,我们返回答案即可。
###python
class Solution:
def countBadPairs(self, nums: List[int]) -> int:
cnt = Counter()
ans = 0
for i, x in enumerate(nums):
ans += i - cnt[i - x]
cnt[i - x] += 1
return ans
###java
class Solution {
public long countBadPairs(int[] nums) {
Map<Integer, Integer> cnt = new HashMap<>();
long ans = 0;
for (int i = 0; i < nums.length; ++i) {
int x = i - nums[i];
ans += i - cnt.merge(x, 1, Integer::sum) + 1;
}
return ans;
}
}
###cpp
class Solution {
public:
long long countBadPairs(vector<int>& nums) {
unordered_map<int, int> cnt;
long long ans = 0;
for (int i = 0; i < nums.size(); ++i) {
int x = i - nums[i];
ans += i - cnt[x]++;
}
return ans;
}
};
###go
func countBadPairs(nums []int) (ans int64) {
cnt := map[int]int{}
for i, x := range nums {
x = i - x
ans += int64(i - cnt[x])
cnt[x]++
}
return
}
###ts
function countBadPairs(nums: number[]): number {
const cnt = new Map<number, number>();
let ans = 0;
for (let i = 0; i < nums.length; ++i) {
const x = i - nums[i];
ans += i - (cnt.get(x) ?? 0);
cnt.set(x, (cnt.get(x) ?? 0) + 1);
}
return ans;
}
###rust
use std::collections::HashMap;
impl Solution {
pub fn count_bad_pairs(nums: Vec<i32>) -> i64 {
let mut cnt: HashMap<i32, i64> = HashMap::new();
let mut ans: i64 = 0;
for (i, &num) in nums.iter().enumerate() {
let x = i as i32 - num;
let count = *cnt.get(&x).unwrap_or(&0);
ans += i as i64 - count;
*cnt.entry(x).or_insert(0) += 1;
}
ans
}
}
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{nums}$ 的长度。
有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈😄~
用markdown语法制作一个好看的网址导航页面(markdown-web-nav)
一分钟解决“3.无重复字符的最长字串问题”(最优解)
Vue 样式深入剖析:从基础到源码级理解(十)
webpack 检出图 第 二 节
webpack 检出图 第 一 节
Lodash源码阅读-getNative
Lodash源码阅读-apply
React-router v7 第五章(路由懒加载)
【Java】6142. 统计坏数对的数目 91.71% 附类似等式交换问题
解题思路
j - i != nums[j] - nums[i]
交换一下j - nums[j] != i - nums[i]
就解了
把向前找的问题
变成当前 + 哈希
问题。避免了向前找的动作。
类似的
[中等] 1814. 统计一个数组中好对子的数目【数组】【哈希表】【数学】【计数】 [哈希表] [1814. 统计一个数组中好对子的数目]
代码
###java
class Solution {
public long countBadPairs(int[] nums) {
long ans = 0;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int val = i - nums[i];
int same = map.getOrDefault(val, 0);
ans += i - same;
map.put(val, same + 1);
}
return ans;
}
}
Vue 配置模块深度剖析(十一)
每日一题-统计坏数对的数目🟡
给你一个下标从 0 开始的整数数组 nums
。如果 i < j
且 j - i != nums[j] - nums[i]
,那么我们称 (i, j)
是一个 坏数对 。
请你返回 nums
中 坏数对 的总数目。
示例 1:
输入:nums = [4,1,3,3] 输出:5 解释:数对 (0, 1) 是坏数对,因为 1 - 0 != 1 - 4 。 数对 (0, 2) 是坏数对,因为 2 - 0 != 3 - 4, 2 != -1 。 数对 (0, 3) 是坏数对,因为 3 - 0 != 3 - 4, 3 != -1 。 数对 (1, 2) 是坏数对,因为 2 - 1 != 3 - 1, 1 != 2 。 数对 (2, 3) 是坏数对,因为 3 - 2 != 3 - 3, 1 != 0 。 总共有 5 个坏数对,所以我们返回 5 。
示例 2:
输入:nums = [1,2,3,4,5] 输出:0 解释:没有坏数对。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
式子变形 + 枚举右维护左(Python/Java/C++/Go/JS/Rust)
前置题目:1512. 好数对的数目
题目要求
$$
j - i \ne \textit{nums}[j] - \textit{nums}[i]
$$
移项得
$$
\textit{nums}[i]-i \ne \textit{nums}[j]-j
$$
正难则反,用总数对个数 $\dfrac{n(n-1)}{2}$ 减去满足
$$
\textit{nums}[i]-i = \textit{nums}[j]-j
$$
的数对个数,即为答案。
设 $a[i] = \textit{nums}[i]-i$,就和 1512 题完全一样了。
为什么要先更新 $\textit{ans}$,再更新 $\textit{cnt}$?理由见 1512 题 我的题解。
class Solution:
def countBadPairs(self, nums: List[int]) -> int:
ans = comb(len(nums), 2)
cnt = defaultdict(int)
for i, x in enumerate(nums):
ans -= cnt[x - i]
cnt[x - i] += 1
return ans
class Solution {
public long countBadPairs(int[] nums) {
int n = nums.length;
long ans = (long) n * (n - 1) / 2;
Map<Integer, Integer> cnt = new HashMap<>();
for (int i = 0; i < n; i++) {
int x = nums[i] - i;
int c = cnt.getOrDefault(x, 0);
ans -= c;
cnt.put(x, c + 1);
}
return ans;
}
}
class Solution {
public long countBadPairs(int[] nums) {
int n = nums.length;
long ans = (long) n * (n - 1) / 2;
Map<Integer, Integer> cnt = new HashMap<>();
for (int i = 0; i < n; i++) {
ans -= cnt.merge(nums[i] - i, 1, Integer::sum) - 1;
}
return ans;
}
}
class Solution {
public:
long long countBadPairs(vector<int>& nums) {
int n = nums.size();
long long ans = 1LL * n * (n - 1) / 2;
unordered_map<int, int> cnt;
for (int i = 0; i < n; i++) {
ans -= cnt[nums[i] - i]++;
}
return ans;
}
};
func countBadPairs(nums []int) int64 {
n := len(nums)
ans := n * (n - 1) / 2
cnt := map[int]int{}
for i, x := range nums {
ans -= cnt[x-i]
cnt[x-i]++
}
return int64(ans)
}
var countBadPairs = function(nums) {
const n = nums.length;
let ans = n * (n - 1) / 2;
const cnt = new Map();
for (let i = 0; i < n; i++) {
const x = nums[i] - i;
const c = cnt.get(x) ?? 0;
ans -= c;
cnt.set(x, c + 1);
}
return ans;
};
use std::collections::HashMap;
impl Solution {
pub fn count_bad_pairs(nums: Vec<i32>) -> i64 {
let n = nums.len() as i64;
let mut ans = n * (n - 1) / 2;
let mut cnt = HashMap::new();
for (i, x) in nums.into_iter().enumerate() {
let e = cnt.entry(x - i as i32).or_insert(0);
ans -= *e as i64;
*e += 1;
}
ans
}
}
复杂度分析
- 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{nums}$ 的长度。
- 空间复杂度:$\mathcal{O}(n)$。
更多相似题目,见下面数据结构题单中的「§0.1 枚举右,维护左」。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/最短路/最小生成树/二分图/基环树/欧拉路径)
- 动态规划(入门/背包/状态机/划分/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府
Java36毫秒 计数+求和
解题思路
1.差值(数值-下标)相同的数可以组成好数对,按相同差值聚合计数。
2.设数组长度为n,数组总共数对个数为(n-1)+(n-2)+...+1累加之和,形成等差数列,公式求和(1+n-1)*(n-1)/2,得到总数对个数。相同差值的个数也可以视为数组长度,同理得到好数对个数。
3.坏数对=总数对-好数对。
代码
###java
class Solution {
public long countBadPairs(int[] nums) {
int n=nums.length;
long cnt=(long)(1.0d*(1+n-1)*(n-1)/2);
Map<Integer,Integer> map=new HashMap<>();
for(int i=0;i<n;i++){
int d=nums[i]-i;
map.put(d,map.getOrDefault(d,0)+1);
}
for(int count:map.values()){
cnt-=(long)(1.0d*(1+count-1)*(count-1)/2);
}
return cnt;
}
}