Как вычислить число пи c
Перейти к содержимому

Как вычислить число пи c

  • автор:

Как вычислить число пи c

In this article, we will discuss some of the mathematical function which is used to derive the value of Pi(π) in C++. Method 1: Using acos() function: Approach:

  1. The value of Π is calculated using acos() function which returns a numeric value between [-Π, Π].
  2. Since using acos(0.0) will return the value for Π/2. Therefore, to get the value of Π:
  1. Now the value obtained from above equation is estimated as:

Below is the implementation of the above approach:

Time Complexity: O(1)

Auxiliary Space: O(1)

Method 2: Using asin() function: Approach:

  1. The value of Π is calculated using asin() function which returns a numeric value between [-Π, Π].
  2. Since using asin(1.0) will return the value for Π/2. Therefore, to get the value of Π:
  1. Now the value obtained from above equation is estimated as:

Below is the implementation of the above approach:

Time Complexity: O(1)

Auxiliary Space: O(1)

Method 3: Using inbuilt constant value define in the “cmath” library: The value of Pi(π) can directly written using the constant stored in cmath library. The name of the constant is M_PI. Below is the program for printing the value of Pi:

Считаем Пи параллельно. Часть 1


В этой серии постов мы попробуем решить одну простую задачу с помощью более-менее актуальных технологий параллельного программирования (Нативные потоки, OpenMP, TBB, MPI, CUDA, OpenCL, OpenACC, Chapel может быть еще что-нить экзотическое. Как бы сравнительно и в hands-on ключе.

Чтобы все влезло в один пост (в нескольких частях) и вообще могло быть совокупно охвачено вниманием проблема была выбрана умышленно тривиальная. Поэтому многие аспекты упомянутых технологий и главное трудности которые возникают при реальном прграммировании современных массивно-параллельных систем останутся за кадром. Также не стоит пользоваться приведённым и примерами в качестве бенчмарка «GPU против CPU» или что-то в этом духе. Единственная цель — показать «как оно работает». Пост рассчитан на людей с параллельным программированием не очень знакомых. Под катом будет много кода. Собственно считать мы будем число Пи путем численного интегрирования

Исходные коды также доступны на github.com/undertherain/pi (будут пополнятся по мере написания следующих частей поста).

Последовательная версия.

Давайте для начала сделаем последовательную версию. Т.е. такую, которая использует одно ядро обычного центрального процессора. Возьмем самый простой из методов численного инетгрирования — метод прямоугольников и закодим его на языке Си (вообще дальше будем пользоваться Си\С++-ным суржиком в виду ряда причин.
Объявим некоторое количество cntSteps прямоугольников на которые мы разобъем нашу площадь под интегралом, посчитаем основание:

и всю площадь, посчитав значение функии в каждом прямоугольнике и домножив на основание.

Впрочем, домножим на основание step мы лучше «за скобками», т.е. за циклом — вот в принципе и все.

Вот полный код программы:

Native Threads

Самая, пожалуй, доступная простым пользователям параллельная архитектура — это обычный многоядерный процессор (кажется сейчас уже трудно найти процессор с одним ядром) или несколько процессоров на одной материнской плате. В принципе и одно ядро способно исполнять параллельно несоколько потоков — такой режим называется псевдо-параллельным или конкурентным. Ядро переключается между процессами (процесс — это грубо говоря находящаяся в памяти программа), выделяя каждому квант времени. В принципе такой режим выполнения уже может привести к росту производительности за счет сокрытия латентности памяти, если не на обычных «домашних» процессорах, то на специализированных многопоточных, но об этом позже. В нашем случае избыточное количество потоков привело бы к замедлению из-за накладных расходов на переключение между потоками.

Самый «исторический» способ исползьовать сразу несколько ядер на процессоре — это механизм потоков операционной системы, который сущесвовал задолго до истинно-параллельных процессоров для конкуренности хотя бы ради более удобного написания программ. С точки зрения программиста важно то, что параллельные потоки, исполняемые на разных ядрах или процессорах видят одно и то же адресное пространство, т.е нет нужды явно передавать данные между потоками. Зато если вдруг разные потоки пишут\читают одну и ту же переменную — то придётся озаботиться синхронизацией.

Ладно, давайти ближе к коду: с точки зрения языка Си поток — это обычная функция или метод класса, удовлетворяющая определённому прототипу. Давайте назовём ее static void * worker ( void * ptrArgs ) , аргументом она получает указатель на структуру, в которой можно сделать полей сколько нужно чтобы передать потоку его аргументы. В нашем случае мы скажем потоковой функии в каком диапазоне считать наш интеграл. В теле потоковой функции — уже известный нам цикл, собсно и считающий по диапазону который мы ему передали в параметрах.

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

На исполнение отдельным потоком функция запускается через системный вызов pthread_create в случае POSIX (в линуксе, например) или в случае windows этто будет аналогичный вызов из Win32 API, вуглядеть будет немного по-другому, но в целом похоже.

Результат будем из каждого потока прибавлять к общем перменной pi += sum * step ; (помним что мы находимся в общем адресном пространстве).

Чтобы память не испортилась в случае если два потока будут одновременно изменять одну ячейко нам надо как-то гарантировать что в один момент времени только один поток получает доступ к переменной pi, т.е. создать так называемую «критическую секцию». Для это можно воспользовться специальным механизмом операционной системы под названием мьютекс (от слова mutual exclusion) — если один поток заблокировал мьютекс, другой поток будет ждать (на попытке получить мьютекс самому) пока первый поток его не «освободит».

Итого выходит как-то так:

Копия на http://dumpz.org/195404/ на случай если у кото-то моё адово форматироваие неровно отображается.

На самом деле конкретно в этом примере (но так везти будет не всегда) можно было обойтись без мьютексов вообще если сохранять в каждом потоке результат в отдельную переменную (элемент массива ArgsThread arrArgsThread [ cntThreads ];
) а потом, дождавшись завершения всех потоков — просуммировать что получилось.

Вот код без мьютексов:

Как видите, код получилася довольно громоздкий и некросплатформенный. Если последнее решается отчасти с помощью boost::threads(но не все хотят ставить boost) или в новом C++11 потоки вообще стали частью языка (очень здорово вышло на самом деле) — но большая часть софта пока еще использует старый C++. Но проблема громоздкости кода всё равно остается.

OpenMP

OpenMP — это расширение языка (C/C++/Fortran) позволяющее делать примерно то же самое что мы делали с помощью API потоков операционной системы — но гораздо проще и лаконичней с помощью так называемых прагм. Прагма как бы говорит компилатору «взять вот этот код и исполнять параллельно» — и копмилятор делает все остальное.
Для распараллеливания цикла for в нашем первом последовательном примере достаточно добавить туда одну строчку:

эта прагма говорит, что нужно распараллелить витки цикла, переменную x сделать приватной для каждого потока, по переменной sum потом провести редукцию (или как это по-русски?) по суммированию. Т.е. сначала создать для каждого потока по копии — а потом их сложить. Примерно то же самое, что мы сделали в прошлом примере без мьютекосв. Также OpenMP предоставляет небольшой API для сервисных нужд.

PI Constant in C++

In this tutorial, we will learn how to use the PI constant in C++.

PI is a mathematical constant which we use in various mathematical calculations ranging from simple finding area of a circle to more complex Stoke’s theorem in mathematics. C++ has a predefined constant in its math library which we can use to access the value of pi wherever needed in our program.

We use the following header file :

Here, _USE_MATH_DEFINES is a #define macro. Later in the program, we use M_PI to access the value of PI. In C++, the value of M_PI is 3.14159265358979323846.

How do I calculate PI in C#?

I was thinking it would be through a recursive function, if so, what would it look like and are there any math equations to back it up?

I’m not too fussy about performance, mainly how to go about it from a learning point of view.

20 Answers 20

If you want recursion:

This would become, after some rewriting:

Isaac Newton (you may have heard of him before 😉 ) came up with this trick. Note that I left out the end condition, to keep it simple. In real life, you kind of need one.

How about using:

If you want better precision than that, you will need to use an algorithmic system and the Decimal type.

Tim Cooper's user avatar

If you take a close look into this really good guide:

You’ll find at Page 70 this cute implementation (with minor changes from my side):

Oliver's user avatar

There are a couple of really, really old tricks I’m surprised to not see here.

atan(1) == PI/4, so an old chestnut when a trustworthy arc-tangent function is present is 4*atan(1).

A very cute, fixed-ratio estimate that makes the old Western 22/7 look like dirt is 355/113, which is good to several decimal places (at least three or four, I think). In some cases, this is even good enough for integer arithmetic: multiply by 355 then divide by 113.

355/113 is also easy to commit to memory (for some people anyway): count one, one, three, three, five, five and remember that you’re naming the digits in the denominator and numerator (if you forget which triplet goes on top, a microsecond’s thought is usually going to straighten it out).

Note that 22/7 gives you: 3.14285714, which is wrong at the thousandths.

355/113 gives you 3.14159292 which isn’t wrong until the ten-millionths.

Acc. to /usr/include/math.h on my box, M_PI is #define’d as: 3.14159265358979323846 which is probably good out as far as it goes.

The lesson you get from estimating PI is that there are lots of ways of doing it, none will ever be perfect, and you have to sort them out by intended use.

355/113 is an old Chinese estimate, and I believe it pre-dates 22/7 by many years. It was taught me by a physics professor when I was an undergrad.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *