Mathematica 中的 While 循环:多个输出

发布于 2024-12-09 17:54:50 字数 875 浏览 1 评论 0原文

我在 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,则生成一个新列表,并且此循环继续,直到 fi 不再为 False。
  5. 调用存储在 f 中的结果。

现在的情况是:这只产生一行输出。我对获得多个输出感兴趣,例如 5-10。我尝试使用 Table 命令以某种方式执行此操作,但问题是没有任何东西可以同时定义函数 f 和 while 循环。因此,通过在 f 上运行 table,我只能多次得到相同的结果。

关于如何继续这里的任何意见?

I have worked on a random number generator in Mathematica, suppressed by a number of conditions. Right now my code looks like this:

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

It is build up like this:

  1. A random list of 7 integers of the interval 1-36 is made
  2. f defines some conditions that have to be met: at least one and at most two elements in the range 1-12. At least one element greater than 31. At most 3 elements can be divisible be the integers in the range 2-7.
  3. If the conditions are met, f equals the list, False otherwise.
  4. Next thing is the "While" loop. If f is False, then a new list i generated, and this loop continues until f i no longer False.
  5. The result stored in f is called.

The thing is now: this only produces one line of output. I am interested in getting multiple outputs, e.g. 5-10. I have tried to do it in some way with the Table command, but the thing is that nothing defines both the function f and the while loop at the same time. So, by running table on f, I only get the same result a lot of times.

Any input on how to proceed here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清风挽心 2024-12-16 17:54:50

我不认为您的 f 定义中的第三行正在做您认为它正在做的事情。例如,考虑

Divisible[20, {2, 7}]

返回 {True, False},而不是 TrueFalse。这意味着
Select[list, Divisible[#, {2, 7}] &] 将始终返回空列表,并且
计数[选择[列表,可除[#, {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]]

I don't think the third line in your definition of f is doing what you think it's doing. Consider for example

Divisible[20, {2, 7}]

which returns {True, False}, not either True or False. This means that
Select[list, Divisible[#, {2, 7}] &] will always return an empty list and
Count[Select[list, Divisible[#, {2, 7}] &], _Integer]
will always return 0.

If I interpret the conditions for the lists correctly, you could instead use something like

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

With this and Alexy's suggestion to use Sow and Reap, you could do something like

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]]
呆头 2024-12-16 17:54:50

函数 flist 引用为自由变量而不是参数。虽然这不是一个不可克服的障碍,但它确实使打包此功能以便在 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}}

The function f references list as a free variable instead of as a parameter. While this is not an insurmountable obstacle, it does make it awkward to package up this functionality so that it can be used in a Table. Let's rework these definitions and apply some simplifications along the way.

First, let's tackle the test as to whether a sample is acceptable:

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]]

The main simplification from the original is that the nested If statements have been flattened into an And condition. The new definition also exploits the fact that Count can test list values without having to invoke a nested Select. In addition, existence checks have been expressed using MemberQ[...]. A helper function has been introduced to perform the divisibility check in an effort to reduce the visual complexity of the main test expression. Note that the original divisibility check was incorrectly returning a list where a boolean value was expected. The _Integer head tests were removed, but if they are deemed desirable they can be re-introduced by changing each n_ to n_Integer.

Now we just need a way to generate samples in a loop until an acceptable one is found:

generateSample[] :=
  While[
    True
  , RandomSample[Range[36], 7] /.
      s_ :> If[acceptableQ[s], Return[Sort @ s]]
  ]

generateSample[] can now be used to generate a table of as many results as needed:

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}}

Generalizing the Pattern

The pattern embodied in generateSample could be parameterized to accept arbitrary generator and filter functions:

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

The generator argument is kept in unevaluated form so that can be evaluated afresh on each pass through the loop. This new function can be used thus:

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}}

The advantage of the new function is that it can be used with any generator and filter functions. Here we generate tuples of three integers that sum to seven.

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}}

As a matter of style, some prefer to avoid defining functions with Hold attributes unless absolutely necessary. generatorSelect2 reflects that design choice:

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

The only difference between this and generatorSelect is that the first argument is now expected to evaluate to a function:

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}}
遇到 2024-12-16 17:54:50

为此,您可以使用 ReapSow

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

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

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

You can use Reap and Sow for this:

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

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

I recommend also to look at the NestWhileList: it may be found very appropriate for your needs.

找回味觉 2024-12-16 17:54:50

这工作正常。请注意使用 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}]

This works ok. Note the use of SameQ (===) for comparing possibly mixed types, lists and booleans. E.g. {4, 7, 17, 22, 25, 27, 34} == False does not evaluate.

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}]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文