在 tikz 中绘制(随机选择的)形状网格

在 tikz 中绘制(随机选择的)形状网格

我想制作一个 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 网格,其中每个位置都有相等的概率成为给定的形状。然后,你从这些网格中丢弃所有不符合标准的网格。现在,你从剩余的图案中均匀地选择一个。

到目前为止,答案将生成相同的网格集(受伪随机限制),但分布不同。每个形状的数量不是均匀分布的,而是遵循三项分布,这类似于二项式,但有三个结果。因此,要使用其他答案的技术,需要选择每个形状的数量以反映三项分布。

幸运的是,您的数字足够大,因此中心极限定理可以发挥作用。因此算法如下:

  1. 在 (0,1) 上均匀生成两个伪随机数
  2. 使用 Box-Muller 变换将它们转换为两个正态分布的随机数。
  3. 将第一个转换为均值为 100×⅓、方差为 100×⅓×⅔ 的正态分布,并四舍五入为最接近的整数。将其称为 n。
  4. 如果不在 10 到 50 之间,则丢弃。这里可能需要进行一些数学运算来找出正确的初始范围,以保证在正确的范围内,但小于 10 或大于 50 的概率约为 0.02%,所以这里丢弃的可能性非常小。
  5. 将第二个转换为平均值为 (100-n)×½、方差为 (100-n)×½×½ 的正态分布,并四舍五入到最接近的整数。
  6. 如果不在要求范围内就丢弃,同样需要丢弃的可能性非常小。

在此,我们用一对二项式模拟三项式,其中第一个二项式是钻石或非钻石,因此概率参数为 ⅓,试验次数为 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}

随机网格

相关内容