我想制作一个 10x10 的形状网格。网格上的每个点都应显示一个形状。该形状应从 {红色圆圈、红色菱形、白色圆圈} 中随机抽取,以便:
- 红色圆圈是均匀绘制的,但最少有 10 个,最多有 50 个;
- 红色菱形是均匀绘制的,但最少有 10 个,最多有 50 个;
- 白色圆圈占据剩余的网格元素。
对于完全均匀随机选择(即不尝试将红色形状的范围限制在 10-50 之间),我的 MWE 尝试如下,但似乎我的使用\pgfmathdeclarerandomlist
不正确。文件无法编译。你知道我该如何解决这个问题吗?
(相比之下,在此之下我包含了一个 MWE,其中只有形状会变化,并且每个元素都是从 {红色圆圈,红色菱形} 中随机抽取的,并且效果很好)
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes}
\begin{document}
\begin{tikzpicture}
\pgfmathdeclarerandomlist{states}{{circle,fill=red}{diamond,fill=red}{circle,fill=white}};
\draw [thin, black] (0,0) rectangle (7.75,7.75);
\foreach \r in {1,...,10} %row
\foreach \c in {1,...,10} %col
{
\pgfmathrandomitem{\state}{states}
\node[\state,draw=black] at (\c*0.75-0.25,0.75*\r-0.25) {};
}
\end{tikzpicture}
\end{document}
仅形状变化且对每种形状的数量没有限制的示例,编译后生成下图:
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes}
\begin{document}
\begin{tikzpicture}
\pgfmathdeclarerandomlist{states}{{circle}{diamond}};
\draw [thin, black] (0,0) rectangle (7.75,7.75);
\foreach \r in {1,...,10} %row
\foreach \c in {1,...,10} %col
{
\pgfmathrandomitem{\state}{states}
\node[\state,fill=red,draw=black] at (\c*0.75-0.25,0.75*\r-0.25) {};
}
\end{tikzpicture}
\end{document}
答案1
我认为这很简单。只需猜测您需要放置 45 个红色圆圈 (RC)、30 个红色菱形 (RD) 和 25 个白色圆圈 (WC)。我们可以在 1 和 100 之间选择一个随机数 n=45+30+25。如果 n 在 [1,45] 中,我们放置一个 RC,如果 n 在 [45+1,45+30]=[46,75] 中,我们放置一个 RD,如果 n 在 [45+30+1,45+30+25]=[76,100] 中,我们放置一个 WC。假设数字为 [46,75] 中的 n=50。我们放置 RD,现在我们有 45 个 RC、29 个 RD 和 25 个 WC 需要放置。只需重复...
为了避免全局变量,我使用了两个计数器。
像这样:
\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{shapes}
\tikzset
{% styles
shape1/.style={draw,circle,fill=red},
shape2/.style={draw,diamond,fill=red},
shape3/.style={draw,circle,fill=white},
}
%% counters
\newcounter{NumberOfRC} % Number of Red Circles
\newcounter{NumberOfRD} % Number of Red Diamonds
\begin{document}
\begin{tikzpicture}
\pgfmathparse{random(10,50)}\setcounter{NumberOfRC}{\pgfmathresult}
\pgfmathparse{random(10,50)}\setcounter{NumberOfRD}{\pgfmathresult}
% Just to check the numbers
%\node[right] at (0,-1) {Red circles: \theNumberOfRC};
%\node[right] at (0,-1.5) {Red diamonds: \theNumberOfRD};
\draw [thin, black] (0,0) rectangle (7.75,7.75);
\foreach\i in {1,...,100}
{
\pgfmathsetmacro\x{0.775*(mod(\i-1,10)+0.5)}
\pgfmathsetmacro\y{0.775*(int((\i-1)/10)+0.5)}
\pgfmathtruncatemacro\RandomNumber{random(0,100-\i)}
\pgfmathtruncatemacro\TotalNumberOfRF{\theNumberOfRC+\theNumberOfRD} % Total number of Red Figures
\ifnum\RandomNumber<\theNumberOfRC
\pgfmathsetmacro\shape{1}
\addtocounter{NumberOfRC}{-1}
\else\ifnum\RandomNumber<\TotalNumberOfRF
\pgfmathsetmacro\shape{2}
\addtocounter{NumberOfRD}{-1}
\else
\pgfmathsetmacro\shape{3}
\fi\fi
\node[shape\shape] at (\x,\y) {};
}
\end{tikzpicture}
\end{document}
答案2
您可以先生成一个包含随机顺序形状的列表,然后在循环中一个接一个地使用这些形状foreach
。
以下代码允许您首先创建一个列表,该列表生成两个介于 10 和 50 之间的随机整数和一个表示 100 余数的整数。然后,用三个不同形状的相应数量填充列表。最后打乱此列表。在循环内部foreach
,然后选择此列表相关索引处的形状。
\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes}
\ExplSyntaxOn
% create the needed integer and sequence variables
\int_new:N \l_randomshapes_shapeAcount_int
\int_new:N \l_randomshapes_shapeBcount_int
\int_new:N \l_randomshapes_shapeCcount_int
\seq_new:N \l_randomshapes_shapelist_seq
\NewDocumentCommand { \createshuffledshapelist } { m m m m m } {
% set the first variable to a random number between #2 and #3
\int_set:Nn \l_randomshapes_shapeAcount_int {
\int_rand:nn { #2 } { #3 }
}
% set the second variable to a random number between #4 and #5
\int_set:Nn \l_randomshapes_shapeBcount_int {
\int_rand:nn { #4 } { #5 }
}
% set the third variable to #1 minus the sum of the previous two variables
\int_set:Nn \l_randomshapes_shapeCcount_int {
#1 - \l_randomshapes_shapeAcount_int - \l_randomshapes_shapeBcount_int
}
% clear the sequence in case
\seq_clear:N \l_randomshapes_shapelist_seq
% add as many `shape a` to the sequence as are defined in the first variable
\int_step_inline:nn \l_randomshapes_shapeAcount_int {
\seq_put_right:Nn \l_randomshapes_shapelist_seq { shape~a }
}
% add as many `shape b` to the sequence as are defined in the second variable
\int_step_inline:nn \l_randomshapes_shapeBcount_int {
\seq_put_right:Nn \l_randomshapes_shapelist_seq { shape~b }
}
% add as many `shape c` to the sequence as are defined in the third variable
\int_step_inline:nn \l_randomshapes_shapeCcount_int {
\seq_put_right:Nn \l_randomshapes_shapelist_seq { shape~c }
}
% shuffle the sequence
\seq_shuffle:N \l_randomshapes_shapelist_seq
}
\NewDocumentCommand { \getfromshuffledshapelist } { m } {
% pick index #1 from the sequence
\seq_item:Nn \l_randomshapes_shapelist_seq { #1 }
}
\ExplSyntaxOff
\begin{document}
\begin{tikzpicture}
\createshuffledshapelist{100}{10}{50}{10}{50}
\tikzset{
% define the shapes
shape a/.style={
circle, fill=red
},
shape b/.style={
diamond, fill=red
},
shape c/.style={
circle, fill=white
},
}
\draw[thin, black] (0,0) rectangle (7.75,7.75);
\foreach \r in {1,...,10} { % rows
% calculate the running index as product of row and column (as integer)
\foreach \c [evaluate=\c as \i using int(10*(\r-1)+\c)] in {1,...,10} { % columns
\node[\getfromshuffledshapelist{\i}, draw=black]
at ({\c*0.75-0.25},{0.75*\r-0.25}) {};
}
}
\end{tikzpicture}
\end{document}
答案3
这不是答案,但我需要比评论更多的空间。我想解决你问题的随机部分。你已经对如何创建网格有了很好的答案,特别是从选择多少个形状开始,但所有答案(到目前为止)都是从统一选择这些数字开始的。这就是我想解决的问题。
让我明确说明我的假设,因为如果这些假设是错误的,那么我的分析就会有点偏差。我设想你用三种形状生成所有可能的 10 x10 网格,其中每个位置都有相等的概率成为给定的形状。然后,你从这些网格中丢弃所有不符合标准的网格。现在,你从剩余的图案中均匀地选择一个。
到目前为止,答案将生成相同的网格集(受伪随机限制),但分布不同。每个形状的数量不是均匀分布的,而是遵循三项分布,这类似于二项式,但有三个结果。因此,要使用其他答案的技术,需要选择每个形状的数量以反映三项分布。
幸运的是,您的数字足够大,因此中心极限定理可以发挥作用。因此算法如下:
- 在 (0,1) 上均匀生成两个伪随机数
- 使用 Box-Muller 变换将它们转换为两个正态分布的随机数。
- 将第一个转换为均值为 100×⅓、方差为 100×⅓×⅔ 的正态分布,并四舍五入为最接近的整数。将其称为 n。
- 如果不在 10 到 50 之间,则丢弃。这里可能需要进行一些数学运算来找出正确的初始范围,以保证在正确的范围内,但小于 10 或大于 50 的概率约为 0.02%,所以这里丢弃的可能性非常小。
- 将第二个转换为平均值为 (100-n)×½、方差为 (100-n)×½×½ 的正态分布,并四舍五入到最接近的整数。
- 如果不在要求范围内就丢弃,同样需要丢弃的可能性非常小。
在此,我们用一对二项式模拟三项式,其中第一个二项式是钻石或非钻石,因此概率参数为 ⅓,试验次数为 100。第二个二项式是红色或白色圆圈,概率参数为 ½,试验次数为第一个二项式之后剩余的形状数。
这是一些代码。我已将其与 Juan 和 Jasper 的绘制网格的代码集成在一起(因此,特别是,如果有人投票支持这个答案,那么他们实际上也应该投票支持其他答案,而这不应该是可接受的答案)。两者只需要进行少量修改即可适应使用此算法生成随机数。
\documentclass{article}
%\url{https://tex.stackexchange.com/q/676390/86}
\usepackage{tikz}
\usetikzlibrary{shapes}
\ExplSyntaxOn
\fp_new:N \l__bm_ua_fp
\fp_new:N \l__bm_ub_fp
\fp_new:N \l__bm_r_fp
\fp_new:N \l__bm_th_fp
\fp_new:N \l__bm_xa_fp
\fp_new:N \l__bm_xb_fp
\fp_new:N \g__bm_outputa_fp
\fp_new:N \g__bm_outputb_fp
% Create two normally distributed variables; this uses the trigonometric version of the Box-Muller algorithm.
% This function generates the numbers inside a group and sets two global output registers
\cs_new_protected_nopar:Npn \__bm_normals:
{
\group_begin:
% Uniformly chosen on (0,1)
\fp_set:Nn \l__bm_ua_fp {rand()}
\fp_set:Nn \l__bm_ub_fp {rand()}
% Polar coordinates
\fp_set:Nn \l__bm_r_fp {sqrt(-2*ln(\l__bm_ua_fp))}
\fp_set:Nn \l__bm_th_fp {2*\c_pi_fp*\l__bm_ub_fp}
% Cartesian coordinates
\fp_set:Nn \l__bm_xa_fp {\l__bm_r_fp * cos( \l__bm_th_fp )}
\fp_set:Nn \l__bm_xb_fp {\l__bm_r_fp * sin( \l__bm_th_fp )}
% Export
\fp_gset_eq:NN \g__bm_outputa_fp \l__bm_xa_fp
\fp_gset_eq:NN \g__bm_outputb_fp \l__bm_xb_fp
\group_end:
}
% The next two functions are wrappers around the above to enable the target variables to be set either locally or globally
\cs_new_protected_nopar:Npn \bm_normals:NN #1#2
{
\__bm_normals:
\fp_set_eq:NN #1 \g__bm_outputa_fp
\fp_set_eq:NN #2 \g__bm_outputb_fp
\fp_gzero:N \g__bm_outputa_fp
\fp_gzero:N \g__bm_outputb_fp
}
\cs_new_protected_nopar:Npn \bm_gnormals:NN #1#2
{
\__bm_normals:
\fp_gset_eq:NN #1 \g__bm_outputa_fp
\fp_gset_eq:NN #2 \g__bm_outputb_fp
\fp_gzero:N \g__bm_outputa_fp
\fp_gzero:N \g__bm_outputb_fp
}
% The next functions calculate a trinomial, the structure is the same as above with an auxiliary function to do the work and two wrappers for saving the results into local or global variables
\fp_new:N \l__bm_tmpa_fp
\fp_new:N \l__bm_tmpb_fp
\fp_new:N \l__bm_mu_fp
\fp_new:N \l__bm_sigma_fp
% #1: number of trials
% #2: probability of first outcome
% #3: probability of second outcome
\cs_new_protected_nopar:Npn \__bm_trinomial:nnn #1#2#3
{
\group_begin:
\bm_normals:NN \l__bm_tmpa_fp \l__bm_tmpb_fp
\fp_set:Nn \l__bm_mu_fp { (#1) * (#2) }
\fp_set:Nn \l__bm_sigma_fp { sqrt( (#1) * (#2) * (1 - #2) ) }
\fp_set:Nn \l__bm_tmpa_fp {round(\l__bm_tmpa_fp * \l__bm_sigma_fp + \l__bm_mu_fp)}
\fp_set:Nn \l__bm_mu_fp { (#1 - \l__bm_tmpa_fp) * (#3) / (1 - #2) }
\fp_set:Nn \l__bm_sigma_fp {
sqrt(
(#1 - \l__bm_tmpa_fp)
* (#3) / (1 - #2)
* (1 - #2 - #3) / (1 - #2)
)
}
\fp_set:Nn \l__bm_tmpb_fp {round(\l__bm_tmpb_fp * \l__bm_sigma_fp + \l__bm_mu_fp)}
\fp_gset_eq:NN \g__bm_outputa_fp \l__bm_tmpa_fp
\fp_gset_eq:NN \g__bm_outputb_fp \l__bm_tmpb_fp
\group_end:
}
\cs_new_protected_nopar:Npn \bm_trinomial:NNnnn #1#2#3#4#5
{
\__bm_trinomial:nnn {#3}{#4}{#5}
\fp_set_eq:NN #1 \g__bm_outputa_fp
\fp_set_eq:NN #2 \g__bm_outputb_fp
\fp_gzero:N \g__bm_outputa_fp
\fp_gzero:N \g__bm_outputb_fp
}
\cs_new_protected_nopar:Npn \bm_gtrinomial:NNnnn #1#2#3#4#5
{
\__bm_trinomial:nnn {#3}{#4}{#5}
\fp_gset_eq:NN #1 \g__bm_outputa_fp
\fp_gset_eq:NN #2 \g__bm_outputb_fp
\fp_gzero:N \g__bm_outputa_fp
\fp_gzero:N \g__bm_outputb_fp
}
% This is a user-level function which puts the resulting numbers as "decimals" (though they will be integers) into two macros (the above interal functions set fp registers)
\DeclareDocumentCommand \SetTrinomial {m m m m m}
{
\__bm_trinomial:nnn {#3}{#4}{#5}
\tl_set:Nx #1 {\fp_to_decimal:N \g__bm_outputa_fp}
\tl_set:Nx #2 {\fp_to_decimal:N \g__bm_outputb_fp}
\fp_gzero:N \g__bm_outputa_fp
\fp_gzero:N \g__bm_outputb_fp
}
% This imposes a restriction on the numbers using a rejection method
\cs_new_protected_nopar:Npn \__bm_trinomial_between:nnnnnnn #1#2#3#4#5#6#7
{
\group_begin:
\bool_do_while:nn
{
\fp_compare_p:n {\g__bm_outputa_fp < #4}
||
\fp_compare_p:n {\g__bm_outputa_fp > #5}
||
\fp_compare_p:n {\g__bm_outputb_fp < #6}
||
\fp_compare_p:n {\g__bm_outputb_fp > #7}
}
{
\__bm_trinomial:nnn {#1}{#2}{#3}
}
\group_end:
}
\DeclareDocumentCommand \SetTrinomialBetween {m m m m m m m}
{
\__bm_trinomial_between:nnnnnnn {#3}{#4}{#5}{#6}{#7}{#6}{#7}
\tl_set:Nx #1 {\fp_to_decimal:N \g__bm_outputa_fp}
\tl_set:Nx #2 {\fp_to_decimal:N \g__bm_outputb_fp}
\fp_gzero:N \g__bm_outputa_fp
\fp_gzero:N \g__bm_outputb_fp
}
% Jasper Habicht's code
\int_new:N \l_randomshapes_shapeAcount_int
\int_new:N \l_randomshapes_shapeBcount_int
\int_new:N \l_randomshapes_shapeCcount_int
\seq_new:N \l_randomshapes_shapelist_seq
\NewDocumentCommand { \createshuffledshapelist } { m m m m m } {
%
%%% Start of modification
%
% Generate trinomials between the limits
\__bm_trinomial_between:nnnnnnn {#1}{1/3}{1/3}{#2}{#3}{#4}{#5}
\int_set:Nn \l_randomshapes_shapeAcount_int {\fp_to_int:N \g__bm_outputa_fp}
\int_set:Nn \l_randomshapes_shapeBcount_int {\fp_to_int:N \g__bm_outputb_fp}
%
%%% End of modification
%
% set the third variable to #1 minus the sum of the previous two variables
\int_set:Nn \l_randomshapes_shapeCcount_int {
#1 - \l_randomshapes_shapeAcount_int - \l_randomshapes_shapeBcount_int
}
% clear the sequence in case
\seq_clear:N \l_randomshapes_shapelist_seq
% add as many `shape a` to the sequence as are defined in the first variable
\int_step_inline:nn \l_randomshapes_shapeAcount_int {
\seq_put_right:Nn \l_randomshapes_shapelist_seq { shape~a }
}
% add as many `shape b` to the sequence as are defined in the second variable
\int_step_inline:nn \l_randomshapes_shapeBcount_int {
\seq_put_right:Nn \l_randomshapes_shapelist_seq { shape~b }
}
% add as many `shape c` to the sequence as are defined in the third variable
\int_step_inline:nn \l_randomshapes_shapeCcount_int {
\seq_put_right:Nn \l_randomshapes_shapelist_seq { shape~c }
}
% shuffle the sequence
\seq_shuffle:N \l_randomshapes_shapelist_seq
}
\NewDocumentCommand { \getfromshuffledshapelist } { m } {
% pick index #1 from the sequence
\seq_item:Nn \l_randomshapes_shapelist_seq { #1 }
}
\ExplSyntaxOff
\begin{document}
% Juan Castaño's code
\tikzset
{% styles
shape1/.style={draw,circle,fill=red},
shape2/.style={draw,diamond,fill=red},
shape3/.style={draw,circle,fill=white},
}
%% counters
\newcounter{NumberOfRC} % Number of Red Circles
\newcounter{NumberOfRD} % Number of Red Diamonds
\begin{tikzpicture}
%
%%% Start of modification
%
\SetTrinomialBetween{\NumberOfRC}{\NumberOfRD}{100}{1/3}{1/3}{10}{50}
\setcounter{NumberOfRC}{\NumberOfRC}
\setcounter{NumberOfRD}{\NumberOfRD}
%
%%% End of modification
%
% Just to check the numbers
%\node[right] at (0,-1) {Red circles: \theNumberOfRC};
%\node[right] at (0,-1.5) {Red diamonds: \theNumberOfRD};
\draw [thin, black] (0,0) rectangle (7.75,7.75);
\foreach\i in {1,...,100}
{
\pgfmathsetmacro\x{0.775*(mod(\i-1,10)+0.5)}
\pgfmathsetmacro\y{0.775*(int((\i-1)/10)+0.5)}
\pgfmathtruncatemacro\RandomNumber{random(0,100-\i)}
\pgfmathtruncatemacro\TotalNumberOfRF{\theNumberOfRC+\theNumberOfRD} % Total number of Red Figures
\ifnum\RandomNumber<\theNumberOfRC
\pgfmathsetmacro\shape{1}
\addtocounter{NumberOfRC}{-1}
\else\ifnum\RandomNumber<\TotalNumberOfRF
\pgfmathsetmacro\shape{2}
\addtocounter{NumberOfRD}{-1}
\else
\pgfmathsetmacro\shape{3}
\fi\fi
\node[shape\shape] at (\x,\y) {};
}
\end{tikzpicture}
% Jasper Habicht's code, continued
\begin{tikzpicture}
\createshuffledshapelist{100}{10}{50}{10}{50}
\tikzset{
% define the shapes
shape a/.style={
circle, fill=red
},
shape b/.style={
diamond, fill=red
},
shape c/.style={
circle, fill=white
},
}
\draw[thin, black] (0,0) rectangle (7.75,7.75);
\foreach \r in {1,...,10} { % rows
% calculate the running index as product of row and column (as integer)
\foreach \c [evaluate=\c as \i using int(10*(\r-1)+\c)] in {1,...,10} { % columns
\node[\getfromshuffledshapelist{\i}, draw=black]
at ({\c*0.75-0.25},{0.75*\r-0.25}) {};
}
}
\end{tikzpicture}
\end{document}