普通视图

发现新文章,点击刷新页面。
今天 — 2025年4月18日技术

科技爱好者周刊(第 345 期):HDMI 2.2 影音可能到头了

作者 阮一峰
2025年4月18日 08:07

这里记录每周值得分享的科技内容,周五发布。

本杂志开源,欢迎投稿。另有《谁在招人》服务,发布程序员招聘信息。合作请邮件联系(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版,传输速率将有多大,又是要满足怎样的场景?

科技动态

1、加州禁止"保质期"

美国加州通过一项法律,从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 脚本,参考这篇教程

2、Pākiki Proxy

一个跨平台的桌面应用,用来拦截本机的 HTTPS 和 Websocket 通信,进行查看和编辑。

3、Giant Log Viewer

一个跨平台的桌面软件,轻松打开和查看极大体积的文件(可以到 TB 级)。

4、OpenAPI DevTools

一个 Chrome 插件,可根据网络请求实时生成 OpenAPI 规范的 API 描述。

5、Online 3D Viewer

一个开源的在线 3D 文件浏览器,支持多种文件格式。

6、Tirreno

开源的访问者分析系统,基于 PHP。

7、EaseVoice Trainer

一个本地的声音克隆软件。(@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 投稿)

3、AI 视频图文创作助手

将任意视频/音频,转化为各种风格的文章,可在本地部署。(@hanshuaikang 投稿)

资源

1、Flight Track in 3D Earth

在 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日

[Python3/Java/C++/Go/TypeScript] 一题一解:式子转换 + 哈希表(清晰题解)

作者 lcbin
2025年4月18日 06:28

方法一:式子转换 + 哈希表

根据题目描述,我们可以得知,对于任意的 $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}$ 的长度。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈😄~

【Java】6142. 统计坏数对的数目 91.71% 附类似等式交换问题

作者 wa-pian-d
2022年8月8日 08:17

解题思路

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;
}
}

每日一题-统计坏数对的数目🟡

2025年4月18日 00:00

给你一个下标从 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)

作者 endlesscheng
2022年8月7日 07:57

前置题目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 枚举右,维护左」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/最短路/最小生成树/二分图/基环树/欧拉路径)
  7. 动态规划(入门/背包/状态机/划分/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

Java36毫秒 计数+求和

作者 zhy-
2022年8月7日 00:31

解题思路

1.差值(数值-下标)相同的数可以组成好数对,按相同差值聚合计数。
2.设数组长度为n,数组总共数对个数为(n-1)+(n-2)+...+1累加之和,形成等差数列,公式求和(1+n-1)*(n-1)/2,得到总数对个数。相同差值的个数也可以视为数组长度,同理得到好数对个数。
3.坏数对=总数对-好数对。

统计坏数对的数目.png

代码

###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;
    }
}
❌
❌