我想格式化一个表格,每列都有多项式。这些多项式不一定具有相同的程度,其系数可以是任意有理数。多项式的项应以适当的方式显示,以使不同行上的项不会混淆(例如,x 3被误认为是 x 2)。
起初我使用tabular
,但必须将每个多项式都包装在中$
变得不方便,因此我按照建议使用了array
+$
这里。
\documentclass{standalone}
\begin{document}
$\begin{array}{r|r|r|r}
d & u & v & q \\
x^5 + 3x^3 + x^2 + 2x + 1 & 1 & 0 & \\
x^4 + 5x^3 -5x^2 - 5x - 6 & 0 & 1 & \\
5x^4 + 8x^3 +6x^2 + 8x + 1 & 1 & -x & x \\
\end{array}$
\end{document}
得出以下结果。
前两个多项式的左侧部分未对齐。如何实现以下效果(其中项和运算符水平对齐)?
d | u | v | q --------------------------+-----+-----+--- x 5 + 3x 3 + x 2 + 2x + 1 | 1 | 0 | x 4 + 5x 3 - 5x 2 - 5x - 6 | 0 | 1 | 5x 4 + 8x 3 + 6x 2 + 8x + 1 | 1 | -x | X
(该表显示了 ℚ[x] 中两个多项式的扩展欧几里得算法)
答案1
当多项式排列良好时,唯一宽度变化的部分是系数。因此,我为运算符 (+/-)、系数和 x n使用了单独的列。为了避免水平间距过大,我@{}
在每个右对齐列说明符r
(记录在array
包中)。
使用该方法,多项式 f(x) 可以写成x^5 & & & + & 3x^3 & + & x^2 & + & 2x & + & 1
。这在源代码中很难阅读(由于重复的&
s 和x^i
s ),所以我定义了一些(递归)宏:
\polyCols{N}
创建 N 次重复r@{}r@{}...
作为array
选项。\poly[N]{..}{..}...
创建一个次数为 的多项式N
。例如\poly[2]{-}{5}{}{}{+}{1}
展开为- & 5x^2 & & & + & 1 &
。
结果如下:
\documentclass{standalone}
% #1 = deg(f), #2 = ignored, #3 = operator, #4 = coefficient
\newcommand\polyRecursiveImplementation[4][0]{
#3 &
\ifx\\#4\\ % Do not print a term if the coefficient is missing
\else
\ifnum#1=0 % Just show the coefficient for x^0
#4
\else
\ifnum#1=1 % Show "x" if the exponent is 1.
\if1#4\else#4\fi x
\else % Show "x^e" otherwise.
\if1#4\else#4\fi x^{#1}
\fi % \if1#4\else#4\fi = Only show the coefficient if it's not 1.
\fi
\fi
&
\ifnum0<#1
\polyRecursiveImplementation[\the\numexpr#1-1]
\fi
}
% Usage: \poly[deg(f)]{sign}{term} (repeat {..}{..} deg(f) times)
\newcommand\poly[3][0]{\polyRecursiveImplementation[#1]{}{#2}{#3}}
% Create a specifier for #1 polynomial terms.
\newcommand\polyCols[1]{
\ifnum0<#1
r@{}r@{}
\polyCols{\the\numexpr#1-1}
\fi
}
\begin{document}
$\begin{array}{
\polyCols{6}@{\hskip\tabcolsep}|
\polyCols{6}@{\hskip\tabcolsep}|
\polyCols{6}@{\hskip\tabcolsep}|
\polyCols{6}r
}
\multicolumn{12}{c|}{d} &
\multicolumn{12}{c|}{u} &
\multicolumn{12}{c|}{v} &
\multicolumn{12}{c}{q} & \\
\hline
\poly[5]{}{1}{}{}{+}{3}{+}{1}{+}{2}{+}{1}
\poly[5]{}{}{}{}{}{}{}{}{}{}{}{1}
\poly[5]{}{}{}{}{}{}{}{}{}{}{}{0}
\poly[5]{}{}{}{}{}{}{}{}{}{}{}{} \\
\poly[5]{}{}{}{1}{-}{5}{-}{5}{-}{5}{-}{6}
\poly[5]{}{}{}{}{}{}{}{}{}{}{}{0}
\poly[5]{}{}{}{}{}{}{}{}{}{}{}{1}
\poly[5]{}{}{}{}{}{}{}{}{}{}{}{} \\
\poly[5]{}{}{}{5}{+}{8}{+}{6}{+}{8}{+}{1}
\poly[5]{}{}{}{}{}{}{}{}{}{}{}{0}
\poly[5]{}{}{}{}{}{}{}{}{-}{1}{}{}
\poly[5]{}{}{}{}{}{}{}{}{}{1}{}{} \\
\end{array}$
\end{document}
代码详述
这是我的第一个非平凡宏,在构建它时我遇到了一些问题。以下是我发现的一些问题以及我如何解决它们:
宏最多可以采用 9 个参数。这个答案展示了如何处理更多参数;通过以另一个宏的不完整调用结束一个宏。然后,另一个宏将处理剩余的参数,这样参数总数就可以超过 9 个。
我的参数宏是递归的,但它必须在某个点结束。所以我从条件宏开始。
% Example: Wrap n elements in parentheses \newcommand\foo[2]{ (#2) \ifnum0<#1 \foo{\the\numexpr#1-1} \fi} \foo{10}{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}
这无法编译(
! File ended while scanning use of \foo.
)。显然,附加了\if...\if
一个额外的变量。为了解决这个问题,我将第一个位置参数声明为可选(\newcommand[N][...]{...}
)并忽略第二个参数。
使用这种构造,我可以将计数器作为参数传递给宏,并处理新元素。% #1 = count, #2 = ignored #3 = value to be wrapped in parentheses \newcommand\fooImpl[3][0]{ (#3) \ifnum0<#1 \fooImpl[\the\numexpr#1-1] \fi} \newcommand\foo[2]{\fooImpl[#1]{}{#2}} \foo{10}{0)(1}{2}{3}{4}{5}{6}{7}{8}{9}{10} % Output : (0)(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)
上述最小示例每次迭代消耗一个参数,而我的实际宏(
\poly
和\polyRecursiveImplementation
)则需要两个。
为了增强可读性,我将的第一个参数标记\poly
为可选,以便清楚地看到[
和之间的值]
用于指定多项式项的数量,而{...}{...}
指定每个项的符号和系数。最初我想省略最后一句
&
省略宏中最后一个项的最后一个。由于以下原因,这种条件逻辑无法实现:“不完整的 \ifnum;行后的所有文本均被忽略”),因此每一行都有一个多余的&
。
注意,参数数量必须匹配。如果你在复制并更改我的示例后出现错误,请检查参数数量是否匹配(\foo[N]{1}{2}...{N+1}
)。