Цикл while в Mathematica: несколько выходов - PullRequest
2 голосов
/ 15 октября 2011

Я работал над генератором случайных чисел в 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. Произведен случайный список из 7 целых чисел в интервале 1-36
  2. f определяет некоторые условия, которые должны быть выполнены: как минимум один и максимум два элемента в диапазоне 1-12.По крайней мере, один элемент больше 31. Максимум 3 элемента могут делиться на целые числа в диапазоне 2-7.
  3. Если выполнены условия, f равен списку, иначе False.
  4. Следующим шагом является цикл «Пока».Если f - False, то новый список, который я сгенерировал, и этот цикл продолжается до тех пор, пока fi не перестанет быть False.
  5. Вызывается результат, сохраненный в f.

Теперь все так:это производит только одну строку вывода.Я заинтересован в получении нескольких выходов, например, 5-10.Я пытался сделать это каким-то образом с помощью команды Table, но дело в том, что ничто не определяет одновременно функцию f и цикл while.Итак, запустив таблицу на f, я получаю один и тот же результат много раз.

Есть какие-либо сведения о том, как действовать здесь?

Ответы [ 4 ]

4 голосов
/ 15 октября 2011

Я не думаю, что третья строка в вашем определении f делает то, что вы думаете, она делает.Рассмотрим, например,

Divisible[20, {2, 7}]

, который возвращает {True, False}, а не True или False.Это означает, что Select[list, Divisible[#, {2, 7}] &] всегда будет возвращать пустой список, а Count[Select[list, Divisible[#, {2, 7}] &], _Integer] всегда будет возвращать 0.

Если я правильно интерпретирую условия для списков, вы можете вместо этого использовать что-то вроде

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

С этим и предложением Алекси использовать Sow и Reap, вы могли бы сделать что-токак

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]]
3 голосов
/ 15 октября 2011

Функция 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 head были удалены, но если они считаются желательными, их можно ввести заново, изменив каждый 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 голосов
/ 15 октября 2011

Это работает нормально.Обратите внимание на использование 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}]
2 голосов
/ 15 октября 2011

Вы можете использовать Reap и Sow для этого:

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

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

Я рекомендую также взглянуть на NestWhileList: он может оказаться очень подходящим для ваших нужд.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...