对这个问题的第一个解释是(\randA)

对这个问题的第一个解释是(\randA)

如何返回某个范围内的随机数?下面是我想要的一个最小(非)工作示例:

\documentclass{article}

\newcommand{\thecmd}[1]{

% todo, set a to: #1-(#1/4)
% todo, set b to: #1+(#1/4)

\pgfmathrandominteger{\thenum}{\a}{\b}
\thenum

}

\begin{document}

\thecmd{10} % eg, this should return 1 number between 7.5-12.5, ideally rounded

\end{document}

范围应自动计算为(#1- #1/4#1+ #1/4)。

答案1

可以使用pgf/tikz包来完成

\documentclass{article}

\usepackage{pgf} 

\pgfmathsetseed{\number\pdfrandomseed} % to ensure that it is randomized
% use \randomseed for xelatex

\newcommand{\thecmd}[1]{% 
\pgfmathsetmacro{\a}{int(#1-#1/4)}%
\pgfmathsetmacro{\b}{int(#1+#1/4)}% 
\pgfmathsetmacro{\thenum}{int(random(\a,\b))}%
\thenum%
}%

\begin{document}

\thecmd{10}

\end{document}

编辑尽管它已被接受,但我将在@Schrödinger'scat 的评论的帮助下对其进行改进

int()截断数字。这会导致错误。使用上面的代码,我将得到一个介于$\text{int}(10-\frac{10}{4})=\text{int}(7.5)=7$和之间的数字$\text{int}(10+\frac{10}{4})=\text{int}(12.5)=12$。因此,一个介于$7$和之间的数字$12$,但$7$不在$7.5$和之间$12.5$,所以不应该是可能的。代码的改进可以是:

\documentclass{article}

\usepackage{pgf} 

\pgfmathsetseed{\number\pdfrandomseed} % to ensure that it is randomized
% use \randomseed for xelatex

\newcommand{\thecmd}[1]{% 
\pgfmathsetmacro{\thenum}{int(random(ceil(#1-#1/4),floor(#1+#1/4)))}%
\thenum%
}%

\begin{document}

\thecmd{10}

\end{document}

由于ceil()对数字进行向上舍入和floor()向下舍入,前面的代码会产生一个介于$\text{ceil}(7.5)=8$和之间的数字$\text{floor}(12.5)=12$

答案2

您可以使用\fp_eval:nrand()来自expl3。根据界面3.pdf,产生一个介于 0 和 1 之间的rand()伪随机浮点数(实际上是 10-16 的倍数) 。

对这个问题的第一个解释是(\randA

通过一些仿射变换,我们可以将 [0, 1) 范围变换成您想要的:[3/4*(#1), 5/4*(#1)) [参见注释 1]。然后我们可以使用将中间结果四舍五入为最接近的整数。当为 10round()时,最终结果是 [8, 12] 范围内的整数。#1

在这里,我故意使用了rand()而不是 ,randint()因为您的示例中有注释,“这应该返回 7.5-12.5 之间的 1 个数字,理想情况下是四舍五入的”。事实上,将该方法调整到例如范围 [7.2, 10.1] 不会产生与范围 [7.4, 10.1] 相同的结果:在两种情况下都可以从范围 [7, 10] 中获得相同的整数值,但频率不同。鉴于您示例中的注释,在我看来这就是您想要的。

请注意,此算法不会以相同的概率产生所有可能的整数值(但请参见\randB下面的变体)。例如,如果\randA使用参数调用12,则计算出的浮点范围为 [9, 15),因为 3(/4)*12 = 9 和 (5/4)*12 = 15,并且随机均匀选择发生在这个范围内。让我们调用这种随机均匀选择的结果。最终结果(的完全展开\randA{12})为:

  • 9 如果在 [9, 9.5) 中

  • 10 如果在 [9.5, 10.5] 中

  • 11 如果位于(10.5,11.5)

  • 12 如果在 [11.5, 12.5] 中

  • 13 如果位于(12.5,13.5)

  • 14 如果在 [13.5, 14.5]

  • 15 如果在(14.5,15)中。

除了第一个和最后一个间隔的长度为 0.5 之外,所有这些间隔的长度均为 1。因此,如果我们忽略可能返回值的数量rand()是有限的,并且某些间隔末端是开放的而其他是封闭的,则\randa{12}返回 10、11、12、13、14 中的一个的概率为 1/6,返回 9、15 中的一个的概率为 1/12。如果您希望以相同的概率获得 9、10、11、12、13、14、15 中的每一个值,则可以使用\randB我们现在将介绍的替代方法。

问题的第二种解释(\randB

\randB是具有不同语义的变体。\randB{x}计算相同的浮点区间 [(3/4)*x, (5/4)*x),但其结果(完全扩展后)是ceil()区间下限和floor()上限之间的均匀选择的随机整数。这意味着\randB{12}可以以相同的概率(即 1/7)扩展为整数 9、10、11、12、13、14、15 中的每一个。这将在下面的基准测试中显示。

代码

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewExpandableDocumentCommand \randA { m }
  {
    \fp_eval:n { round( 0.5*(#1)*(rand() + 1.5) ) }
  }

\NewExpandableDocumentCommand \randB { m }
  {
    \fp_eval:n { randint( ceil(0.75*(#1)), floor(1.25*(#1)) ) }
  }

\ExplSyntaxOff

\begin{document}

\randA{10}
\randB{10}

\end{document}

由于随机性,当您重新编译该文档时,输出将会有所不同。

比较两个函数的基准

\documentclass{article}
\usepackage{xparse}
\usepackage{xfp}
\usepackage{booktabs}
\usepackage{floatrow}
\usepackage{siunitx}
\sisetup{round-mode = places, round-precision=4}

\ExplSyntaxOn

\NewExpandableDocumentCommand \randA { m }
  {
    \fp_eval:n { round( 0.5*(#1)*(rand() + 1.5) ) }
  }

\NewExpandableDocumentCommand \randB { m }
  {
    \fp_eval:n { randint( ceil(0.75*(#1)), floor(1.25*(#1)) ) }
  }

% Code for the benchmark
\prop_new:N \l_my_prop          % nb of occurrences for each obtained result
\int_new:N \l_my_value_int
\int_new:N \l_my_count_int
\seq_new:N \l_my_values_seq
\tl_new:N \l_my_tabular_data_tl

\cs_generate_variant:Nn \prop_put:Nnn { NVx }

\cs_new_protected:Npn \my_benchmark:nn #1#2
  {
    \prop_clear:N \l_my_prop
    \int_step_inline:nn {#1}
      {
        \int_set:Nn \l_my_value_int {#2}

        \prop_if_in:NVTF \l_my_prop \l_my_value_int
          {
            \prop_get:NVN \l_my_prop \l_my_value_int \l_tmpa_tl
            \int_set:Nn \l_my_count_int { 1 + \l_tmpa_tl }
            \prop_put:NVx \l_my_prop \l_my_value_int
              { \int_use:N \l_my_count_int }
          }
          { \prop_put:NVn \l_my_prop \l_my_value_int { 1 } }
      }
  }

\NewDocumentCommand \runAndDisplayBenchmark { m m }
  {
    \my_benchmark:nn {#1} {#2}
    \seq_clear:N \l_my_values_seq

    \prop_map_inline:Nn \l_my_prop
      { \seq_put_right:Nn \l_my_values_seq { {##1} {##2} } }

    \seq_sort:Nn \l_my_values_seq
      {
        \int_compare:nNnTF { \use_i:nn ##1 } > { \use_i:nn ##2 }
          { \sort_return_swapped: }
          { \sort_return_same: }
      }

    \tl_clear:N \l_my_tabular_tl
    \seq_map_inline:Nn \l_my_values_seq
      {
        \tl_put_right:Nn \l_my_tabular_data_tl
          { \use_i:nn ##1 & \fp_eval:n { \use_ii:nn ##1 / (#1) } \\ }
      }

    \begin{tabular}{rS}
      \toprule
      { Value } & { Frequency } \\ \midrule
      \tl_use:N \l_my_tabular_data_tl
      \bottomrule
    \end{tabular}
  }

\ExplSyntaxOff

\newcommand{\numberOfTests}{10000}

\begin{document}

\begin{table}
  \floatsetup{rowfill=yes, captionskip=8pt}
  \begin{floatrow}[2]
    \hfil
    \ttabbox
      {\runAndDisplayBenchmark{\numberOfTests}{\randA{12}}}
      {\caption{\texttt{\string\randA}}}%
    \hfil
    \ttabbox
      {\runAndDisplayBenchmark{\numberOfTests}{\randB{12}}}
      {\caption{\texttt{\string\randB}}}%
    \hfil
  \end{floatrow}
\end{table}

\section*{\texttt{\string\randA}}

As explained earlier in the answer, the probabilities with \verb|\randA{12}|
are:
\begin{itemize}
\item $1/6 \approx \num{\fpeval{1/6}}$ for values 10, 11, 12, 13, and 14;
\item $1/12 \approx \num{\fpeval{1/12}}$ for values 9 and 15.
\end{itemize}

\section*{\texttt{\string\randB}}

If all possible values are chosen with the same probability, which should be
the case with \verb|\randB|, then the probability of each possible value
obtained with \verb|\randB{12}| is $1/7 \approx \num{\fpeval{1/7}}$.

\end{document}

这是我得到的结果。由于随机性,如果你自己运行这个基准测试,你当然可能会得到略有不同的结果。

基准测试结果


脚注

  1. 括号表示区间的开口。例如,[1,2] 包含所有浮点数 x,其中 1 ≤ x ≤ 2,而 [1,2) 是同一集合,但删除了数字 2,即:所有浮点数 x,其中 1 ≤ x < 2。

答案3

我的评论变成了答案。请注意,名称中有一个拼写错误,应该是\int_rand:nn。它将表达式作为纯整数表达式进行评估(因此不允许使用浮点数作为输入):

\documentclass[]{article}

\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand \randomint { m m }
  { \int_rand:nn { #1 } { #2 } }
\ExplSyntaxOff

\begin{document}
\randomint{8-8/4}{8+8/4}
\end{document}

因此,仅使用一个参数(仍然使用整数表达式,因为浮点数已经被覆盖)@frougon)你可以使用:

\documentclass[]{article}

\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand \randomint { m }
  { \int_rand:nn { #1 - #1/4 } { #1 + #1/4 } }
\ExplSyntaxOff

\begin{document}
\randomint{8}
\end{document}

答案4

可能对你来说不太满意,但是lcg包可以按如下方式使用:

\documentclass{article}
\usepackage[]{lcg}
\newcommand{\thecmd}[2]{
\chgrand[first=#1]
\reinitrand[last=#2]
\rand\arabic{rand}
}
\begin{document}

\thecmd{7}{12}

\end{document}

相关内容