Вычисление числа Пи. Мирошник Глеб. 6 курс — различия между версиями

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск
(Новая страница: «==Цель== Оценить число π при помощи знакочередующегося ряда Лейбница двумя способами: сум…»)
 
(Листинг программ)
Строка 20: Строка 20:
  
 
==Листинг программ==
 
==Листинг программ==
 +
===Для суммирования ряда===
 
<code>
 
<code>
#include "stdafx.h"
+
&#35;include "stdafx.h"
#include <iostream>
+
&#35;include <iostream>
#include "mpi.h"
+
&#35;include "mpi.h"
#include <math.h>
+
&#35;include <math.h>
#include <time.h>
+
&#35;include <time.h>
 
using namespace std;
 
using namespace std;
#define Iterations 10000000 //количество итераций
+
&#35;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);
 +
 
 +
}
 +
}
 +
 
 +
</code>
 +
 
 +
===Для вычисления интеграла===
 +
<code>
 +
&#35;include "stdafx.h"
 +
&#35;include <iostream>
 +
&#35;include "mpi.h"
 +
&#35;include <math.h>
 +
&#35;include <time.h>
 +
using namespace std;
 +
&#35;define Iterations 10000000 //количество итераций
  
  

Версия 13:49, 19 января 2017

Цель

Оценить число π при помощи знакочередующегося ряда Лейбница двумя способами: суммированием и интегрированием. Разработать программу для параллельных вычислений. Оценить скорость вычислений в зависимости от количества задействованных процессоров.

Постановка задачи

Лейбниц доказал, что ряд

[math]1-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+\frac{1}{9}-\frac{1}{11}+\frac{1}{13}-\cdot\cdot\cdot=\sum^{\infty}_{n=1} {\frac{(-1)^{n}}{2n+1}}[/math]

равен

[math]\frac{\pi}{4}[/math].

Также данный ряд можно получить разложением арктангенса 1 в ряд Тейлора, что даёт нам возможность оценить число π вычислением определённого интеграла

[math]\int\limits_0^1 \frac{1}{1+x^{2}}\,dx = frac{\pi}{4}[/math]


Предлагаемое решение

Суммированием элементов ряда

После объявления переменных и инициализации 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

Выводы

  • Для малого числа узлов в сетке использовать многопроцессорные вычисления не выгодно: время работы программы увеличивается.
  • При увеличении числа процессоров относительный выигрыш во времени уменьшается.

Полезные ссылки

Уравнение теплопроводности