[toc] # 答案提交指南 ## 摘要 本篇帮助介绍了提交答案的格式、语法等内容,以及一些常见问题。 解析答案使用的编译器基于 SymPy,大部分 SymPy 功能可以使用。 ## 表达式 程序的单元是**表达式**。表达式代表的是一个具体的值,或者一个含有未知量的算式。 ### 实数 一个实数是表达式中的单元。可以使用在大多数编程语言中适用的语法表示整数和浮点数。也可以使用科学计数法,例如 `6.67430e-11` 表示 $6.67430\times10^{-11}$。编译器将自动将浮点数解析为有理数以保证精确。 ### 基本运算符 在表达式中,可以使用 `+`、`-` 等**基本运算符**。以下是所有可用的基本运算符。 运算符 | 意义 | 优先级 | 备注 :---: | :-: | :---: | --- `(...)` | 括号 | 9 `\|...\|` | 取绝对值 | 9 | 用法与括号相同 `!` | 阶乘运算 | 8 | 一元右结合 `^` | 乘方运算 | 7 | 二元右结合 `+` | 取正号 | 6 | 一元左结合 `-` | 取负号 | 6 | 一元左结合 `*` | 乘法运算 | 5 `/` | 除法运算 | 5 `+` | 加法运算 | 4 `-` | 减法运算 | 4 ### 逻辑运算符 在表达式中,还可以使用 `>`、`<` 等**逻辑运算符**。以下是所有可用的逻辑运算符。 运算符 | 意义 | 优先级 | 运算符 | 意义 | 优先级 | 运算符 | 意义 | 优先级 :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: `in` | 包含于 | 3 `>` | 严格大于 | 3 | `>=` | 大于或等于 | 3 | `==` | 等于 | 3 `<>` | 不等于 | 3 | `<=` | 小于或等于 | 3 | `<` | 严格小于 | 3 `not` | 非 | 2 | `and`| 且 | 1 | `or` | 或 | 0 与大部分编程语言不同的是,6 个比较运算符连续结合时将自动解析为且运算的连续结合。例如:`x < y < z` 与 `x < y and y < z` 等价。 ### 函数调用 表达式中,还可以使用**函数**。例如,使用 `sqrt(x)` 表示 $\sqrt x$。表达式支持大多数 SymPy 函数,也可以调用自己定义的函数。有关如何自定义函数,参见[此处](#函数定义)。 函数可以使用关键字参数。例如,定义函数 ``` f(x, y, z) = x^2 + 2*y^2 + 3*z^2; ``` 则使用 `f(1, 2, 3)`、`f(1, 2, z=3)`、`f(1, z=3, y=2)` 效果相同。 以下是常用的 SymPy 函数: 用法 | 意义 :-: | --- `Pow(x, n)` 或 `x^n`、`sqrt(x)` 或 `x^(1/2)` | 指数或平方根 `Abs(x)` 或 `\|x\|` | 取绝对值 `exp(x)` 或 `E^x` | 以 $\mathrm e$ 为底的指数函数 `log(x)` 或 `ln(x)`、`log(x, b)` 或 `ln(x, b)` | 自然对数或以 $b$ 为底的对数 `factorial(x)` | 阶乘 `gcd(m, n)`、`lcm(m, n)` | 最大公约数或最小公倍数 `divisors(n)` | 求整数的因子 `isprime(p)` | 判断是否为素数 `diff(cos(x), x)`、`diff(cos(x), x, 2)` | 求一阶或高阶导数或偏导数 `integrate(cos(x), x)`、`integrate(exp(-x), (x, 0, oo))` | 求不定积分或定积分 `integrate(exp(-x^2 - y^2), (x, -oo, oo), (y, -oo, oo))` | 求多重积分 `limit(sin(x)/x, x, 0)` | 求极限 `(1, 2, 3)` | 创建元组 `Matrix([[1, 2, 3], [4, 5, 6]])` | 创建矩阵 `eye(6)` | 创建单位矩阵 ### 对象属性与方法 可以使用 `.` 获取对象的**属性**或**方法**。例如,`sqrt(2).evalf()` 返回 $\sqrt2$ 的浮点数值(`1.41421356237310`);`sqrt(2).args` 返回 $\sqrt2$ 中的所有*单位*。 以 `_` 开头的属性和方法被认为是内置的或 Python 特性敏感的,如果调用将会被拦截。 注:算式中的*单位*是指描述算式的直接参数。事实上,SymPy 的 `sqrt` 函数将 $\sqrt x$ 展开为 $x^{1/2}$,所以单位是 $x, 1/2$,而 $\sqrt{x + y}$ 的单位是 $x + y, 1/2$。 以下是常用的 SymPy 属性或方法: 用法 | 意义 :--------------------: | --- `expr.evalf()` | 求浮点数值 `expr.args` | 求算式中的单位 `f(x).series(x, 0, 6)` | Taylor 展开前 $6$ 项 `f(x).series(x, 6)` | 在 $x = 6$ 处 Taylor 展开 `M.T` | 矩阵转置 `M^(-1)` | 求逆矩阵 `M.det()` | 求矩阵行列式 `M.eigenvals()` | 求矩阵特征值 `M.diagonalize()` | 求对角化矩阵 ### 默认常量 表达式中可以使用一些预定义的默认**常量**。以下是一些常用的预定义常量: 名称 | 意义 -------------------: | :-- `Catalan` | Catalan 常数 `E` | 自然对数 `EulerGamma` | Euler–Mascheroni 常数 `GoldenRatio` | 黄金分割比 `I` | 虚数单位 `TribonacciConstant` | 三 Fibonacci 常数 `false` | 假值 `nan` | 未定义值 `oo` | 实无穷 `pi` | 圆周率 `true` | 真值 `zoo` | 复无穷 ### 数组 表达式中还可以使用 `[表达式1, 表达式2, ...]` 创建**数组**。例如 ``` A = [8, 3, 5, 1, 2, 0, 7, 6, 4, 9]; ``` 就将数组存储在了变量 `A` 中。 可以使用 `A[i]` 取出数组中的元素。例如,`A[6]` 的结果为 `7`。在表达式中也可以不指定 `i`,从而使 `A[i]` 泛指数组中的某个元素。 ### 元组 使用 `(表达式1, 表达式2, ...)` 创建**元组**,使用方法与数组类似,但要注意 `Sum`、`integrate` 等函数的一些参数只接受元组而非数组。 ### 集合 #### 有限集合 使用 `{表达式1, 表达式2, ...}` 创建**有限集合**。例如 ``` S = {8, 3, 5, 1, 2, 0, 7, 6, 4, 9}; ``` 就将有限集合存储在了变量 `S` 中。创建集合时,会自动去除其中重复的元素。 #### 条件集合 使用 `{标识符 | 条件}` 或 `{标识符 in 基集 | 条件}` 创建条件集合,例如 ``` S = {x | 14 <= x <= 36}; ``` 就创建了一个集合 $\{x \mid 14 \le x \le 36 \}$;如果想要集合元素为整数,可以使用 ``` S = {x in Naturals | 14 <= x <= 36}; ``` 这相当于 $\{x \in \N \mid 14 \le x \le 36 \}$; #### 区间 使用 `Interval()` 创建区间,例如 `Interval(14, 36)` 就创建了一个区间 $[14, 36]$。可以指定 `left_open=true` 和/或 `right_open=true` 控制区间左右的开闭,例如 `Interval(14, 36, left_open=true)` 相当于 $(14, 36]$。 #### 预定义集合 下面的标识符是一些预定义的集合,可以直接使用: 名称 | 意义 :------------: | --- `UniversalSet` | 全集 $\mathbb U$ `Reals` | 实数集 $\R$ `Rationals` | 有理数集 $\mathbb Q$ `Integers` | 整数集 $\Z$ `Naturals0` | 自然数集 $\N$,包含 $0$ `Naturals` | 正整数集 $\N^+$(又作 $\N^*$、$\N_+$),不含 $0$ `EmptySet` | 空集 $\emptyset$ #### 集合的常用操作 下面是集合的常用操作,其中的 `S`、`S1`、`S2` 代表集合 $S$、$S_1$、$S_2$,`x` 代表元素: 用法 | 意义 :-: | --- `S1 + S2` 或 `S1.union(S2)` | $S_1 \cup S_2$ `S2 - S1` 或 `S1.complement(S2)` | $S_1 \setminus S_2$ `S1.intersect(S2)` 或 `S1.intersection(S2)` | $S_1 \cap S_2$ `S.powerset()` | $S$ 的幂集 `S1 * S2` | $S_1$ 和 $S_2$ 的 Cartesian 积 `x in S` 或 `S.contains(x)` | $x \in S$ `S1.is_subset(S2)` | $S_1 \subset S_2$ `S1.is_proper_subset(S2)` | $S_1 \subsetneq S_2$ `S1.is_superset(S2)` | $S_1 \supset S_2$ `S1.is_proper_superset(S2)` | $S_1 \supsetneq S_2$ `S1.is_disjoint(S2)` | $S_1$ 与 $S_2$ 不相交 ## 语句 程序的所有部分都由**语句**组成。若干个语句以分号 `;` 隔开。**语句体**是指一条语句除分号以外的内容。 ### 语句块与返回值 一个**语句块**是用大括号 `{ 语句 }` 包围的一组语句。这一组语句中的最后一条语句可以省略分号,仅保留语句体。语句块可以嵌套。 一条语句可能有一个**返回值**。一个表达式可以独成一条语句,其返回值就是其本身。整个程序的返回值是这一程序中第一个返回语句的返回值;如果程序中没有执行返回语句,程序的返回值就是程序最后一条语句的返回值。 一个语句块的返回值是这一语句块中第一个返回语句的返回值;如果语句块中没有执行返回语句,语句块的返回值就是语句块最后一条语句的返回值。 有关语句的返回值,参见[此处](#返回语句)。 ### 赋值语句 使用 `变量名 = 值` 语句为变量**赋值**。赋值后可以在表达式中使用值。赋值语句的返回值就是被赋的值。 也可以使用**自增**、**自减**等语法,使用方法为 ``` 变量名 ( += | -= | *= | /= | %= | ^= ) 值 ``` 使用方法与大部分编程语言中的相同。其中,`a ^= b` 操作相当于 `a = a ^ b`,即求幂。 ### 条件语句 使用 ``` if (条件) 语句或语句块 [ else 语句或语句块 ] ``` 编写**条件判断语句**,其中 `else` 块可选。即使条件语句的最后一个部分是语句块,为了将其与后面的语句隔开,其后面也要添加一个分号 `;`。 ### 循环语句 使用 ``` while (条件) 语句或语句块 ``` 编写**循环语句**,即使循环语句的最后一个部分是语句块,为了将其与后面的语句隔开,其后面也要添加一个分号 `;`。其意义与其在大部分编程语言中的意义相同,即重复循环体知道某条件不成立。 ### 返回语句 使用 `return 值` **返回**值。执行后,当前所在的作用域的剩余语句停止执行。 ### 函数定义 函数定义的语法与赋值语句相似。使用 ``` 函数名(参数1, 参数2, ...) = 表达式或语句块 ``` 定义函数。与普通语句块不同,在计算函数体的结果时会创建单独的作用域,其父作用域是函数定义时的当前作用域。有关作用域的内容,参见[此处](#变量与作用域)。 ## 变量与作用域 整个程序中存在一个**主作用域**;一些语句块,例如函数的语句块中,存在**独立的作用域**。两独立作用域中可以存在名称相同而值不同的变量,互不干扰。除主作用域外,每个作用域都有一个**父作用域**。 一个作用域中有一些**全局变量名**和**非局部变量名**。当表达式中有某变量名时,查找变量的值的过程为: 1. 如果变量名是全局变量名,则查询该变量在父作用域中的值,并将本作用域中该变量的值设为查询出的值;如果当前作用域是主作用域,则返回当前作用域中该变量的值; 2. 如果变量名是非局部变量名,则查询该变量在父作用域中的值; 3. 否则查询当前作用域中该变量的值;如果未能查询到且当前作用域非主作用域,则返回父作用域中该变量的值。 使用 `global 变量名1, 变量名2, ...` 语句将一些变量名递归添加为当前作用域及其祖先作用域的全局变量名。使用 `nonlocal 变量名1, 变量名2, ...` 语句将一些变量名添加为当前作用域的非局部变量名。 ## Backus-Naur 范式 我们使用的语言的 BNF 如下: ```bnf ::= IDENTIFIER | FLOAT_ATOM | INT_ATOM ::= IDENTIFIER "=" { "," IDENTIFIER "=" } ::= "(" { "," } [ [ "," ] | ] [ "," ] ")" ::= "[" { "," } [ "," ] "]" ::= IDENTIFIER ( | )+ ::= "[" [ { "," } [ "," ] ] "]" ::= "{" [ { "," } [ "," ] ] "}" ::= "{" IDENTIFIER [ "in" ] "|" "}" ::= "(" ")" | "|" "|" ::= ( | | | | | ) { "." IDENTIFIER { | } } ::= { "!" } ::= { "^" } ::= { "+" | "-" } ::= { ( "*" | "/" | "%" ) } ::= { ( "+" | "-" ) } ::= { ( "<" | "<=" | "==" | "<>" | ">=" | ">" | "in" ) } ::= { "not" } ::= { "and" } ::= { "or" } ::= ::= IDENTIFIER ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "^=" ) ::= IDENTIFIER LPAREN [ IDENTIFIER { "," IDENTIFIER } [ "," ] ] RPAREN "=" ( | ) ::= "return" ::= "if" [ ( ";" | ) "else" ] ( | ) ::= "while" ( | ) ::= "global" IDENTIFIER { "," IDENTIFIER } [ "," ] ::= "nonlocal" IDENTIFIER { "," IDENTIFIER } [ "," ] ::= | | | | | | | | ::= { ";" } [ ";" ] ::= "{" "}" ``` 其中 `IDENTIFIER` 指变量名这类标识符,即开头是字母或下划线,后面紧跟任意个字母、数字或下划线的字符串;`FLOAT_ATOM` 指浮点数;`INT_ATOM` 指整数。 ## 常见问题与解答 ### 为什么我提交正确的答案后仍显示错误或超时? 中国大陆连接服务器所需时间较长,且服务器硬件设施不佳,可能是服务器的问题,尝试多提交几次。 ### 为什么我提交答案后得到满分却显示“无法求值”? 服务器硬件设施不佳,单独求值时也可能产生无故超时等问题。单独求值结果不重要,可以忽略这一问题。 ### 测试点的不同图标各有什么意义? 图标 | 意义 :-: | --- | 答案正确 | 答案错误,答案与题目作者提供的答案不符 | 解析错误,答案存在语法错误等 | 求值错误,答案存在运行时错误等 | 超时错误,答案的解析与求值时间超过指定时间 | 作者提供的答案存在语法错误、运行时错误、超时等问题,此时请联系题目作者 你也可以将鼠标在测试点图标上悬停一段时间,然后就能看到测试点的状态。