考虑以下代码:
\documentclass{article}
\begin{document}
\noindent
Can I make \LaTeX{} reduce a fraction automatically?\\[\baselineskip]
For example, I would like the fraction
\[ \frac{278\,922}{74\,088} \]
to be reduced to
\[ \frac{6641}{1764} \]
\end{document}
PS 在我的例子中,分子和分母始终都是自然数。
答案1
一个expl3
执行:
\nonstopmode \input expl3-generic \relax \ExplSyntaxOn % -*- expl3 -*-
\cs_new:Nn \svend_gcd:nn
{
\int_compare:nNnTF {#2} = { 0 } {#1}
{ \svend_gcd:ff {#2} { \int_mod:nn {#1} {#2} } }
}
\cs_generate_variant:Nn \svend_gcd:nn { ff }
\int_new:N \l__svend_tmp_int
\cs_new:Nn \svend_reduced:nn
{
\int_set:Nn \l__svend_tmp_int { \svend_gcd:nn {#1} {#2} }
{ \int_eval:n { #1 / \l__svend_tmp_int } }
\over
{ \int_eval:n { #2 / \l__svend_tmp_int } }
}
$$ \svend_reduced:nn {278922} {74088} $$
\bye
LaTeX 版本:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% ... code code code
\msg_new:nnn { svend } { malformed-fraction }
{
The~input~you~have~provided~is~malformed.~
Please~provide~input~in~the~form~of~`p/q'.
}
\NewDocumentCommand \ReducedFraction { > { \SplitList { / } } m }
{
\int_compare:nTF { \tl_count:n {#1} = 2 }
{ \svend_reduced:nn #1 }
{ \msg_error:nn { svend } { malformed-fraction } }
}
\ExplSyntaxOff
\begin{document}
\[ \ReducedFraction{278922/74088} \]
\end{document}
使用包装器进行编辑
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Nn \svend_gcd:nn
{
\int_compare:nNnTF {#2} = { 0 } {#1}
{ \svend_gcd:ff {#2} { \int_mod:nn {#1} {#2} } }
}
\cs_generate_variant:Nn \svend_gcd:nn { ff }
\int_new:N \l__svend_tmp_int
\cs_new:Nn \svend_reduced:nn
{
\int_set:Nn \l__svend_tmp_int { \svend_gcd:nn {#1} {#2} }
\frac { \svend_reduced_wrap:n { \int_eval:n { #1 / \l__svend_tmp_int } } }
{ \svend_reduced_wrap:n { \int_eval:n { #2 / \l__svend_tmp_int } } }
}
\cs_new:Nn \svend_reduced_use_wrapper:N
{ \cs_set_eq:NN \svend_reduced_wrap:n #1 }
\svend_reduced_use_wrapper:N \use:n
%%% Interface
\msg_new:nnn { svend } { malformed-fraction }
{
The~input~you~have~provided~is~malformed.~
Please~provide~input~in~the~form~of~`p/q'.
}
\NewDocumentCommand \ReducedFractionWrapper { m }
{ \svend_reduced_use_wrapper:N #1 }
\NewDocumentCommand \ReducedFraction { o > { \SplitList { / } } m }
{
\group_begin:
\IfValueT{#1}{\ReducedFractionWrapper{#1}}
\int_compare:nTF { \tl_count:n {#2} = 2 }
{ \svend_reduced:nn #2 }
{ \msg_error:nn { svend } { malformed-fraction } }
\group_end:
}
\ExplSyntaxOff
\usepackage{siunitx}
\begin{document}
\[ \ReducedFraction[\num]{278922/74088} \]
\ReducedFractionWrapper{\num}
\[ \ReducedFraction{27892/74088} \]
\end{document}
答案2
这是一个平面的 LaTeX2e 实现。
\documentclass{article}
\usepackage{amsmath}
\newcount{\numerator}
\newcount{\denominator}
\newcount{\gcd}
% compute \gcd and returns reduced \numerator and \denominator
\newcommand{\reduce}[2]% #1=numerator, #2=denominator
{\numerator=#1\relax
\denominator=#2\relax
\loop
\ifnum\numerator<\denominator
\advance\denominator by -\numerator
\gcd=\denominator
\else
\advance\numerator by -\denominator
\gcd=\numerator% swap
\fi
\ifnum\gcd>1 \repeat
\ifnum\gcd=0 \gcd=\denominator\fi
\numerator=#1\relax
\divide\numerator by \gcd
\denominator=#2\relax
\divide\denominator by \gcd
}
\begin{document}
For example, I would like the fraction
\begin{equation*}
\frac{278922}{74088}
\end{equation*}
to be reduced to\reduce{278922}{74088}
\begin{equation*}
\frac{\the\numerator}{\the\denominator} =
\frac{6641}{1764}
\end{equation*}
\end{document}
答案3
如果你没有义务expl3
(在这种情况下你“只”需要实现该算法):
\documentclass{scrartcl}
\usepackage{xintgcd,xintfrac}
\newcommand*\reducedfrac[2]
{\begingroup
\edef\gcd{\xintGCD{#1}{#2}}%
\frac{\xintNum{\xintDiv{#1}{\gcd}}}{\xintNum{\xintDiv{#2}{\gcd}}}%
\endgroup}
\begin{document}
\[
\frac{278922}{74088} = \reducedfrac{278922}{74088}
\]
\end{document}
答案4
已经有另一个 LuaTeX 答案,但在我看来,它不能正确处理局部性和整数除法。所以在这里,利用 Lua 5.3+ 的事实包括按位运算符,使用不同的方法二进制 GCD 算法:
\documentclass{standalone}
%\usepackage{amsmath}
\usepackage{luacode}
\begin{luacode*}
userdata = userdata or {}
--https://xlinux.nist.gov/dads/HTML/binaryGCD.html
userdata.gcd = function (u,v)
--To handle with negative values
local u, v, g = math.abs(u), math.abs(v), 1
--Nice error message
assert(v~=0, "Denominator cannot be zero")
while u&1==0 and v&1==0 do
u=u>>1
v=v>>1
g=g<<1
end
while u>0 do
if u&1==0 then
u=u>>1
elseif v&1==0 then
v=v>>1
else
local t = math.abs(u-v)>>1
if u<v then v=t else u=t end
end
end
return v*g
end
userdata.simplified = function(u,v)
local gcd = userdata.gcd(u,v)
tex.sprint(("\\frac{%d}{%d}")
:format(u//gcd, v//gcd))
end
\end{luacode*}
\newcommand\simplified[2]{\directlua{userdata.simplified(#1,#2)}}
\begin{document}
$\displaystyle\simplified{278922}{74088}$
\end{document}
二进制算法稍微快一些,但这只有在文档中大量使用简化分数时才会明显。