Как сделать логические тесты для всех элементов списка в Mathematica - PullRequest
9 голосов
/ 15 декабря 2011

У меня есть список, и я хочу применить логический тест к каждому элементу, и если какой-либо из них не удовлетворяет этому условию, вернуть false.Я хочу написать это в Mathematica или найти встроенную функцию, но, похоже, ForAll на самом деле этого не делает.

Мой вопрос: как сделать это наиболее эффективно?

Бонус: как примерно так же для функции Exists: то есть, если какой-либо элемент в списке удовлетворяет условию, верните true

Ответы [ 7 ]

9 голосов
/ 15 декабря 2011

Ответ на первую часть вашего вопроса может быть следующим:

forAll[list_, cond_] := Select[list, ! cond@# &, 1] === {};

, который используется так:

forAll[{1, 2, 3, 3.5}, IntegerQ]

Функция «существует» уже реализована как MemberQ. Это может быть переопределено как:

exists[list_,cond_] := Select[list, cond, 1] =!= {};

Используйте это как

exists[Range@100, (10 == # &)]

, который возвращает true, поскольку 10 является элементом, вызывающим Select, чтобы возвратить {10}, который не равен {}.

8 голосов
/ 15 декабря 2011

Этот ответ не предназначен для демонстрации наиболее эффективного метода, а скорее является альтернативным методом, который служит педагогической цели для демонстрации некоторых важных основных функций в Mathematica.

Ответ nixeagle позволяет избежать явного тестирования каждого элемента списка.Если тест не поддается включению в третий аргумент Select, то может быть полезно следующее:

Для этого вам нужно узнать о стандарте *Функции 1012 *Or и And, а также Map (/@) и Apply (@@) команд, которые чрезвычайно важны для обучения любому пользователю Mathematica.(см. этот учебник )

Вот простой пример.

In[2]:= data = RandomInteger[{0, 10}, {10}]

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

In[4]:= # > 5 & /@ data

Out[4]= {True, False, False, True, False, False, False, False, False, \
False}

In[6]:= And @@ (# > 5 & /@ data)

Out[6]= False

Здесь происходит то, что вы отображаете функцию («больше 5»)) к каждому элементу списка, используя Map, чтобы получить список значений True / False.Затем вы применяете стандартную логическую функцию And ко всему списку, чтобы получить единственное логическое значение.

Это все основные функции в Mathematica, и я рекомендую вам внимательно прочитать документацию по этим функциям и попрактиковатьсяих использование.

Это не самый эффективный метод, но для небольших проблем вы не заметите разницу.

In[11]:= Do[Select[data, ! # > 5 &, 1] === {}, {10000}] // Timing

Out[11]= {0.031, Null}

In[12]:= Do[And @@ (# > 5 & /@ data);, {10000}] // Timing

Out[12]= {0.11, Null}

Для Exists альтернативой Select будет MatchQ для шаблонов или MemberQ для явных значений.В документации есть несколько полезных примеров.

4 голосов
/ 15 декабря 2011

Подход, основанный на шаблонах:

forAll[list_, test_] := MatchQ[ list, _[__?test] ]

MemberQ уже существует.


Mathematica 10 имеет новую функцию для этого: AllTrue. Когда все элементы проходят тест, моя функция выглядит немного быстрее:

a = Range[2, 1*^7, 2];

AllTrue[a, EvenQ] // Timing // First
forAll[a, EvenQ]  // Timing // First
1.014007

0.936006

Однако при досрочном выходе выгода новой функции становится очевидной:

a[[123456]] = 1;

AllTrue[a, EvenQ] // Timing // First
forAll[a, EvenQ]  // Timing // First
0.031200

0.265202
4 голосов
/ 15 декабря 2011

Не следует воспринимать слишком серьезно, но это

ClearAll[existsC];
existsC[cond_] := With[
  {c = cond},
  Compile[
   {{l, _Integer, 1}},
   Module[
    {flag = False, len = Length@l},
    Do[
     If[cond[l[[i]]],
       flag = True; Break[];
       ];,
     {i, 1, len}];
    flag
    ],
   CompilationTarget -> "C"
   ]
  ]

примерно в 300 раз быстрее, чем решения nixeagle на моей машине. Для этого нужно сгенерировать скомпилированную функцию, которая берет список и сравнивает его элементы с заданным условием (фиксированным во время компиляции), возвращая True, если какой-либо из них соответствует.

Используется следующим образом: Компилировать с соответствующими cond, например,

t = existsC[# == 99999 &];

, а затем

t[Range@100000] // timeIt

возвращает 2.33376*10^-6 (наихудший сценарий, поскольку я просто ищу линейно, а соответствующий элемент находится в конце), а

exists[Range@100000, (99999 == # &)] // timeIt

возвращает 0.000237162 (здесь timeIt - это это ).

1 голос
/ 15 декабря 2011

Несмотря на то, что && и || выполняют оценку короткого замыкания, т. Е. Не оценивают их аргументы без необходимости, я подозреваю, что решения, основанные на Select[] или Map[], от этого не выиграют.Это потому, что они применяют логический тест к каждому элементу, создавая список логических значений истинности перед тем, как выполнит соединение / дизъюнкцию между ними.Если указанный вами тест медленный, он может стать настоящим узким местом.

Так что вот вариант, который также выполняет оценку состояния при коротком замыкании:

allSatisfy[list_, cond_] :=
   Catch[Fold[If[cond[#2], True, Throw[False]] &, True, list]]

Тестированиеесли какой-либо элемент в списке удовлетворяет условию, он хорошо симметричен:

anySatisfy[list_, cond_] := 
   Catch[Fold[If[cond[#2], Throw[True], False] &, False, list]]

Конечно, это можно было бы сделать (и, честно говоря, проще) с использованием процедурных циклов, таких как While[], но у меня естьслабое место для функционального программирования!

0 голосов
/ 01 августа 2014

Есть простое решение:

In[1401]:= a = {1, 2, 3}

Out[1401]= {1, 2, 3}

In[1398]:= Boole[Thread[a[[2]] == a]]

Out[1398]= {0, 1, 0}

In[1400]:= Boole[Thread[a[[2]] >= a]]

Out[1400]= {1, 1, 0}

In[1402]:= Boole[Thread[a[[2]] != a]]

Out[1402]= {1, 0, 1}

Успех!

0 голосов
/ 15 декабря 2011

nixeagle получил бонусную часть, но я бы выполнил первую часть следующим образом:

AllSatisfy[expr_, cond_] := Length@Select[expr, cond] == Length@expr
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...