在Mathematica中循环:多个输出

时间:2011-10-15 11:03:14

标签: loops random while-loop wolfram-mathematica

我曾在Mathematica中使用随机数生成器,并受到许多条件的抑制。现在我的代码看起来像这样:

list = RandomSample[Range[36], 7];
f := If[1 <= Count[Select[list, # <= 12 &], _Integer] <= 2,
  If[Count[Select[list, # > 31 &], _Integer] >= 1,
   If[Count[Select[list, Divisible[#, {2, 7}] &], _Integer] <= 3, 
    Sort[list], False], False], False]
While[f == False,
  list = RandomSample[Range[36], 7];
  If[list == f, f]];
f

它是这样构建的:

  1. 制作间隔1-36的7个整数的随机列表
  2. f定义了必须满足的一些条件:1-12范围内的至少一个且最多两个元素。至少有一个元素大于31.最多3个元素可以被2-7范围内的整数整除。
  3. 如果满足条件,则f等于列表,否则为False。
  4. 接下来是“While”循环。如果f为False,则生成一个新列表,并且此循环继续,直到f i不再为False。
  5. 调用存储在f中的结果。
  6. 现在的事情是:这只产生一行输出。我有兴趣获得多个输出,例如5-10。我试图用Table命令以某种方式做到这一点,但问题是没有任何同时定义函数f和while循环。因此,通过在f上运行表格,我只会多次得到相同的结果。

    有关如何进行此处的任何输入?

4 个答案:

答案 0 :(得分:4)

我不认为f定义中的第三行正在按照您的想法行事。考虑例如

Divisible[20, {2, 7}]

返回{True, False},而不是TrueFalse。这意味着 Select[list, Divisible[#, {2, 7}] &]将始终返回一个空列表 Count[Select[list, Divisible[#, {2, 7}] &], _Integer] 将始终返回0

如果我正确解释列表的条件,您可以使用类似

的内容
Count[Select[list, Or @@ Divisible[#, Range[2, 7]] &], _Integer] <= 3

有了这个和Alexy建议使用SowReap,你可以做类似的事情

f[list_] := And[
  1 <= Count[Select[list, # <= 12 &], _Integer] <= 2, 
  Count[Select[list, # > 31 &], _Integer] >= 1, 
  Count[Select[list, Or @@ Divisible[#, Range[2, 7]] &], _Integer] <= 3]

Block[{n = 0, list},
  Reap[While[n < 5, list = Sort@RandomSample[Range[36], 7]; 
    If[f[list], n++; Sow[list]]]]][[2, 1]]

答案 1 :(得分:3)

函数f引用list作为自由变量而不是参数。虽然这不是一个不可逾越的障碍,但它确实使得打包这个功能变得尴尬,以便它可以在Table中使用。让我们重新修改这些定义,并在此过程中应用一些简化。

首先,让我们来测试样品是否可以接受:

acceptableQ[sample_] :=
  MemberQ[sample, n_ /; n > 31] &&
  1 <= Count[sample, n_ /; n <= 12] <= 2 &&
  Count[sample, n_ /; divisible2to7[n]] <= 3

divisible2to7[n_] := MemberQ[Range[2, 7], d_ /; Divisible[n, d]]

原始的主要简化是嵌套的If语句已被展平为And条件。新定义还利用Count可以测试列表值而无需调用嵌套Select的事实。此外,使用MemberQ[...]表达了存在性检查。引入了辅助函数来执行可除性检查,以减少主要测试表达式的视觉复杂性。请注意,原始可分性检查错误地返回了一个预期布尔值的列表。已移除_Integer头部测试,但如果认为需要,可以通过将每个n_更改为n_Integer来重新引入这些测试。

现在我们只需要一种在循环中生成样本的方法,直到找到可接受的样本:

generateSample[] :=
  While[
    True
  , RandomSample[Range[36], 7] /.
      s_ :> If[acceptableQ[s], Return[Sort @ s]]
  ]
现在可以使用

generateSample[]生成一个包含所需结果的表格:

In[113]:= Table[generateSample[], {5}]

Out[113]= {{6, 13, 17, 19, 25, 29, 33}, {1, 11, 13, 15, 31, 35, 36},
           {1, 10, 17, 23, 25, 31, 32}, {1, 6, 17, 19, 22, 23, 33},
           {8, 17, 19, 23, 30, 31, 36}}

概括模式

generateSample中体现的模式可以参数化,以接受任意生成器和过滤器函数:

SetAttributes[generatorSelect, HoldFirst]
generatorSelect[generator_, predicate_] :=
  While[True, generator /. s_ :> If[predicate[s], Return[s]]]

generator参数以未评估的形式保存,以便可以在每次循环中重新评估。因此可以使用这个新功能:

In[114]:= Table[
            generatorSelect[RandomSample[Range[36], 7], acceptableQ] // Sort
          , {5}
          ]

Out[114]= {{9, 17, 19, 23, 27, 29, 32}, {8, 13, 17, 19, 22, 23, 35},
           {4, 17, 19, 21, 23, 29, 36}, {1, 8, 15, 19, 23, 31, 33},
           {1, 10, 17, 19, 24, 29, 36}}

新功能的优点是它可以与任何发生器和滤波器功能一起使用。这里我们生成三个整数的元组,总计为七个。

In[115]:= Table[
            generatorSelect[RandomInteger[7, 3], Total[#] == 7 &]
          , {5}
          ]

Out[115]= {{2, 3, 2}, {0, 5, 2}, {5, 0, 2}, {2, 4, 1}, {2, 1, 4}}

作为一种风格问题,有些人宁愿避免使用Hold属性定义函数,除非绝对必要。 generatorSelect2反映了设计选择:

generatorSelect2[generator_, predicate_] :=
  While[True, generator[] /. s_ :> If[predicate[s], Return[s]]]

这和generatorSelect之间的唯一区别是第一个参数现在应该计算为函数:

In[116]:= Table[
            generatorSelect2[RandomInteger[7, 3] &, Total[#] == 7 &]
          , {5}
          ]

Out[116]= {{5, 1, 1}, {3, 0, 4}, {0, 1, 6}, {3, 2, 2}, {4, 1, 2}}

答案 2 :(得分:2)

您可以使用ReapSow

n = 1; Last@Last@Reap@While[n < 4, Sow[n++]]

(*=> {1, 2, 3}*)

我还建议您查看NestWhileList:它可能非常适合您的需求。

答案 3 :(得分:2)

这没关系。注意使用SameQ(===)来比较可能的混合类型,列表和布尔值。例如。 {4, 7, 17, 22, 25, 27, 34} == False未评估。

f := If[1 <= Count[Select[list, # <= 12 &], _Integer] <= 2, 
  If[Count[Select[list, # > 31 &], _Integer] >= 1, 
   If[Count[Select[list, Divisible[#, {2, 7}] &], _Integer] <= 3, 
    Sort[list], False], False], False]

g := (list = RandomSample[Range[36], 7];
  While[f === False, list = RandomSample[Range[36], 7];
   If[list === f, f]];
  f)

Table[g, {9}]