<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://wiki2.linuxformat.ru/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>LXF130:R - История изменений</title>
		<link>http://wiki2.linuxformat.ru/index.php?title=LXF130:R&amp;action=history</link>
		<description>История изменений этой страницы в вики</description>
		<language>ru</language>
		<generator>MediaWiki 1.11.1</generator>
		<lastBuildDate>Wed, 13 May 2026 22:27:38 GMT</lastBuildDate>
		<item>
			<title>Crazy Rebel: викификация, оформление, иллюстрация</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF130:R&amp;diff=11819&amp;oldid=prev</link>
			<description>&lt;p&gt;викификация, оформление, иллюстрация&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая статья&lt;/b&gt;&lt;/p&gt;&lt;div&gt;: '''''R''''' Высокопроизводительные вычисления на современном персональном компьютере&lt;br /&gt;
&lt;br /&gt;
==''R'': Ускоряем на примере==&lt;br /&gt;
&lt;br /&gt;
{{Цикл/R}}&lt;br /&gt;
&lt;br /&gt;
: Завершая мини­курс под кодовым названием «'''''R'' на примере'''», '''Сергей Петров''' и '''Евгений Балдин''' рассмотрят высокопроизводительные вычисления.&lt;br /&gt;
&lt;br /&gt;
Компьютеры ускоряются, диски становятся просторнее, но темпы роста объёма данных и усложнение их обработки всё равно вынуждают ускорять вычисления. &lt;br /&gt;
&lt;br /&gt;
По традиции, сложные вычисления выполняют кластеры. Многие наверняка слышали про список TOP500, но речь пойдет не о нём: кластерные технологии потихоньку проникают и на обычные компьютеры, можно сказать, в дома. Почти у каждого процессора сейчас несколько ядер. Отчего же этим не воспользоваться? Но сперва прикинем, как оценить выигрыш от подобных приёмов.&lt;br /&gt;
&lt;br /&gt;
===Анализ эффективности===&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF130_64_1.jpg|300px]] Результаты профилирования функции '''lm()'''...&lt;br /&gt;
[[Изображение:LXF130_64_2.jpg|300px]] ...и '''lm.fit()'''. Как говорится, почувствуйте разницу!|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
Измерить скорость вычислений в ''R'' и, соответственно, оценить эффективность написанного кода можно несколькими способами:&lt;br /&gt;
* Применить '''system.time()''' для простых измерений.&lt;br /&gt;
* Профилировать выполнение кода с помощью '''Rprof()'''.&lt;br /&gt;
* Профилировать потребление памяти с помощью '''Rprofmem()'''.&lt;br /&gt;
&lt;br /&gt;
Для визуализации накопленных с помощью '''Rprof()''' данных можно применять пакеты ''profr'' и ''proftools''.&lt;br /&gt;
&lt;br /&gt;
Приложим средства профилирования к простой задаче. Пусть в ходе моделирования многократно вычисляются параметры линейной регрессии, через функцию '''lm()'''. Её недостаток – много лишних действий: она просто слишком универсальна. Для простой линейной регрессии достаточно уточнённого вызова '''lm.fit()'''.&lt;br /&gt;
&lt;br /&gt;
Насколько же эффективнее прямой вызов? Попробуем обработать набор макроэкономических показателей '''longley''' с помощью обоих функций. Построим зависимость между числом работающих и остальными показателями. Для оценки проделаем по 1000 вычислений за раз.&lt;br /&gt;
&lt;br /&gt;
 # Загружаем данные&lt;br /&gt;
 &amp;gt; data(longley)&lt;br /&gt;
 # Записываем профиль в файл lm.out&lt;br /&gt;
 &amp;gt; Rprof(“lm.out”)&lt;br /&gt;
 # Выполняем lm() 1000 раз&lt;br /&gt;
 &amp;gt; invisible(replicate(1000,lm(Employed ~ .­1, data=longley)))&lt;br /&gt;
 # Отключаем профилирование&lt;br /&gt;
 &amp;gt; Rprof(NULL) &lt;br /&gt;
 # Готовим данные для lm.fit()&lt;br /&gt;
 &amp;gt; longleydm &amp;lt;­ data.matrix(data.frame(longley))&lt;br /&gt;
 # Записываем профиль в файл&lt;br /&gt;
 lm.fit.out&lt;br /&gt;
 &amp;gt; Rprof(“lm.fit.out”)&lt;br /&gt;
 # Выполняем lm.fit() 1000 раз&lt;br /&gt;
 &amp;gt; invisible(replicate(1000,lm.&lt;br /&gt;
 fit(longleydm[,­7],longleydm[,7])))&lt;br /&gt;
 # Отключаем профилирование&lt;br /&gt;
 &amp;gt; Rprof(NULL)&lt;br /&gt;
&lt;br /&gt;
Записанные в файлах профилирования данные можно проанализировать с помощью встроенной команды '''summaryRprof()'''. Например, для того, чтобы выяснить время работы программы, достаточно обратиться к переменной '''sampling.time''':&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; summaryRprof(“lm.out”)$sampling.time&lt;br /&gt;
 [1] 6.42&lt;br /&gt;
 &amp;gt; summaryRprof(“lm.fit.out”)$sampling.time&lt;br /&gt;
 [1] 0.44&lt;br /&gt;
&lt;br /&gt;
Данные профилирования можно отобразить и графически (см. рисунок), с помощью пакета ''profr'':&lt;br /&gt;
&lt;br /&gt;
 # Устанавливаем пакет (если нужно)&lt;br /&gt;
 &amp;gt; install.packages(“profr”)&lt;br /&gt;
 &amp;gt; library(“profr”)&lt;br /&gt;
 &amp;gt; plot(parse_rprof(“lm.out”),main=”Profile of lm()”)&lt;br /&gt;
 &amp;gt; plot(parse_rprof(“lm.fit.out”),main=”Profile of lm.fit()”)&lt;br /&gt;
&lt;br /&gt;
Из рисунков видно, сколько времени проводит программа в каждой из функций. Неудивительно, что '''lm.fit()''' работает в четырнадцать (!) раз быстрее.&lt;br /&gt;
&lt;br /&gt;
Для представления зависимостей вызовов в виде графа можно воспользоваться пакетом ''proftools''. Для этого необходимо установить системный пакет ''graphviz-dev'':&lt;br /&gt;
&lt;br /&gt;
 =&amp;gt; aptitude install graphvizdev&lt;br /&gt;
&lt;br /&gt;
а также пакет ''Rgraphviz'' и сам ''proftools'':&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; source(“http://bioconductor.org/biocLite.R”)&lt;br /&gt;
 &amp;gt; biocLite(“Rgraphviz”)&lt;br /&gt;
 &amp;gt; install.packages(“proftools”)}&lt;br /&gt;
&lt;br /&gt;
Для отображения графа нужно выполнить такие команды:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; library(“Rgraphviz”)&lt;br /&gt;
 &amp;gt; library(“proftools”)&lt;br /&gt;
 &amp;gt; lmfitprod &amp;lt; readProfileData(“lm.fit.out”)&lt;br /&gt;
 &amp;gt; plotProfileCallGraph(lmfitprod)&lt;br /&gt;
&lt;br /&gt;
Для отладки потребления памяти нужно использовать специально модифицированную версию ''R'', которая собрана с опцией '''--enable-memory-profiling'''. При этом для анализа применяется команда '''Rprofmem''', и все происходит аналогично использованию '''Rprof'''.&lt;br /&gt;
&lt;br /&gt;
Перерасход дисковой памяти отследит функция '''tracemem''': она срабатывает при каждом копировании какого-либо объекта.&lt;br /&gt;
&lt;br /&gt;
===Ключ к ускорению===&lt;br /&gt;
&lt;br /&gt;
Достичь максимальных скоростей поможет знание встроенных функций ''R.'' Обычно такие функции создаются, когда выполняемые ими задачи начинают частить в обработке данных. Подобный подход существенно сокращает усилия по вводу кода программы, а также ускоряет выполнение кода, за счёт реализованных во встроенных функциях оптимальных алгоритмов.&lt;br /&gt;
&lt;br /&gt;
Как правило, встроенные функции реализованы на более низкоуровневом языке, предоставляющем углубленный доступ к возможностям оборудования. Поясним это на примере: напишем программу сложения чисел от 1 до указанного, в обычном цикле, и сравним с программой, использующей встроенные функции ''R'':&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; mysum &amp;lt;- function(N) { a &amp;lt;- 0;&lt;br /&gt;
 + for (i in 1:N) a &amp;lt;- a + i;&lt;br /&gt;
 + return(a) }&lt;br /&gt;
 &amp;gt; system.time(mysum(10000000))&lt;br /&gt;
 user system elapsed&lt;br /&gt;
 7.456 0.024 7.482&lt;br /&gt;
 &amp;gt; system.time(sum(as.numeric(seq(1,10000000))))&lt;br /&gt;
 user system elapsed&lt;br /&gt;
 0.052 0.060 0.112&lt;br /&gt;
&lt;br /&gt;
Разница очевидна: второй вариант подсчёта суммы работает более чем в 70 раз быстрее! То, что сейчас было проделано, носит название «векторизация». На такой способ расчётов трудно переключиться после императивного стиля программирования, но именно векторизация обуславливает необычайно высокую продуктивность работы в ''R''. Заметьте: не только уменьшилось время выполнения программы, но и сократился её размер!&lt;br /&gt;
&lt;br /&gt;
Возьмем простую тестовую задачу: «Найти распределение детерминанта матрицы 2×2, в которую занесены независимо и случайно изменяющиеся значения из диапазона 0, 1, 2, … , 9». Она эквивалентна задаче о нахождении всех сочетаний ab-cd, где a, b, c, d – это цифры.&lt;br /&gt;
&lt;br /&gt;
Наивное императивное решение «с циклами» выглядит привычно, и благодаря синтаксису ''R'' достаточно «компактно»:&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF130_65_1.jpg|300px]] Граф вызовов, полученный с помощью пакета '''proftools'''. |Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; dd.for.0 &amp;lt;- function()&lt;br /&gt;
 + {&lt;br /&gt;
 + val &amp;lt;- NULL&lt;br /&gt;
 + for (a in 0:9) for (b in 0:9) for (d in 0:9) for (e in 0:9)&lt;br /&gt;
 + val &amp;lt;- c(val, a*b - d*e)&lt;br /&gt;
 + table(val)&lt;br /&gt;
 + }&lt;br /&gt;
 &amp;gt; system.time(dd.for.0())&lt;br /&gt;
 user system elapsed&lt;br /&gt;
 0.196 0.000 0.195&lt;br /&gt;
&lt;br /&gt;
От запуска к запуску время слегка изменяется, так что распределение отличается от нормального, и лучше всего находить медиану нескольких попыток:&lt;br /&gt;
&lt;br /&gt;
 median(replicate(20, system.time(dd.for.0())[“elapsed”]))&lt;br /&gt;
 [1] 0.177&lt;br /&gt;
&lt;br /&gt;
Попробуем добиться от этого наивного варианта большего: например, выделим память под расчёты сразу в начале программы.&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; dd.for.1 &amp;lt;- function()&lt;br /&gt;
 + {&lt;br /&gt;
 + val &amp;lt;- double(10000) # преаллоцируем val&lt;br /&gt;
 + nval &amp;lt;- 0&lt;br /&gt;
 + for (a in 0:9) for (b in 0:9) for (d in 0:9) for (e in 0:9)&lt;br /&gt;
 + val[nval &amp;lt;- nval + 1] &amp;lt;- a*b - d*e&lt;br /&gt;
 + table(val)&lt;br /&gt;
 + }&lt;br /&gt;
 &amp;gt; median(replicate(20, system.time(dd.for.1())[“elapsed”]))&lt;br /&gt;
 [1] 0.059&lt;br /&gt;
&lt;br /&gt;
Эффект от проделанных изменений очевиден: код ускорился более чем в три раза. Поскольку наши данные – целые числа, давайте посмотрим, что даст нам использование встроенной функции '''tabulate()''':&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; dd.for.3 &amp;lt;- function()&lt;br /&gt;
 + {&lt;br /&gt;
 + val &amp;lt;- double(10000)&lt;br /&gt;
 + nval &amp;lt;- 0&lt;br /&gt;
 + for (a in 0:9) for (b in 0:9) for (d in 0:9) for (e in 0:9)&lt;br /&gt;
 + val[nval &amp;lt;- nval + 1] &amp;lt;- a*b - d*e&lt;br /&gt;
 + tabulate(val)&lt;br /&gt;
 + }&lt;br /&gt;
 &amp;gt; median(replicate(20, system.time(dd.for.3())[“elapsed”]))&lt;br /&gt;
 [1] 0.057&lt;br /&gt;
&lt;br /&gt;
Время выполнения сократилось еще чуть-чуть, но это всё полумеры. Давайте сделаем решительный шаг и вспомним, что прародителем ''R'' был и язык ''APL''. Запишем решение задачи как операцию над массивами:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; dd.fast &amp;lt;- function()&lt;br /&gt;
 + {&lt;br /&gt;
 + val &amp;lt;- outer(0:9, 0:9, “*”)&lt;br /&gt;
 + val &amp;lt;- outer(val, val, “-”)&lt;br /&gt;
 + tabulate(val)&lt;br /&gt;
 + }&lt;br /&gt;
 &amp;gt; median(replicate(20, system.time(dd.fast())[“elapsed”]))&lt;br /&gt;
 [1] 0.001&lt;br /&gt;
&lt;br /&gt;
Лучшее из наших решений, использующих циклы, обойдено более чем в 50 раз, а «традиционное» – почти в 200!&lt;br /&gt;
&lt;br /&gt;
Что же делать, если избавиться от цикла нельзя? Один из вариантов – использовать среду ''R'', в которую встроена возможность компиляции кода. Сборка ''R'' с функцией '''JIT''' (Just-in-Time Compilation, компиляция во время выполнения) позволяет рассчитывать на ускорение кода, содержащего циклы, раза в полтора.&lt;br /&gt;
&lt;br /&gt;
Операции с матрицами являются в ''R'' такими производительными, потому что они опираются на процедуры библиотеки ''BLAS'' (Basic Linear Algebra Subprogram). ''R'' может быть скомпилирова на с различными вариантами реализации ''BLAS'': это и свободная библиотека ''Atlas'' (пакет '''atlas3‑base'''), и платная ''Goto'', и библиотеки от двух основных производителей процессоров: Intel (Intel Math Kernel Library, http://software.intel.com/ru-ru/intel-mkl/) и AMD (AMD Core Math Library, http://www.amd.com/acml). Они не только имеют более производительный код, но и автоматически задействуют в вычислениях все имеющиеся ядра процессора персонального компьютера. Дополнительный прирост производительности можно получить, настроив ''Atlas'' под конкретно используемый в расчётах процессор.&lt;br /&gt;
&lt;br /&gt;
Помимо библиотеки ''BLAS'', можно «попытать счастья» с экспериментальным пакетом ''pnmath0'' (http://www.stat.uiowa.edu/~luke/R/experimental/) от Люка Тьерни [Luke Tierney]. Он распараллеливает выполнение векторных функций ''R'', используя потоки ''Pthreads''. Эта возможность пока не встроена в ''R'' – её придётся добавить самостоятельно. Учтите, что параллельные вычисления активируются только при достаточной длине векторов аргументов.&lt;br /&gt;
&lt;br /&gt;
Если на компьютере установлена видеокарта, которая поддерживает вычисления на своем GPU (пока сюда входят только NVIDIA CUDA и CUBLAS), то при установке пакета ''gputools'' появляется возможность выполнять иерархический кластерный анализ, классификацию с обучением (по алгоритму '''SVM'') и расчёт коэффициентов корреляции с очень высокой скоростью.&lt;br /&gt;
&lt;br /&gt;
Несмотря на использование высокопроизводительных векторных операций и компиляции в режиме Just-in-Time, бывают моменты, когда на счету каждый такт процессора. В этом случае есть два способа оперативно встроить в расчет, выполненный в среде ''R'', низкоуровневый код императивного языка программирования:&lt;br /&gt;
* '''inline''' — для простой вставки небольшого фрагмента кода;&lt;br /&gt;
* '''Rcpp''' – для облегчённого процесса интеграции сложного кода на ''C++''.&lt;br /&gt;
&lt;br /&gt;
Пакет ''inline'' предоставляет функцию '''cfunction()''', умеющую автоматически встраивать код, написанный на ''Fortran, C, C++''. Для выполнения следующего простого примера на ''Fortran'', естественно необходимо установить и загрузить сам ''inline'':&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 # Не забудьте про отсту пы! Fortran есть Fortran.&lt;br /&gt;
 &amp;gt; code &amp;lt;­ “&lt;br /&gt;
 +     do i = 1, n(1)&lt;br /&gt;
 +      x(i) = x(i)**3&lt;br /&gt;
 +     end do”&lt;br /&gt;
 &amp;gt; cubefn &amp;lt;­ cfunction(signature(n=”integer”, x=”numeric”),&lt;br /&gt;
 +               code, convention=”.Fortran”)&lt;br /&gt;
 &amp;gt; x &amp;lt;­ as.numeric (1:10)&lt;br /&gt;
 &amp;gt; n &amp;lt;­ as.integer(10)&lt;br /&gt;
 &amp;gt; cubefn(n,x)$x&lt;br /&gt;
 [1] 1 8 27 64 125 216 343 512 729 1000&lt;br /&gt;
&lt;br /&gt;
===В параллель===&lt;br /&gt;
&lt;br /&gt;
Среда ''R'', работающая в 64-битном окружении, практически не имеет ограничений на объём обрабатываемых данных. Современным ответом в области вычисления с гигантскими массивами данных является пакет ''iterators'' от REvolution Computing. С его возможностью поэлементно обработать структуру, не вмещающуюся в память, крайне удачно сочетается второй пакет, ''foreach''. Он вводит возможность циклически обработать созданный итератор и вернуть суммарный результат. Отсутствие побочных эффектов позволяет выполнить оптимизируемую операцию параллельно.&lt;br /&gt;
&lt;br /&gt;
Отсутствие побочных эффектов – именно то, что позволяет не заботиться, где выполняется та или иная часть кода. Компьютеры не только стали мощнее и у них теперь больше ядер. Компьютеров, прежде всего, стало много больше. Под рукой практически у каждого из нас имеются 5–10 машин, и их многочисленные процессоры, если приглядеться внимательно, не загружены и на 10%. Вся эта мощь доступна пользователю при работе с ''R''.&lt;br /&gt;
&lt;br /&gt;
Кластерные вычисления настолько естественны для векторных операций ''R'', что существует несколько способов их реализации в этой среде:&lt;br /&gt;
* '''Rmpi''' – реализация Message Passing Interface (MPI), который является стандартом в области паралельных вычислений;&lt;br /&gt;
* '''NWS''' – написанная на ''Python'' альтернативная реализация MPI;&lt;br /&gt;
* '''snow''' – высокоуровневая надстройка над MPI, PVM, NWS и сокетами (sockets);&lt;br /&gt;
* '''papply''' – параллелизация функции apply через MPI;&lt;br /&gt;
* '''multicore''' – параллельные вычисления на многоядерных машинах.&lt;br /&gt;
&lt;br /&gt;
Поскольку наша цель – это быстро и «глобально» для нашей же пользы задействовать мощь окружающих нас и фактически простаивающих компьютеров, сразу же воспользуемся высокоуровневым средством, а именно, пакетом ''snow''. А чтобы рассказ не был пустым теоретизированием, попробуем решить реальную задачу, имеющую практическую ценность.&lt;br /&gt;
&lt;br /&gt;
====Задача====&lt;br /&gt;
&lt;br /&gt;
Число телефонных пар, проходящих рядом и передающих сигнал без помех, ограничено. Безусловно, влияет и длина кабеля, и его ёмкость, и диаметр жил. Однако в номограммах оценки характеристик телефонной пары не предусмотрено свыше 25 ADSL-пар в одном кабеле.&lt;br /&gt;
&lt;br /&gt;
Нормативные документы российских провайдеров ADSL определяют емкость одиночного кабеля в 18 %. Скорее всего, для кабелей небольшой ёмкости всё же верен предел на 18 жил, занятых одновременно работающими ADSL-модемами, в одном кабеле. А для кабелей ёмкостью более 100 пар верен процентный лимит.&lt;br /&gt;
&lt;br /&gt;
Пусть в городе Н-ске ёмкость ADSL-сети Н-ск-телеком превысила 10 000 абонентов. Попробуем оценить, какие проблемы встретит сеть при своем развитии.&lt;br /&gt;
&lt;br /&gt;
====Модель====&lt;br /&gt;
&lt;br /&gt;
Город Н-ск насчитывает 300 000 населения, в средней семье 3 человека, что определяет следующие исходные условия:&lt;br /&gt;
&lt;br /&gt;
 # количество абонентов услуги&lt;br /&gt;
 &amp;gt; n.abonentov &amp;lt;- 10000&lt;br /&gt;
 # количество домов&lt;br /&gt;
 &amp;gt; n.domov &amp;lt;- 1000&lt;br /&gt;
 # количество квартир в доме&lt;br /&gt;
 &amp;gt; n.kvartir &amp;lt;- 100&lt;br /&gt;
 # критическое число абонентов для ADSL в одном доме&lt;br /&gt;
 &amp;gt; n.kritic &amp;lt;- 18&lt;br /&gt;
&lt;br /&gt;
Мы получаем модель города в виде вектора, каждый элемент которого – квартира, помеченная номером дома:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; gorod &amp;lt;- rep(1:n.domov, each = n.kvartir)&lt;br /&gt;
&lt;br /&gt;
Поскольку вычисления ресурсоёмкие, сразу загрузим библиотеку для параллелизации основных функций пакета (предполагается, что эта экспериментальная библиотека уже установлена):&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; library(“pnmath0”)&lt;br /&gt;
&lt;br /&gt;
Делаем выборку случайных '''n.abonentov''' в векторе '''gorod'''.&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; vyborka &amp;lt;- as.factor(gorod)[sample(1:length(gorod), + n.abonentov, replace= FALSE)]&lt;br /&gt;
&lt;br /&gt;
Подсчитываем, сколько абонентов попало в нее в каждом доме, и строим гистограмму.&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; hist(as.numeric(tapply(rep(1,n.abonentov), vyborka, sum)))&lt;br /&gt;
&lt;br /&gt;
Вычислим, сколько домов испытывают трудности:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; length(as.numeric(tapply(rep(1,n.abonentov),&lt;br /&gt;
 + vyborka, sum))[as.numeric(tapply(rep(1,n.abonentov),&lt;br /&gt;
 + vyborka, sum))&amp;gt;n.kritic])&lt;br /&gt;
 [1] 4&lt;br /&gt;
&lt;br /&gt;
а также оценим число «проблемных» абонентов:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; sum(as.numeric(tapply(rep(1,n.abonentov),&lt;br /&gt;
 + vyborka, sum))[as.numeric(tapply(rep(1,n.abonentov),&lt;br /&gt;
 + vyborka, sum))&amp;gt;n.kritic])&lt;br /&gt;
 [1] 81&lt;br /&gt;
&lt;br /&gt;
Это только одна реализация. Построим бутстреп-процедуру ([[LXF128:R|LXF128]]) и оценим долю домов, где будет свыше '''n.kritic''' абонентов. Тут нужно не менее 10 000 вычислительных экспериментов:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; nn &amp;lt;- 10000&lt;br /&gt;
 &amp;gt; bstr.dom &amp;lt;- numeric(nn)&lt;br /&gt;
 &amp;gt; bstr.abonent &amp;lt;- numeric(nn)&lt;br /&gt;
 &amp;gt; for (n in 1:nn) {&lt;br /&gt;
 + vyborka &amp;lt;- as.factor(gorod)[sample(1:length(gorod), n.abonentov, replace= FALSE)]&lt;br /&gt;
 + rr &amp;lt;- as.numeric(tapply(rep(1,n.abonentov),vyborka , sum))&lt;br /&gt;
 + bstr.abonent[n] &amp;lt;- sum(rr[rr&amp;gt;n.kritic])&lt;br /&gt;
 + bstr.dom[n] &amp;lt;- length(rr[rr&amp;gt;n.kritic])&lt;br /&gt;
 + }&lt;br /&gt;
 # Проблемные абоненты&lt;br /&gt;
 &amp;gt; hist(bstr.abonent)&lt;br /&gt;
 # Проблемные дома&lt;br /&gt;
 &amp;gt; hist(bstr.dom)&lt;br /&gt;
&lt;br /&gt;
Проведём эксперимент с подсчётом: сколько процентов абонентов и какое количество домов окажется с плохим качеством услуги при росте абонентской базы от 10 000 до 20 000 абонентов.&lt;br /&gt;
&lt;br /&gt;
Оформим функцию:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; my.boot.adsl &amp;lt;­ function (n.abonentov) {&lt;br /&gt;
 + nn &amp;lt;­ 10000&lt;br /&gt;
 + bstr.dom &amp;lt;­ numeric(nn)&lt;br /&gt;
 + bstr.abonent &amp;lt;­ numeric(nn)&lt;br /&gt;
 + for (n in 1:nn) {&lt;br /&gt;
 + vyborka &amp;lt;­ as.factor(gorod)[sample(1:length(gorod),&lt;br /&gt;
 +               n.abonentov, replace= FALSE)]&lt;br /&gt;
 + rr &amp;lt;­ as.numeric(tapply(rep(1,n.abonentov),vyborka , sum))&lt;br /&gt;
 + bstr.abonent[n] &amp;lt;­ sum(rr[rr&amp;gt;n.kritic])&lt;br /&gt;
 + bstr.dom[n] &amp;lt;­ length(rr[rr&amp;gt;n.kritic])&lt;br /&gt;
 + }&lt;br /&gt;
 + return(abonent=bstr.abonent, dom=bstr.dom)&lt;br /&gt;
 +}&lt;br /&gt;
&lt;br /&gt;
Рассчитаем оценку числа проблемных абонентов в диапазоне размера абонентской базы от 10 000 до 20 000:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; xx &amp;lt;­ c(NA)&lt;br /&gt;
 &amp;gt; for (n in 1:10) {&lt;br /&gt;
 + xx[n] &amp;lt;­ my.boot.adsl(10000+(n*1000))&lt;br /&gt;
 +}&lt;br /&gt;
&lt;br /&gt;
Отнормируем на размер абонентской базы:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; xx.norm &amp;lt;­ xx&lt;br /&gt;
 &amp;gt; xx.norm[[1]] &amp;lt;­ xx[[1]]/11000&lt;br /&gt;
 &amp;gt; for (n in 2:10) {&lt;br /&gt;
 &amp;gt; xx.norm[[n]] &amp;lt;­ xx[[n]]/(10000+(n*1000))&lt;br /&gt;
 &amp;gt;}&lt;br /&gt;
&lt;br /&gt;
Наконец, отобразим в виде боксплота:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; boxplot(xx.norm,names=seq(11000,20000,by = 1000))&lt;br /&gt;
&lt;br /&gt;
Данный модельный город предполагал наличие только 100-квартирных домов. Повторим вычисления на данных о реальном количестве квартир в городе Н-ске.&lt;br /&gt;
&lt;br /&gt;
====Реальность====&lt;br /&gt;
&lt;br /&gt;
Ввиду ресурсоёмкости вычислений напишем параллельную версию программы. Для этого загрузим библиотеку '''snow''':&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; library(snow)&lt;br /&gt;
&lt;br /&gt;
Создадим кластер из двух узлов:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; cl &amp;lt;- makeCluster(c(“localhost”,”localhost”), type = “SOCK”)&lt;br /&gt;
&lt;br /&gt;
Критическое число абонентов для ADSL в одном доме –&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; n.kritic &amp;lt;- 18&lt;br /&gt;
 &amp;gt; clusterExport(cl, “n.kritic”)&lt;br /&gt;
&lt;br /&gt;
Получаем модель города в виде вектора, каждый элемент которого – квартира, помеченная номером дома. Обработаем реальные дома города Гродно (Беларусь). Загрузим список максимальных номеров квартир в доме:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; Nsk &amp;lt;- na.omit(read.table(“data_Nsk.txt”))&lt;br /&gt;
 &amp;gt; Nsk.sort &amp;lt;- sort(t(Nsk))&lt;br /&gt;
&lt;br /&gt;
Файл '''data_Nsk.txt''' – просто колонка чисел, которую можно скачать, например, отсюда: http://www.inp.nsk.su/~baldin/data_Nsk.txt.&lt;br /&gt;
&lt;br /&gt;
Создадим модель города:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; gorod &amp;lt;- unlist(mapply(rep,&lt;br /&gt;
 + 1:length(Nsk.sort),Nsk.sort))&lt;br /&gt;
 &amp;gt; clusterExport(cl, “gorod”)&lt;br /&gt;
&lt;br /&gt;
Отведём место под результаты оценки числа проблемных домов и абонентов, для уменьшения накладных расходов при выделении памяти.&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; nn &amp;lt;- 10000&lt;br /&gt;
 &amp;gt; bstr.dom &amp;lt;- numeric(nn)&lt;br /&gt;
 &amp;gt; clusterExport(cl,”bstr.dom”)&lt;br /&gt;
 &amp;gt; bstr.abonent &amp;lt;- numeric(nn)&lt;br /&gt;
 &amp;gt; clusterExport(cl,”bstr.abonent”)&lt;br /&gt;
&lt;br /&gt;
Функция расчёта числа проблемных абонентов при случайном распределении выглядит так:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; my.boot.func &amp;lt;­ function (n, n.abonentov) {&lt;br /&gt;
 + vyborka &amp;lt;­ as.factor(gorod)[sample(1:length(gorod),&lt;br /&gt;
 +              n.abonentov, replace= FALSE)]&lt;br /&gt;
 + rr &amp;lt;­ as.numeric(tapply(rep(1,n.abonentov),vyborka , sum))&lt;br /&gt;
 + bstr.abonent[n] &amp;lt;­ sum(na.omit(rr[rr&amp;gt;n.kritic]))&lt;br /&gt;
 +}&lt;br /&gt;
&lt;br /&gt;
Рассчитаем оценку числа проблемных абонентов в диапазоне абонентской базы от 3000 до 32 000 человек&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; xx &amp;lt;­ cbind(sapply(1000+((1:10)*3000),&lt;br /&gt;
 +            function (n.abonentov) parSapply(cl,&lt;br /&gt;
 +               c(1:nn), my.boot.func, n.abonentov )))&lt;br /&gt;
&lt;br /&gt;
Вновь отнормируем на размер абонентской базы:&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; xx.norm &amp;lt;­ xx&lt;br /&gt;
 &amp;gt; xx.norm[,1] &amp;lt;­ xx[,1]/4000&lt;br /&gt;
 &amp;gt; for (n in 2:10) {&lt;br /&gt;
 + xx.norm[,n] &amp;lt;­ xx[,n]/(1000+(n*3000))&lt;br /&gt;
 +}&lt;br /&gt;
&lt;br /&gt;
И, наконец, отобразим в виде боксплота, после чего остановим кластер.&lt;br /&gt;
&lt;br /&gt;
 &amp;gt; boxplot(xx.norm, names=seq(4000,31000,by = 3000))&lt;br /&gt;
 &amp;gt; stopCluster(cl)&lt;br /&gt;
&lt;br /&gt;
====А в чём выигрыш?====&lt;br /&gt;
&lt;br /&gt;
По приведённым выше вычислениям тестирование выполнялось на процессоре семейства Intel Core Duo, модель T2050 с частотой 1,60 ГГц. При использовании распараллеливания время вычисления составляло 6470 секунд, а без оного – 12754 секунд. Иными словами, два ядра примерно в два раза лучше, чем одно. Что и Требовалось Доказать.&lt;/div&gt;</description>
			<pubDate>Thu, 14 Apr 2011 08:57:14 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF130:R</comments>		</item>
	</channel>
</rss>