$4 blog
GNU/Linux, Debian/Ubuntu, Free Software/Open Source Software, and Programming.
2026年1月6日 星期二
深入解析 grep 的「Broken pipe」錯誤:隨機出現的原因與優雅解法
2026年1月5日 星期一
自幹了一個 Agent Skills - Launchpad Skill
放在 https://github.com/fourdollars/lp-api/ 專案底下的 launchpad 目錄底下,當然就是用 lp-api 這個小工具來串接。
我自己有在 OpenCode, GitHub Copilot CLI, Gemini CLI 上面成功掛載起來使用。
基本上就是把 AI Agent 接上 Launchpad,用自然語言去叫 AI 做自己想要做的事情。
以下是幾張 OpenCode 上使用的示範。
有興趣的人可以去看看 Agent Skills 了解一下。
2025年11月28日 星期五
Kadane's algorithm
昨天練習的題目是 3381. Maximum Subarray Sum With Length Divisible by K,題目的最佳解是利用 Kadane's algorithm,我覺得比較容易理解的方式是先計算前面 k-1 個 kSum 之後,再往後面用 Kadane's algorithm 來解。
如果是寫 C++ 是這樣:
class Solution {
public:
long long maxSubarraySum(vector< int>& nums, int k) {
const int n = nums.size();
vector< long long> kSum(k, 0);
long long maxSum = numeric_limits< long long>::min(), prefixSum = 0;
for (int i = 0; i < k - 1; ++i) {
prefixSum += nums[i];
kSum[i] = prefixSum;
}
for (int i = k - 1; i < n; ++i) {
prefixSum += nums[i];
int r = i % k;
maxSum = max(maxSum, prefixSum - kSum[r]);
kSum[r] = min(kSum[r], prefixSum);
}
return maxSum;
}
};
寫 Python3 是這樣:
class Solution:
def maxSubarraySum(self, nums: List[int], k: int) -> int:
n = len(nums)
kSum = [0] * k
prefixSum = 0
maxSum = float("-inf")
for i in range(k - 1):
prefixSum += nums[i]
kSum[i] = prefixSum
for i in range(k - 1, n):
prefixSum += nums[i]
r = i % k
maxSum = max(maxSum, prefixSum - kSum[r])
kSum[r] = min(kSum[r], prefixSum)
return maxSum
寫 Go 是這樣,這邊要注意即便是使用 math.MinInt64 還是要用 int64() 明確轉型,不然預設會用 int 然後就會跟後面的 int64 衝突。
func maxSubarraySum(nums []int, k int) int64 {
n := len(nums)
kSum := make([]int64, k)
prefixSum, maxSum := int64(0), int64(math.MinInt64)
for i := range n {
prefixSum += int64(nums[i])
if i < k-1 {
kSum[i] = prefixSum
continue
}
r := i % k
maxSum = max(maxSum, prefixSum-kSum[r])
kSum[r] = min(kSum[r], prefixSum)
}
return maxSum
}
寫 Rust 是這樣,沒什麼特別要注意的地方,反正寫錯時編譯器會提醒你該如何修正。 :-P
impl Solution {
pub fn max_subarray_sum(nums: Vec< i32>, k: i32) -> i64 {
let k = k as usize;
let n = nums.len();
let mut kSum = vec![0i64; k];
let mut prefixSum = 0i64;
let mut maxSum = i64::MIN;
for i in 0..(k-1) {
prefixSum += nums[i] as i64;
kSum[i] = prefixSum;
}
for i in (k-1)..n {
prefixSum += nums[i] as i64;
let r = i % k;
maxSum = maxSum.max(prefixSum - kSum[r]);
kSum[r] = kSum[r].min(prefixSum);
}
maxSum
}
}
不曉得為何,寫 Rust 有最愉快的感覺。 :-P
2025年11月17日 星期一
Binary Search
今日練習的題目是 300. Longest Increasing Subsequence 主要是需要使用到 Binary Search
首先是 C++ 的 lower_bound
class Solution {
public:
int lengthOfLIS(vector< int>& nums) {
vector< int> arr;
arr.reserve(nums.size());
for (auto& num : nums) {
auto ptr = ranges::lower_bound(arr.begin(), arr.end(), num);
if (ptr == arr.end()) {
arr.push_back(num);
} else {
*ptr = num;
}
}
return arr.size();
}
};
然後是 Python 的 bisect.bisect_left()
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
arr = []
for num in nums:
i = bisect.bisect_left(arr, num)
if i == len(arr):
arr.append(num)
else:
arr[i] = num
return len(arr)
然後是 Go 的 sort.Search()
func lengthOfLIS(nums []int) int {
arr := make([]int, 0, len(nums))
for _, num := range nums {
idx := sort.Search(len(arr), func(i int) bool {
return arr[i] >= num
})
if idx == len(arr) {
arr = append(arr, num)
} else {
arr[idx] = num
}
}
return len(arr)
}
最後是 Rust 的 binary_search
impl Solution {
pub fn length_of_lis(nums: Vec< i32>) -> i32 {
let mut arr = Vec::with_capacity(nums.len());
for num in nums {
let i = match arr.binary_search(&num) {
Ok(index) => index,
Err(index) => index,
};
if i == arr.len() {
arr.push(num);
} else {
arr[i] = num;
}
}
arr.len() as i32
}
}
每種程式語言使用方式都有所不同。
2025年11月8日 星期六
Gray code
今天練習的題目是 1611. Minimum One Bit Operations to Make Integers Zero,後來自己想出解法後去看官方解答才發現是在考 Gray code 的轉換。
我自己列出 16 轉換到 0 的所有 bits 的表現方式,然後仔細觀察後發現有某種規律,於是就猜想了一個方程式出來試試看,結果還真的可以解答,先聲明一下這不是最有效的方法,只是眾多解法中的其中一種而已。
設 $p_1, p_2, \dots, p_j$ 為 $n$ 的二進位中所有 1 的 0-based 索引,且 $p_1 > p_2 > \dots > p_j$ ($p_1$ 是 MSB)。
$$f(n) = \sum_{i=1}^{j} (-1)^{i-1} \cdot (2^{p_i+1} - 1)$$
這是我初始第一個版本
class Solution {
public:
int minimumOneBitOperations(int n) {
if (n == 0)
return 0;
int k = 32 - __builtin_clz(n);
int ans = 0;
bitset<30> arr(n);
int sign = 1;
for (int i = k; i > 0; --i) {
if (arr[i - 1]) {
ans += sign * ((1 << i) - 1);
sign *= -1;
}
}
return ans;
}
};
簡化後的第二個版本
class Solution {
public:
int minimumOneBitOperations(int n) {
if (n == 0)
return 0;
int k = 32 - __builtin_clz(n);
int ans = 0;
int sign = 1;
for (int i = k; i > 0; --i) {
if (n & (1 << (i - 1))) {
ans += sign * ((1 << i) - 1);
sign *= -1;
}
}
return ans;
}
};
再進一步簡化後的第三個版本
class Solution {
public:
int minimumOneBitOperations(int n) {
if (n == 0)
return 0;
int ans = 0;
int sign = 1;
while (n) {
int k = 32 - __builtin_clz(n);
ans += sign * ((1 << k) - 1);
sign *= -1;
n ^= (1 << (k-1));
}
return ans;
}
};
有用 Gemini AI 幫忙寫了一份解答分享在 LeetCode 上面 Using a Math Formula 這樣
2025年10月28日 星期二
accumulate/sum 加總
今天練習的題目是 3354. Make Array Elements Equal to Zero,題目可以利用加總來解題。
首先是 C++ 要使用 accumulate
class Solution {
public:
int countValidSelections(vector< int>& nums) {
int ans = 0, prev = 0, post = accumulate(nums.begin(), nums.end(), 0);
for (auto num : nums) {
if (num == 0) {
if (prev == post)
ans += 2;
else if (abs(prev - post) == 1)
++ans;
} else {
post -= num;
prev += num;
}
}
return ans;
}
};
Python3 就用 sum()
class Solution:
def countValidSelections(self, nums: List[int]) -> int:
ans, prev, post = 0, 0, sum(nums)
for num in nums:
if num == 0:
if prev == post:
ans += 2
elif abs(prev - post) == 1:
ans += 1
else:
prev += num
post -= num
return ans
Go 加總要自己來,abs() 也要自己刻,不過發現可以利用 slices.Values() 在 range 上面,這樣就不用多寫一個 _ 底線,像是 for _, num := range nums 這樣,不過多寫 slices.Values 也不一定好到哪裡去。
func abs(num int) int {
if num < 0 {
return -num
}
return num
}
func countValidSelections(nums []int) int {
ans, prev, post := 0, 0, 0
for num := range slices.Values(nums) {
post += num
}
for num := range slices.Values(nums) {
if num == 0 {
if prev == post {
ans += 2
} else if abs(prev-post) == 1 {
ans += 1
}
} else {
prev += num
post -= num
}
}
return ans
}
最後 Rust 也可以用 sum() 不過要用在 iter() 後面,另外要注意的一點是 sum() 會需要指定型別,不然會有型別無法推導的問題,也可以用 nums.iter().sum::< i32>(),不過也可以用 fold 來算,像是 nums.iter().fold(0, |x, y| x + y) 這樣,就能成功推導型別。
nums.iter().sum() 的問題在於它非常泛型。sum() 方法可以將 &i32 的迭代器加總成多種不同的型別,例如 i32, i64, i128 等(任何實作了 Sum<&i32> trait 的型別)。
impl Solution {
pub fn count_valid_selections(nums: Vec<i32>) -> i32 {
let (mut ans, mut prev, mut post): (i32, i32, i32) = (0, 0, nums.iter().sum());
for num in nums {
if num == 0 {
if prev == post {
ans += 2;
} else if (prev-post).abs() == 1 {
ans += 1;
}
} else {
prev += num;
post -= num;
}
}
ans
}
}
2025年10月14日 星期二
ubuntu-dev-tools: /usr/bin/pbuilder-dist-simple
pbuilder-dist-simple 是個方便用來創建 pbuilder base 的 wrapper 腳本,使用的方式也很特別,就是要自己手動建立一個 symbolic link,然後它就會根據 symbolic link 的名稱來自動解析參數來處理。
例如:questing 是 Ubuntu 25.10 的 series name。
$ dpkg -S /usr/bin/pbuilder-dist-simple
ubuntu-dev-tools: /usr/bin/pbuilder-dist-simple
$ mkdir ~/bin
$ ln -s /usr/bin/pbuilder-dist-simple ~/bin/pbuilder-questing
建立完 symbolic link 就可以直接使用了,如果 PATH 有先設好的話。
$ echo $PATH
/home/user/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
$ pbuilder-questing create
W: /root/.pbuilderrc does not exist
I: Distribution is questing.
I: Current time: Tue Oct 14 06:55:39 UTC 2025
I: pbuilder-time-stamp: 1760424939
I: Building the build environment
I: running debootstrap
/usr/sbin/debootstrap
...
I: creating base tarball [/home/user/pbuilder/questing-base.tgz]
...
接下來我們可以抓 Debian libchewing source package 來試試看
$ pull-debian-source libchewing
Found libchewing 0.10.3-1 in sid
...
Downloading libchewing_0.10.3.orig.tar.xz from deb.debian.org (1.571 MiB)
[=====================================================>]100%
Downloading libchewing_0.10.3-1.debian.tar.xz from deb.debian.org (0.008 MiB)
[=====================================================>]100%
$ pbuilder-questing build libchewing_0.10.3-1.dsc
W: /root/.pbuilderrc does not exist
I: pbuilder: network access will be disabled during build
I: Current time: Tue Oct 14 07:02:09 UTC 2025
I: pbuilder-time-stamp: 1760425329
I: Building the build Environment
I: extracting base tarball [/home/user/pbuilder/questing-base.tgz]
...
dpkg-deb: building package 'libchewing3-data' in '../libchewing3-data_0.10.3-1_all.deb'.
dpkg-deb: building package 'libchewing3-dev' in '../libchewing3-dev_0.10.3-1_amd64.deb'.
dpkg-deb: building package 'libchewing3' in '../libchewing3_0.10.3-1_amd64.deb'.
dpkg-deb: building package 'chewing-tools' in '../chewing-tools_0.10.3-1_amd64.deb'.
...
$ cd ~/pbuilder/ && ls *chewing*
chewing-tools_0.10.3-1_amd64.deb libchewing_0.10.3-1_amd64.buildinfo libchewing_0.10.3-1.debian.tar.xz libchewing_0.10.3-1_source.changes libchewing3_0.10.3-1_amd64.deb libchewing3-dbgsym_0.10.3-1_amd64.ddeb
chewing-tools-dbgsym_0.10.3-1_amd64.ddeb libchewing_0.10.3-1_amd64.changes libchewing_0.10.3-1.dsc libchewing_0.10.3.orig.tar.xz libchewing3-data_0.10.3-1_all.deb libchewing3-dev_0.10.3-1_amd64.deb
看起來目前的 libchewing 0.10.3-1 in sid 可以在 Ubuntu 25.10 正常編譯沒有問題。


