CONTENTS

巴塞尔问题(你好,世界!)

你好,世界!这是我的第一篇文章,专门用来测试这个网站的功能。

以下是用不同语言计算巴塞尔问题的一些代码片段:

首先,

Python

def pi_squared_over_6(N: int) -> float:
    return sum(x**(-2) for x in range(1,N))

Rust

fn pi_squared_over_6(N: u64) -> f64 {
    (1..N).map(|x| 1.0 / ((x*x) as f64)).sum()
}

Haskell

piSquaredOver6 :: Integer -> Double
-- Haskell 里没有大写的 N :(
piSquaredOver6 n = sum $ map (\x -> 1 / fromIntegral (x * x)) [1..n]

C

double pi_squared_over_6(unsigned int N) {
    double sum = 0.0;
    for (int i = 1; i < N; i++) {
        sum += 1.0 / (i*i);
    }
    return sum;
}

你最喜欢哪种解法?

性能

让我们看看在 M1 Pro 上,当 时,它们的性能对比。

语言 耗时(毫秒,
Rust (并行化)
Rust (–release)
C (-O3)
Haskell (-O3)
Python (3.10)

修复 Python

Python 代码的运行时间长得离谱,所以我们通过利用 numpy(它调用的是向量化的 C 代码)来修复它。

import numpy as np

def pi_squared_over_6(N: int) -> float:
    x = np.ones(N)
    r = np.arange(1,N)
    sq = np.square(r)
    div = np.divide(x, sq)
    return float(np.sum(div))

好一些了,但当我查看 btm 时,过高的内存消耗表明大部分工作是在移动数十亿个浮点数,而不是实际的计算。让我们尝试将其分块处理:

def pi_squared_over_6(N: int) -> float:
    CHUNKS = 25000
    SIZE = N // CHUNKS
    s = 0.0
    x = np.ones(N // CHUNKS - 1)
    for i in range(CHUNKS):
        N_tmp = i * SIZE
        r = np.arange(N_tmp + 1, N_tmp + SIZE)
        sq = np.square(r)
        div = np.divide(x, sq)
        s += np.sum(div)
        # 释放内存
        del sq
        del div
        del r
        
    return s

好多了!现在它能在 2 秒内运行完毕!

✦ 本文的构思、研究、撰写和编辑均未使用大语言模型。