解方程¶
题目¶
小明这回的确问了个“物理”些的问题。虽然依旧是一个数学问题。他还是希望你能编写一个程序,帮他解决这个问题。他依旧想把你的程序运行在超算上,并要求你的程序是并行的。
给定函数 f(x,y) , 正整数 N , 在 N×N 均匀网格上用五点格式求解 Poisson 方程
\begin{cases} &-\Delta u(x,y)=f(x,y),\;(x,y)\in \Omega = (0,1)^2 \\ &u(x,y)=0,\;(x,y)\in \partial \Omega \end{cases}
记 U_{i,j} 为 u(x_i,y_j) 的近似值, 离散格式为
\begin{aligned} &4U_{i,j}-U_{i-1,j}-U{i+1,j}-U_{i,j-1}-U_{i,j+1}=\frac{1}{N^2}f(x_i,y_i),\;x_i=\frac{i}{N},y_i=\frac{j}{N} \\ & U_{i,j}=0,\;(i=0)\lor(i=N)\lor(j=0)\lor(j=N) \end{aligned}
用 Gauss-Seidel 迭代求解上述线性方程组。
小明定义了一个量,名叫 EPS(各分量绝对值差最大值)。他希望你的程序能在 EPS 小于 10−15 后输出结果。
你看着题目苦笑了一下。如你所料,小明总是念念不忘他的 OpenMP。
输入格式¶
文件引用 rhs.h,该头文件定义了:
void rhs(double &x, double &y, double &value);
该函数即为 f(x,y)。传入 x, y, value, 那么 f(x,y) 就会被保存到 value 内。
程序需要接收一个命令行参数,即为 N。
输出格式¶
每行一个浮点数,依次输出 U0,0, U0,1, ..., U0,n, U1,0, U1,1, ..., Un,n。通常保留 12 位有效数字即可。当然,我们只会检查误差大小,并不严格要求保留的小数位数。
如果误差超过 10−10,那么该测试点将获得 0 分。误差在 10−10 范围之内的,误差越小,得分越高;误差在 10−11 范围以内的,有机会获得该测试点的全部分数。
样例¶
rhs.cpp
#include <cmath>
#include "rhs.h"
#define PI25DT 3.141592653589793238462643
#define doublePIsquare 19.73920880217871723766898
void rhs(double &x, double &y, double &value) {
value = doublePIsquare * std::sin(PI25DT * x) * std::sin(PI25DT * y);
}
rhs.h
#ifndef _rhs_h_
#define _rhs_h_
void rhs(double &, double &, double &);
#endif
我们输入的 N 大约在几十的范围内。
测试点说明¶
本题一共设置 6 个测试点:
| 编号 | 分值 | 时限 | 核心 |
|---|---|---|---|
| 0 | 10 | 4s | 8 |
| 1 | 20 | 6s | 8 |
| 2 | 20 | 6s | 8 |
| 3 | 20 | 9s | 8 |
| 4 | 20 | 6s | 8 |
| 5 | 30 | 15s | 8 |
同时,你需要确保你的程序足够并行,也即是说:你需要充分利用所给核心,否则可能出现 Not So Parallel 的结果,那样只能获得该测试点原本得分一半的分数。