PDL :: Threadingp - Интернет в облаке

Это команда PDL :: Threadingp, которую можно запустить в бесплатном хостинг-провайдере OnWorks, используя одну из наших многочисленных бесплатных онлайн-рабочих станций, таких как Ubuntu Online, Fedora Online, онлайн-эмулятор Windows или онлайн-эмулятор MAC OS.

ПРОГРАММА:

ИМЯ


PDL :: Threading - Учебное пособие по функции потоковой передачи PDL

ВВЕДЕНИЕ


Одна из самых мощных функций PDL - это нарезания резьбы, который может производить очень компактные и
очень быстрый код PDL, избегая множественных вложенных циклов for, которые могут быть у пользователей C и BASIC
знаком с. Проблема в том, что к этому нужно привыкнуть, а новые пользователи могут не
оцените преимущества многопоточности.

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

ТЕРМИНОЛОГИЯ: ПИДДЛ


MATLAB обычно относится к векторам, матрицам и массивам. В Perl уже есть массивы, а
термины «вектор» и «матрица» обычно относятся к одно- и двумерным коллекциям
данные. Не имея подходящего термина для описания своего объекта, разработчики PDL придумали термин
"лужа"чтобы дать имя их типу данных.

A лужа состоит из ряда чисел, организованных как N-мерный набор данных. Piddles
обеспечивают эффективное хранение и быстрое вычисление больших N-мерных матриц. Они есть
оптимизирован для числовой работы.

МЫШЛЕНИЕ IN СРОКИ OF НИТИ


Если вы уже какое-то время использовали PDL, возможно, вы использовали потоки без
понимая это. Запустите оболочку PDL (введите на терминале «perldl» или «pdl2»). Большинство примеров
в этом руководстве используйте оболочку PDL. Убедитесь, что PDL :: NiceSlice и PDL :: AutoLoader
включен. Например:

% пдл2
оболочка perlDL v1.352
...
ReadLines, NiceSlice, MultiLines включены
...
Примечание. Автозагрузчик не включен (рекомендуется использовать PDL :: AutoLoader)

pdl>

В этом примере NiceSlice был включен автоматически, а автозагрузчик - нет. Включить
введите "использовать PDL :: AutoLoader".

Начнем с двухмерного лужа:

pdl> $ a = последовательность (11,9)
pdl> p $ a
[
[0 1 2 3 4 5 6 7 8 9 10]
[11 12 13 14 15 16 17 18 19 20 21]
[22 23 24 25 26 27 28 29 30 31 32]
[33 34 35 36 37 38 39 40 41 42 43]
[44 45 46 47 48 49 50 51 52 53 54]
[55 56 57 58 59 60 61 62 63 64 65]
[66 67 68 69 70 71 72 73 74 75 76]
[77 78 79 80 81 82 83 84 85 86 87]
[88 89 90 91 92 93 94 95 96 97 98]
]

Метод «info» дает вам основную информацию о лужа:

pdl> p $ a-> информация
PDL: двойной D [11,9]

Это говорит нам, что $ a - это 11 x 9 лужа состоит из чисел двойной точности. Если мы
хотел добавить 3 ко всем элементам в piddle "nxm", традиционный язык использовал бы два
вложенные циклы for:

# Псевдокод. Традиционный способ добавления 3 в массив.
for (x = 0; x <n; x ++) {
for (y = 0; y <m; y ++) {
а (х, у) = а (х, у) + 3
}
}

Внимание: Обратите внимание, что индексы начинаются с 0, как в Perl, C и Java (и в отличие от MATLAB и IDL).

Но с PDL мы можем просто написать:

pdl> $ b = $ a + 3
pdl> p $ b
[
[3 4 5 6 7 8 9 10 11 12 13]
[14 15 16 17 18 19 20 21 22 23 24]
[25 26 27 28 29 30 31 32 33 34 35]
[36 37 38 39 40 41 42 43 44 45 46]
[47 48 49 50 51 52 53 54 55 56 57]
[58 59 60 61 62 63 64 65 66 67 68]
[69 70 71 72 73 74 75 76 77 78 79]
[80 81 82 83 84 85 86 87 88 89 90]
[91 92 93 94 95 96 97 98 99 100 101]
]

Это простейший пример многопоточности, и это то, что все числовое программное обеспечение
инструменты делают. Операция «+ 3» автоматически применялась по двум измерениям. Теперь предположим
вы хотите вычесть строку из каждой строки в $ a:

pdl> $ line = последовательность(11)
pdl> p $ line
[0 1 2 3 4 5 6 7 8 9 10]
pdl> $ c = $ a - $ строка
pdl> p $ c
[
[0 0 0 0 0 0 0 0 0 0 0]
[11 11 11 11 11 11 11 11 11 11 11]
[22 22 22 22 22 22 22 22 22 22 22]
[33 33 33 33 33 33 33 33 33 33 33]
[44 44 44 44 44 44 44 44 44 44 44]
[55 55 55 55 55 55 55 55 55 55 55]
[66 66 66 66 66 66 66 66 66 66 66]
[77 77 77 77 77 77 77 77 77 77 77]
[88 88 88 88 88 88 88 88 88 88 88]
]

Здесь следует отметить два момента: во-первых, значение $ a остается прежним. Попробуйте "p $ a", чтобы проверить.
Во-вторых, PDL автоматически вычитал $ line из каждой строки в $ a. Почему это произошло? Давайте
посмотрите на размеры $ a, $ line и $ c:

pdl> p $ line-> info => PDL: Double D [11]
pdl> p $ a-> info => PDL: Double D [11,9]
pdl> p $ c-> info => PDL: Double D [11,9]

Итак, и $ a, и $ line имеют одинаковое количество элементов в 0-м измерении! Какой тогда PDL
did проходил через более высокие измерения в $ a и повторял ту же операцию 9 раз, чтобы
все строки на $ a. Это поток PDL в действии.

Что, если вы хотите вычесть $ line только из первой строки в $ a? Вы можете сделать это
явно указав строку:

pdl> $ a (:, 0) - = $ строка
pdl> p $ a
[
[0 0 0 0 0 0 0 0 0 0 0]
[11 12 13 14 15 16 17 18 19 20 21]
[22 23 24 25 26 27 28 29 30 31 32]
[33 34 35 36 37 38 39 40 41 42 43]
[44 45 46 47 48 49 50 51 52 53 54]
[55 56 57 58 59 60 61 62 63 64 65]
[66 67 68 69 70 71 72 73 74 75 76]
[77 78 79 80 81 82 83 84 85 86 87]
[88 89 90 91 92 93 94 95 96 97 98]
]

См. PDL :: Indexing и PDL :: NiceSlice, чтобы узнать больше об указании подмножеств из piddles.

Истинная сила потоковой передачи проявляется, когда вы понимаете, что у piddle может быть любое количество
Габаритные размеры! Создадим четырехмерный пиддл:

pdl> $ piddle_4D = последовательность (11,3,7,2)
pdl> $ c = $ piddle_4D - $ строка

Теперь $ c - это piddle того же размера, что и $ piddle_4D.

pdl> p $ piddle_4D-> info => PDL: Double D [11,3,7,2]
pdl> p $ c-> info => PDL: Double D [11,3,7,2]

На этот раз PDL автоматически распределяет потоки по трем высшим измерениям, вычитая $ line.
весь путь.

Но, возможно, вы хотите вычитать не из строк (размер 0), а из столбцов
(размер 1). Как мне вычесть столбец чисел из каждого столбца в $ a?

pdl> $ cols = последовательность(9)
pdl> p $ a-> info => PDL: Double D [11,9]
pdl> p $ cols-> info => PDL: Double D [9]

Естественно, мы не можем просто набрать «$ a - $ cols». Размеры не совпадают:

pdl> p $ a - $ cols
PDL: PDL :: Ops :: minus (a, b, c): параметр 'b'
PDL: Несоответствующий размер неявной резьбы 0: должно быть 11, равно 9

Как сообщить PDL, что вместо этого мы хотим вычесть из измерения 1?

МАНИПУЛЯЦИЯ РАЗМЕРЫ


Существует множество функций PDL, которые позволяют изменять размеры массивов PDL. Они есть
в основном рассматривается в PDL :: Slices. Три самых распространенных:

хчг
mv
Изменение порядка

Метод: "xchg"
Метод "xchg" "обмен«два измерения в корне:

pdl> $ a = последовательность (6,7,8,9)
pdl> $ a_xchg = $ a-> xchg (0,3)

pdl> p $ a-> info => PDL: Double D [6,7,8,9]
pdl> p $ a_xchg-> info => PDL: Двойное D [9,7,8,6]
| |
В.В.
(тусклый 0) (тусклый 3)

Обратите внимание, что размеры 0 и 3 были поменяны местами, не влияя на другие измерения.
Также обратите внимание, что «xchg» не изменяет $ a. Исходная переменная $ a остается нетронутой.

Метод: "мв"
Метод "mv" "движется"одно измерение в пустоте, сдвигая другие измерения как
необходимо.

pdl> $ a = последовательность (6,7,8,9) (тусклый 0)
pdl> $ a_mv = $ a-> mv (0,3) |
pdl> V _____
pdl> p $ a-> info => PDL: Double D [6,7,8,9]
pdl> p $ a_mv-> info => PDL: Двойное D [7,8,9,6]
----- |
V
(тусклый 3)

Обратите внимание, что когда размер 0 был перемещен в позицию 3, все остальные измерения должны были быть
тоже сдвинулся. Также обратите внимание, что «mv» не изменяет $ a. Исходная переменная $ a остается
нетронутый.

Метод: "Изменение порядка"
Метод «переупорядочения» является обобщением методов «xchg» и «mv». Это "переупорядочивает"
размеры любым указанным вами способом:

pdl> $ a = последовательность (6,7,8,9)
pdl> $ a_reorder = $ a-> переупорядочить (3,0,2,1)
pdl>
pdl> p $ a-> info => PDL: Double D [6,7,8,9]
pdl> p $ a_reorder-> info => PDL: Double D [9,6,8,7]
| | | |
VV v V
размеры: 0 1 2 3

Обратите внимание на то, что произошло. Когда мы написали «reorder (3,0,2,1)», мы проинструктировали PDL:

* Сначала укажите размер 3.
* Далее поставить размер 0.
* Далее поставить размер 2.
* Далее поставить размер 1.

Когда вы используете метод «переупорядочивания», все измерения перемешиваются. Обратите внимание, что "переупорядочить"
не изменяет $ a. Исходная переменная $ a остается нетронутой.

ПОПАЛСЯ: СВЯЗЬ VS ЗАДАНИЕ


Связывающий
По умолчанию piddles связанный вместе так что изменения на одном вернутся и повлияют на
оригинал as хорошо.

pdl> $ a = последовательность (4,5)
pdl> $ a_xchg = $ a-> xchg (1,0)

Здесь $ a_xchg is a отдельный объект. Это просто другой взгляд на $ a. Любой
изменение в $ a_xchg также появится в $ a.

pdl> p $ a
[
[0 1 2 3]
[4 5 6 7]
[8 9 10 11]
[12 13 14 15]
[16 17 18 19]
]
pdl> $ a_xchg + = 3
pdl> p $ a
[
[3 4 5 6]
[7 8 9 10]
[11 12 13 14]
[15 16 17 18]
[19 20 21 22]
]

Назначение
Иногда связывание - это не то поведение, которое вам нужно. Если вы хотите, чтобы лужи
независимые, воспользуйтесь методом «копирования»:

pdl> $ a = последовательность (4,5)
pdl> $ a_xchg = $ a-> копия-> xchg (1,0)

Теперь $ a и $ a_xchg - полностью отдельные объекты:

pdl> p $ a
[
[0 1 2 3]
[4 5 6 7]
[8 9 10 11]
[12 13 14 15]
[16 17 18 19]
]
pdl> $ a_xchg + = 3
pdl> p $ a
[
[0 1 2 3]
[4 5 6 7]
[8 9 10 11]
[12 13 14 15]
[16 17 18 19]
]
pdl> $ a_xchg
[
[3 7 11 15 19]
[4 8 12 16 20]
[5 9 13 17 21]
[6 10 14 18 22]
]

ЗАПУСК IT BCE ВМЕСТЕ


Теперь мы готовы решить проблему, которая мотивировала всю эту дискуссию:

pdl> $ a = последовательность (11,9)
pdl> $ cols = последовательность(9)
pdl>
pdl> p $ a-> info => PDL: Double D [11,9]
pdl> p $ cols-> info => PDL: Double D [9]

Как мы скажем PDL вычесть $ cols по измерению 1 вместо измерения 0? В
Самый простой способ - использовать метод "xchg" и полагаться на связывание PDL:

pdl> p $ a
[
[0 1 2 3 4 5 6 7 8 9 10]
[11 12 13 14 15 16 17 18 19 20 21]
[22 23 24 25 26 27 28 29 30 31 32]
[33 34 35 36 37 38 39 40 41 42 43]
[44 45 46 47 48 49 50 51 52 53 54]
[55 56 57 58 59 60 61 62 63 64 65]
[66 67 68 69 70 71 72 73 74 75 76]
[77 78 79 80 81 82 83 84 85 86 87]
[88 89 90 91 92 93 94 95 96 97 98]
]
pdl> $ a-> xchg (1,0) - = $ cols
pdl> p $ a
[
[0 1 2 3 4 5 6 7 8 9 10]
[10 11 12 13 14 15 16 17 18 19 20]
[20 21 22 23 24 25 26 27 28 29 30]
[30 31 32 33 34 35 36 37 38 39 40]
[40 41 42 43 44 45 46 47 48 49 50]
[50 51 52 53 54 55 56 57 58 59 60]
[60 61 62 63 64 65 66 67 68 69 70]
[70 71 72 73 74 75 76 77 78 79 80]
[80 81 82 83 84 85 86 87 88 89 90]
]

Общая стратегия:
Переместите измерения, с которыми вы хотите работать, в начало измерения вашего piddle
список. Затем позвольте PDL пройти через более высокие измерения.

Пример: КОНВЕЯ ИГРЫ OF LIFE


Ладно, хватит теории. Сделаем что-нибудь поинтереснее: напишем Конвея Игра
of ЖИЗНЬЮ в PDL и посмотрите, насколько мощным может быть PDL!

The Игра of ЖИЗНЬЮ представляет собой симуляцию на большой двумерной сетке. Каждая ячейка в сетке
может быть живым или мертвым (обозначается 1 или 0). Следующее поколение клеток в
сетка рассчитывается по простым правилам в зависимости от количества живых клеток в ней.
непосредственное соседство:

1) Если пустая ячейка имеет ровно трех соседей, создается живая ячейка.

2) Если у живой клетки меньше двух соседей, она погибает от перекармливания.

3) Если у живой клетки 4 и более соседей, она умирает от голода.

Программист определяет только первое поколение ячеек. После этого
симуляция выполняется полностью в соответствии с этими правилами. Чтобы рассчитать следующее поколение, вы
нужно посмотреть на каждую ячейку в 2D-поле (требуется две петли), вычислить количество
живые клетки, прилегающие к этой ячейке (требующие еще две петли), а затем заполните следующую
сетка генерации.

Классический реализация
Вот классический способ написания этой программы на Perl. Мы используем только PDL для адресации
отдельные клетки.

#!/ usr / bin / perl -w
использовать PDL;
используйте PDL :: NiceSlice;

# Сделайте доску для игры в жизнь.
мой $ nx = 20;
мой $ ny = 20;

# Текущее поколение.
my $ a = нули ($ nx, $ ny);

# Следующее поколение.
мой $ n = нули ($ nx, $ ny);

# Вставьте простой планер.
$ a (1: 3,1: 3). = pdl ([1,1,1],
[0,0,1],
[0,1,0]);

for (my $ i = 0; $ i <100; $ i ++) {
$ n = нули ($ nx, $ ny);
$ new_a = $ a-> копировать;
for ($ x = 0; $ x <$ nx; $ x ++) {
for ($ y = 0; $ y <$ ny; $ y ++) {

# Для каждой ячейки посмотрите на окружающих соседей.
for ($ dx = -1; $ dx <= 1; $ dx ++) {
for ($ dy = -1; $ dy <= 1; $ dy ++) {
$ px = $ x + $ dx;
$ py = $ y + $ dy;

# Оберните по краям.
если ($ px <0) {$ px = $ nx-1};
если ($ py <0) {$ py = $ ny-1};
если ($ px> = $ nx) {$ px = 0};
если ($ py> = $ ny) {$ py = 0};

$ n ($ x, $ y). = $ n ($ x, $ y) + $ a ($ px, $ py);
}
}
# Не считайте центральную ячейку.
$ n ($ x, $ y) - = $ a ($ x, $ y);

# Определите, живет ли клетка или умирает:
# Мертвая клетка живет, если n = 3
# Живая клетка умирает, если n не равно 2 или 3
if ($ a ($ x, $ y) == 1) {
если ($ n ($ x, $ y) <2) {$ new_a ($ x, $ y). = 0};
если ($ n ($ x, $ y)> 3) {$ new_a ($ x, $ y). = 0};
} Еще {
if ($ n ($ x, $ y) == 3) {$ new_a ($ x, $ y). = 1}
}
}
}

print $ a;

$ a = $ new_a;
}

Если вы запустите это, вы увидите, как маленький планер ползет по диагонали по сетке нулей.
На моей машине он распечатывает пару поколений в секунду.

С резьбой PDL реализация
А вот и многопоточная версия в PDL. Всего четыре строки кода PDL, и одна из них
распечатка последнего поколения!

#!/ usr / bin / perl -w
использовать PDL;
используйте PDL :: NiceSlice;

мой $ a = нули (20,20);

# Вставьте простой планер.
$ a (1: 3,1: 3). = pdl ([1,1,1],
[0,0,1],
[0,1,0]);

мой $ n;
for (my $ i = 0; $ i <100; $ i ++) {
# Подсчитать количество соседей на ячейку.
$ n = $ a-> range (ndcoords ($ a) -1,3, «периодический») -> изменить порядок (2,3,0,1);
$ n = $ n-> сумовер-> сумовер - $ a;

# Рассчитайте следующее поколение.
$ a = ((($ n == 2) + ($ n == 3)) * $ a) + (($ n == 3) *! $ a);

print $ a;
}

Многопоточная версия PDL намного быстрее:

Классический => 32.79 секунды.
Резьба => 0.41 секунды.

объяснение
Как работает многопоточная версия?

Существует множество функций PDL, предназначенных для помощи в многопоточности PDL. В этом
Например, ключевыми функциями являются:

Метод: "диапазон"

На простейшем уровне метод "диапазона" - это другой способ выбрать часть
лужа. Вместо обозначения «$ a (2,3)» мы используем другой элемент piddle.

pdl> $ a = последовательность (6,7)
pdl> p $ a
[
[0 1 2 3 4 5]
[6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]
[24 25 26 27 28 29]
[30 31 32 33 34 35]
[36 37 38 39 40 41]
]
pdl> p $ a-> range (pdl [1,2])
13
pdl> p $ a (1,2)
[
[13]
]

На этом этапе метод «диапазона» очень похож на обычный слайс PDL. Но
Метод «диапазона» является более общим. Например, вы можете выбрать сразу несколько компонентов:

pdl> $ index = pdl [[1,2], [2,3], [3,4], [4,5]]
pdl> p $ a-> диапазон ($ индекс)
[13 20 27 34]

Кроме того, "диапазон" принимает второй параметр, который определяет размер фрагмента для
возвращаться:

pdl> $ size = 3
pdl> p $ a-> range (pdl ([1,2]), $ size)
[
[13 14 15]
[19 20 21]
[25 26 27]
]

Мы можем использовать это, чтобы выбрать один или несколько блоков 3x3.

Наконец, «диапазон» может принимать третий параметр, называемый «граничным» условием. Он сообщает PDL
что делать, если запрашиваемое вами поле размера выходит за пределы поля. Мы не пойдем
по всем параметрам. Мы просто скажем, что опция "периодический" означает, что piddle
"оборачивается". Например:

pdl> p $ a
[
[0 1 2 3 4 5]
[6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]
[24 25 26 27 28 29]
[30 31 32 33 34 35]
[36 37 38 39 40 41]
]
pdl> $ size = 3
pdl> p $ a-> range (pdl ([4,2]), $ size, "периодический")
[
[16 17 12]
[22 23 18]
[28 29 24]
]
pdl> p $ a-> range (pdl ([5,2]), $ size, "периодический")
[
[17 12 13]
[23 18 19]
[29 24 25]
]

Обратите внимание, как рамка обтекает границу piddle.

Метод: "ndcoords"

Метод «ndcoords» - это удобный метод, который возвращает нумерованный список
координаты, подходящие для использования с методом "дальности".

pdl> p $ piddle = последовательность (3,3)
[
[0 1 2]
[3 4 5]
[6 7 8]
]
pdl> p ndcoords ($ piddle)
[
[
[0 0]
[1 0]
[2 0]
]
[
[0 1]
[1 1]
[2 1]
]
[
[0 2]
[1 2]
[2 2]
]
]

Это может быть немного трудно читать. В основном это говорит о том, что координаты для каждого
элемент в $ piddle задается следующим образом:

(0,0) (1,0) (2,0)
(1,0) (1,1) (2,1)
(2,0) (2,1) (2,2)

Объединяя "диапазон" и "ndcoords"

Что действительно важно, так это то, что «ndcoords» разработан для совместной работы с «диапазоном», без каких-либо
Параметр $ size, вы получите то же самое значение.

pdl> p $ piddle
[
[0 1 2]
[3 4 5]
[6 7 8]
]
pdl> p $ piddle-> диапазон (ndcoords ($ piddle))
[
[0 1 2]
[3 4 5]
[6 7 8]
]

Почему это может быть полезно? Потому что теперь мы можем попросить серию «коробок» для всего
лужа. Например, коробки 2х2:

pdl> p $ piddle-> range (ndcoords ($ piddle), 2, "периодический")

Вывод этой функции трудночитаем, потому что «квадраты» на последних двух
измерение. Мы можем сделать результат более читабельным, изменив размеры:

pdl> p $ piddle-> range (ndcoords ($ piddle), 2, "периодический") -> reorder (2,3,0,1)
[
[
[
[0 1]
[3 4]
]
[
[1 2]
[4 5]
]
...
]

Здесь вы можете более четко увидеть, что

[0 1]
[3 4]

Поле 2x2 начинается с элемента (0,0) $ piddle.

Мы еще не закончили. Для игры в жизнь нам нужны коробки 3x3 от $ a:

pdl> p $ a
[
[0 1 2 3 4 5]
[6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]
[24 25 26 27 28 29]
[30 31 32 33 34 35]
[36 37 38 39 40 41]
]
pdl> p $ a-> range (ndcoords ($ a), 3, "периодический") -> reorder (2,3,0,1)
[
[
[
[0 1 2]
[6 7 8]
[12 13 14]
]
...
]

Мы можем подтвердить, что это поле 3x3, начинающееся с элемента (0,0) $ a. Но есть
это одна проблема. На самом деле мы хотим, чтобы коробка 3x3 была центр на (0,0). Это не
проблема. Просто вычтите 1 из всех координат в «ndcoords ($ a)». Помните, что
«периодический» вариант позаботится о том, чтобы все было циклическим.

pdl> p $ a-> range (ndcoords ($ a) - 1, 3, "периодический") -> reorder (2,3,0,1)
[
[
[
[41 36 37]
[5 0 1]
[11 6 7]
]
[
[36 37 38]
[0 1 2]
[6 7 8]
]
...

Теперь мы видим прямоугольник 3x3 с элементом (0,0) в центре.

Метод: "сумовер"

Метод «сумувера» добавляет только первое измерение. Если мы применим его дважды, мы будем
добавление всех элементов каждого блока 3x3.

pdl> $ n = $ a-> range (ndcoords ($ a) -1,3, "периодический") -> reorder (2,3,0,1)
pdl> p $ n
[
[
[
[41 36 37]
[5 0 1]
[11 6 7]
]
[
[36 37 38]
[0 1 2]
[6 7 8]
]
...
pdl> p $ n-> сумовер-> сумовер
[
[144 135 144 153 162 153]
[72 63 72 81 90 81]
[126 117 126 135 144 135]
[180 171 180 189 198 189]
[234 225 234 243 252 243]
[288 279 288 297 306 297]
[216 207 216 225 234 225]
]

С помощью калькулятора убедитесь, что 144 - это сумма всех элементов в первом поле 3x3.
135 - сумма всех элементов во втором квадрате 3x3.

Подсчет соседи

Мы почти у цели!

Сложить все элементы в поле 3x3 - это не так. вполне что мы хотим. Мы не хотим считать
центральная коробка. К счастью, это легко исправить:

pdl> p $ n-> sumover-> sumover - $ a
[
[144 134 142 150 158 148]
[66 56 64 72 80 70]
[114 104 112 120 128 118]
[162 152 160 168 176 166]
[210 200 208 216 224 214]
[258 248 256 264 272 262]
[180 170 178 186 194 184]
]

Применительно к «Игре жизни» Конвея это покажет нам, сколько живых соседей у ​​каждого
ячейка имеет:

pdl> $ a = нули (10,10)
pdl> $ a (1: 3,1: 3). = pdl ([1,1,1],
.. (> [0,0,1],
.. (> [0,1,0])
pdl> p $ a
[
[0 0 0 0 0 0 0 0 0 0]
[0 1 1 1 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
]
pdl> $ n = $ a-> range (ndcoords ($ a) -1,3, "периодический") -> reorder (2,3,0,1)
pdl> $ n = $ n-> сумовер-> сумовер - $ a
pdl> p $ n
[
[1 2 3 2 1 0 0 0 0 0]
[1 1 3 2 2 0 0 0 0 0]
[1 3 5 3 2 0 0 0 0 0]
[0 1 1 2 1 0 0 0 0 0]
[0 1 1 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
]

Например, это говорит нам, что ячейка (0,0) имеет 1 живого соседа, а ячейка (2,2) имеет 5
живые соседи.

Расчет следующий поколение

На данный момент переменная $ n содержит количество живых соседей для каждой ячейки. Сейчас мы
примените правила игры жизни, чтобы рассчитать следующее поколение.

Если пустая ячейка имеет ровно трех соседей, создается живая ячейка.
Получите список ячеек, у которых ровно три соседа:

pdl> p ($ n == 3)
[
[0 0 1 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0 0]
[0 1 0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
]

Получите список пустой ячейки, у которых ровно три соседа:

pdl> p ($ n == 3) *! $ a

Если у живой клетки меньше 2 или больше 3 соседей, она умирает.
Получите список ячеек, у которых ровно 2 или 3 соседа:

pdl> p (($ n == 2) + ($ n == 3))
[
[0 1 1 1 0 0 0 0 0 0]
[0 0 1 1 1 0 0 0 0 0]
[0 1 0 1 1 0 0 0 0 0]
[0 0 0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
]

Получите список жизнью ячейки, у которых ровно 2 или 3 соседа:

pdl> p (($ n == 2) + ($ n == 3)) * $ a

Собирая все вместе, следующее поколение:

pdl> $ a = ((($ n == 2) + ($ n == 3)) * $ a) + (($ n == 3) *! $ a)
pdl> p $ a
[
[0 0 1 0 0 0 0 0 0 0]
[0 0 1 1 0 0 0 0 0 0]
[0 1 0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
]

Бонус функции: Графика!
Если у вас установлен PDL :: Graphics :: TriD, вы можете сделать графическую версию программы
просто изменив три строки:

#!/ usr / bin / perl
использовать PDL;
используйте PDL :: NiceSlice;
используйте PDL :: Graphics :: TriD;

мой $ a = нули (20,20);

# Вставьте простой планер.
$ a (1: 3,1: 3). = pdl ([1,1,1],
[0,0,1],
[0,1,0]);

мой $ n;
for (my $ i = 0; $ i <100; $ i ++) {
# Подсчитать количество соседей на ячейку.
$ n = $ a-> range (ndcoords ($ a) -1,3, «периодический») -> изменить порядок (2,3,0,1);
$ n = $ n-> сумовер-> сумовер - $ a;

# Рассчитайте следующее поколение.
$ a = ((($ n == 2) + ($ n == 3)) * $ a) + (($ n == 3) *! $ a);

# Отображать.
nokeeptwiddling3d ();
imagrgb [$ a];
}

Но если мы действительно хотим увидеть что-то интересное, нам следует внести еще несколько изменений:

1) Начните со случайного набора единиц и нулей.

2) Сделайте сетку больше.

3) Добавьте небольшой тайм-аут, чтобы мы могли видеть, как игра развивается лучше.

4) Используйте цикл while, чтобы программа могла работать столько, сколько нужно.

#!/ usr / bin / perl
использовать PDL;
используйте PDL :: NiceSlice;
используйте PDL :: Graphics :: TriD;
используйте Time :: HiRes qw (usleep);

мой $ a = random (100,100);
$ a = ($ a <0.5);

мой $ n;
в то время как (1) {
# Подсчитать количество соседей на ячейку.
$ n = $ a-> range (ndcoords ($ a) -1,3, «периодический») -> изменить порядок (2,3,0,1);
$ n = $ n-> сумовер-> сумовер - $ a;

# Рассчитайте следующее поколение.
$ a = ((($ n == 2) + ($ n == 3)) * $ a) + (($ n == 3) *! $ a);

# Отображать.
nokeeptwiddling3d ();
imagrgb [$ a];

# Сон на 0.1 секунды.
ты спишь(100000);
}

Вывод ОБЩАЯ ИНФОРМАЦИЯ Стратегия


Общая стратегия такова: Двигаемся размеры хотеть в работать on в Начало of
пиддл измерение . Затем позволять PDL нить за высший Габаритные размеры.

Threading - это мощный инструмент, который помогает устранить циклы for и может сделать ваш код более удобным.
лаконичный. Надеюсь, этот урок показал, почему стоит разобраться с потоками.
в PDL.

АВТОРСКИЕ ПРАВА


Авторские права 2010 Мэтью Кенуорти (kenworthy@strw.leidenuniv.nl) и Даниэль Каррера
(dcarrera@gmail.com). Вы можете распространять и / или изменять этот документ на тех же условиях.
как текущая лицензия Perl.

Увидеть: http://dev.perl.org/licenses/

Используйте PDL :: Threadingp в Интернете с помощью сервисов onworks.net



Новейшие онлайн-программы для Linux и Windows