我正在尝试在 LaTeX 中实现欧几里得算法。是的,我知道在 Python 中可以更轻松地做到这一点,但这不是重点。我有一个内部循环(提取到宏)来计算一个计数器除以另一个计数器的余数。外循环应该执行内循环,交换计数器的值,然后重复,直到其中一个计数器为 0。
\documentclass{article}
\usepackage{calc}
\setlength{\parskip}{\baselineskip}
\setlength{\parindent}{0pt}
\newcommand{\computeRemainder}[2]{%
\loop
\relax
\ifnum \value{#1} > \value{#2}
\addtocounter{#1}{-\value{#2}}
\repeat
\loop
\relax
\ifnum \value{#1} < 0
\addtocounter{#1}{\value{#2}}
\repeat}
\newcommand{\swapCounters}[2]{%
\addtocounter{#1}{\value{#2}} % cA = A + B
\addtocounter{#2}{\value{#1}} % cB = A + 2B
\addtocounter{#1}{-\value{#2}} % cA = -B
\setcounter{#2}{2*\value{#1}+\value{#2}} % cB = A
\setcounter{#1}{-\value{#1}} % cA = B
}
\begin{document}
\newcounter{counterA}
\setcounter{counterA}{45}
\newcounter{counterB}
\setcounter{counterB}{27}
Before: \( A = \thecounterA, \; B = \thecounterB \) \\
\loop
\computeRemainder{counterA}{counterB}
\swapCounters{counterA}{counterB}
\ifnum \value{counterB} > 0
Since this is true, shouldn't the loop continue? \\
\repeat
End: \( A = \thecounterA, \; B = \thecounterB \)
\end{document}
上述代码产生以下输出:
我对循环的理解是,如果它打印\ifnum \value{counterB} > 0
和之间的文本\repeat
,它应该重复循环。但是,执行后,和counterA
都counterB
大于 0。知道为什么外循环不会继续吗?
答案1
你不能嵌套\loop
在同一级别分组中嵌套,请参阅Tex 嵌套循环
>=
对于你可以使用的问题
\loop\unless\ifnum \value{#1} < \value{#2}
您可能还对完全可扩展版本感兴趣。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\euclid}{mm}
{
\int_compare:nTF { \int_abs:n { #1 } < \int_abs:n { #2 } }
{
\egreg_euclid:ff { \int_abs:n { #2 } } { \int_abs:n { #1 } }
}
{
\egreg_euclid:ff { \int_abs:n { #1 } } { \int_abs:n { #2 } }
}
}
\cs_new:Nn \egreg_euclid:nn
{
\int_compare:nTF { #2 = 0 }
{ #1 } % end
{ \egreg_euclid:nf { #2 } { \int_mod:nn { #1 } { #2 } } }
}
\cs_generate_variant:Nn \egreg_euclid:nn { nf , ff }
\ExplSyntaxOff
\begin{document}
\euclid{45}{27}
\euclid{45}{-27}
\euclid{27}{45}
\euclid{10}{0}
\euclid{0}{0}
\edef\temp{\euclid{2345}{356}}
\texttt{\meaning\temp}
\end{document}
宏\egreg_euclid:nn
以递归方式调用自身(在后续调用中使用变体以便使用“显式”值来提高效率)。
另外,在启动时会接受并规范化负值,因此 gcd 始终是非负的。
这里附有\fulleuclid
显示步骤的宏(仅非负数)。
\documentclass{article}
\usepackage{xparse,amsmath}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\euclid}{mm}
{
\int_compare:nTF { \int_abs:n { #1 } < \int_abs:n { #2 } }
{
\egreg_euclid:ff { \int_abs:n { #2 } } { \int_abs:n { #1 } }
}
{
\egreg_euclid:ff { \int_abs:n { #1 } } { \int_abs:n { #2 } }
}
}
\cs_new:Nn \egreg_euclid:nn
{
\int_compare:nTF { #2 = 0 }
{ #1 } % end
{ \egreg_euclid:nf { #2 } { \int_mod:nn { #1 } { #2 } } }
}
\cs_generate_variant:Nn \egreg_euclid:nn { nf , ff }
\NewDocumentCommand{\fulleuclid}{mm}
{
\tl_clear:N \l_egreg_fulleuclid_swap_tl
\seq_clear:N \l_egreg_fulleuclid_steps_seq
\int_compare:nTF { #1 < #2 }
{
\tl_set:Nn \l_egreg_fulleuclid_swap_tl { #1 &\leftrightarrow #2 \\ }
\egreg_fulleuclid:nn { #2 } { #1 }
}
{
\egreg_fulleuclid:nn { #1 } { #2 }
}
\begin{align}
\tl_use:N \l_egreg_fulleuclid_swap_tl
\seq_use:Nn \l_egreg_fulleuclid_steps_seq { \\ }
\end{align}
}
\tl_new:N \l_egreg_fulleuclid_swap_tl
\seq_new:N \l_egreg_fulleuclid_steps_seq
\cs_new_protected:Nn \egreg_fulleuclid:nn
{
\int_compare:nF { #2 = 0 }
{
\seq_put_right:Nx \l_egreg_fulleuclid_steps_seq
{
#1 &= #2 \exp_not:N \cdot
\int_div_truncate:nn { #1 } { #2 } +
\int_mod:nn { #1 } { #2 }
}
\egreg_fulleuclid:nf { #2 } { \int_mod:nn { #1 } { #2 } }
}
}
\cs_generate_variant:Nn \egreg_fulleuclid:nn { nf }
\ExplSyntaxOff
\begin{document}
\euclid{45}{27}
\euclid{45}{-27}
\euclid{27}{45}
\euclid{10}{0}
\euclid{0}{0}
\edef\temp{\euclid{2345}{356}}
\texttt{\meaning\temp}
\fulleuclid{356}{2345}
\end{document}