使用 LaTeX3 自动简化分数

使用 LaTeX3 自动简化分数

考虑以下代码:

\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}

output

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}

enter image description here

二进制算法稍微快一些,但这只有在文档中大量使用简化分数时才会明显。

相关内容