如何返回某个范围内的随机数?下面是我想要的一个最小(非)工作示例:
\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:n
和rand()
来自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,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}