Вычисление числа Пи. Мирошник Глеб. 6 курс
Содержание
Цель
Оценить число π при помощи знакочередующегося ряда Лейбница двумя способами: суммированием и интегрированием. Разработать программу для параллельных вычислений. Оценить скорость вычислений в зависимости от количества задействованных процессоров.
Постановка задачи
Лейбниц доказал, что ряд
равен
- .
Также данный ряд можно получить разложением арктангенса 1 в ряд Тейлора, что даёт нам возможность оценить число π вычислением определённого интеграла
Предлагаемое решение
Суммированием элементов ряда
После объявления переменных и инициализации MPI запускается счётчик времени. Предлагается при фиксированном количестве элементов ряда n считать элементы последовательно каждым процессором, "перепрыгивая" через количество процессов, занятых в расчёте. Начальный рассчитываемый номер ряда определяется порядковым номером процесса. Рассчитанный элемент добавляется к сумме внутри процесса. После окончания расчёта предварительные суммы "скидываются" в нулевой процесс и суммируются между собой. Таймер останавливается. Происходит вычисление ошибки по эталонному значению числа π и вывод информации о расчёте.
Численным вычислением определённого интеграла
Численное вычисление интеграла предлагается проводить методом прямоугольников. Сам же процесс будет аналогичен решению суммированием элементов ряда.
Листинг программ
Для суммирования ряда
#include "stdafx.h"
#include <iostream>
#include "mpi.h"
#include <math.h>
#include <time.h>
using namespace std;
#define Iterations 10000000 //количество итераций
int main() {
int ProcNum, ProcRank, i;
double x, MyPi = 0;
double Sum = 0;
double TotalTime;
clock_t StartClock, EndClock; // объявляем стартовую и конечную переменную таймера и инициализируем стартовую переменную
StartClock = clock();
MPI_Init(NULL, NULL); // инициализируем MPI
MPI_Comm_size(MPI_COMM_WORLD, &ProcNum); // записываем количество процессов с переменную ProcNum
MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank); // записываем номер процесса в переменную ProcRank
for (i = ProcRank; i < Iterations; i += ProcNum)
{
x = pow(-1,ProcRank)*4/(2*i + 1);
Sum += x;
}
MPI_Reduce(&Sum, &MyPi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); // пердварительные суммы Sum в переменную MyPi
MPI_Finalize(); // завершаем работу MPI
if (ProcRank == 0)
{
EndClock = clock(); // останавливаем таймер и переходим к выводу результатов на экран
printf("Number of iterations = %i\n", Iterations);
printf("Obtained value of Pi = %3.20f\n", MyPi);
double pi = 3.1415926535897932384626433832795;
printf("Refrence value of Pi = %3.20f\n", pi);
printf("Error = %3.20f\n", abs(MyPi - pi));
TotalTime = (double)(EndClock - StartClock) / CLOCKS_PER_SEC;
printf("Computing time = %f sec\n", TotalTime);
} }
Для вычисления интеграла
#include "stdafx.h"
#include <iostream>
#include "mpi.h"
#include <math.h>
#include <time.h>
using namespace std;
#define Iterations 10000000 //количество итераций
int main() {
int ProcNum, ProcRank, i;
double x, MyPi = 0;
double Sum = 0;
double TotalTime;
clock_t StartClock, EndClock; // объявляем стартовую и конечную переменную таймера и инициализируем стартовую переменную
StartClock = clock();
MPI_Init(NULL, NULL); // инициализируем MPI
MPI_Comm_size(MPI_COMM_WORLD, &ProcNum); // записываем количество процессов с переменную ProcNum
MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank); // записываем номер процесса в переменную ProcRank
double h = 1.0 / Iterations;
for (i = ProcRank; i < Iterations; i += ProcNum) // вычисляем численно интеграл по методу прямоугольников
{
x = (i + 0.5) * h;
Sum += 4.0 / (1 + x*x); // (после общего суммирования надо будет разделить на количество итераций)
}
MPI_Reduce(&Sum, &MyPi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); // пердварительные суммы Sum в переменную MyPi
MPI_Finalize(); // завершаем работу MPI
if (ProcRank == 0)
{
MyPi *= h; //делим сумму на количество итераций
EndClock = clock(); // останавливаем таймер и переходим к выводу результатов на экран
printf("Number of iterations = %i\n", Iterations);
printf("Obtained value of Pi = %3.20f\n", MyPi);
double pi = 3.1415926535897932384626433832795;
printf("Refrence value of Pi = %3.20f\n", pi);
printf("Error = %3.20f\n", abs(MyPi - pi));
TotalTime = (double)(EndClock - StartClock) / CLOCKS_PER_SEC;
printf("Computing time = %f sec\n", TotalTime);
} }
Компьютерная реализация
Результаты
Количество процессов | Время рассчета (сек) |
---|---|
1 | 184.2 |
2 | 91.6 |
5 | 39.4 |
10 | 19.2 |
20 | 9.9 |
30 | 8.1 |
40 | 7.5 |
Выводы
- Для малого числа узлов в сетке использовать многопроцессорные вычисления не выгодно: время работы программы увеличивается.
- При увеличении числа процессоров относительный выигрыш во времени уменьшается.