Вопрос
Как я могу вычислить значение PI с помощью C #?
Я думал, что это будет сделано с помощью рекурсивной функции, если да, то как это будет выглядеть и есть ли какие-либо математические уравнения, подтверждающие это?
Я не слишком придирчив к производительности, главным образом к тому, как добиться этого с точки зрения обучения.
Решение
Если вы хотите рекурсию:
PI = 2 * (1 + 1/3 * (1 + 2/5 * (1 + 3/7 * (...))))
Это станет после некоторого переписывания:
PI = 2 * F(1);
с F (i):
double F (int i) {
return 1 + i / (2.0 * i + 1) * F(i + 1);
}
Исаак Ньютон (возможно, вы слышали о нем раньше;)) придумал этот трюк. Обратите внимание, что я пропустил конечное условие, чтобы было проще. В реальной жизни вам это нужно.
Другие советы
Как насчет использования:
double pi = Math.PI;
Если вам нужна более высокая точность, вам нужно использовать алгоритмическую систему и тип Decimal.
Если вы внимательно посмотрите на это действительно хорошее руководство:
Вы найдете на этой симпатичной реализации (с небольшими изменениями с моей стороны):
static decimal ParallelPartitionerPi(int steps)
{
decimal sum = 0.0;
decimal step = 1.0 / (decimal)steps;
object obj = new object();
Parallel.ForEach(
Partitioner.Create(0, steps),
() => 0.0,
(range, state, partial) =>
{
for (int i = range.Item1; i < range.Item2; i++)
{
decimal x = (i - 0.5) * step;
partial += 4.0 / (1.0 + x * x);
}
return partial;
},
partial => { lock (obj) sum += partial; });
return step * sum;
}
Есть пара очень, очень старых трюков, которые я удивлен, что не вижу здесь.
atan(1) == PI / 4, таким образом, старый каштан, когда присутствует надежная функция касательной к дуге , равен 4 * atan(1).
Очень симпатичная оценка с фиксированным коэффициентом, по сравнению с которой старый Western 22/7 выглядит как грязь равна 355/113, что соответствует нескольким десятичным знакам (по крайней мере, трем или четырем, я думаю).В некоторых случаях этого даже достаточно для целочисленной арифметики:умножьте на 355, затем разделите на 113.
355/113 также легко сохранить в памяти (во всяком случае, для некоторых людей).:сосчитайте один, один, три, три, пять, пять и помните, что вы называете цифры в знаменателе и числителе (если вы забудете, какая тройка идет сверху, подумайте микросекунду, чтобы все исправить).
Обратите внимание, что 22/7 дает вам:3.14285714, что неверно в тысячных долях.
355/113 дает вам 3,14159292, что не является ошибкой до десятимиллионных долей.
Асс.в /usr/include/math.h в моем поле M_PI равен #define'd как:3.14159265358979323846 что, вероятно, хорошо, насколько это возможно.
Урок, который вы получаете из оценки PI, заключается в том, что существует множество способов сделать это, ни один из них никогда не будет совершенным, и вы должны отсортировать их по предполагаемому использованию.
355/113 - это старая китайская оценка, и я полагаю, что она на много лет старше 22/7.Этому научил меня профессор физики, когда я был старшекурсником.
Хороший обзор различных алгоритмов:
Я не уверен в сложности, заявленной для алгоритма Гаусса-Лежандра-Саламина в первой ссылке (я бы сказал, O(N log ^ 2 (N) log(log (N)))).
Я действительно призываю вас попробовать это, хотя сходимость такова в самом деле быстро.
Кроме того, я не совсем уверен в том, зачем пытаться преобразовать довольно простой процедурный алгоритм в рекурсивный?
Обратите внимание, что если вас интересует производительность, то работайте с ограниченной точностью (как правило, требуется 'double', 'float',...output) на самом деле не имеет смысла, поскольку очевидный ответ в таком случае - просто жестко закодировать значение.
Вот статья о расчете PI в C #:
Что такое PI? Окружность круга делится на его диаметр.
В компьютерной графике вы можете построить / нарисовать окружность с центром в точке 0,0 от начальной точки x, y, следующую точку x ', y' можно найти по простой формуле: x '= x + y / h: y' = y - x '/ h
h обычно является степенью 2, так что деление может быть легко выполнено с помощью сдвига (или вычитания из показателя степени в два раза). h также хочет быть радиусом r вашего круга. Легкой начальной точкой будет x = r, y = 0, а затем посчитать c количество шагов до x & Lt; = 0, чтобы построить четверть круга. PI составляет 4 * с / г или PI составляет 4 * с / ч.
Рекурсия на любую большую глубину, как правило, нецелесообразна для коммерческой программы, но хвостовая рекурсия позволяет рекурсивно выражать алгоритм при его реализации в виде цикла. Иногда рекурсивные алгоритмы поиска могут быть реализованы с использованием очереди, а не стека процесса, при поиске необходимо вернуться из тупика и выбрать другой путь - эти точки возврата можно поместить в очередь, а несколько процессов могут снять эти точки с очереди и попробовать другие пути.
Рассчитайте так:
x = 1 - 1/3 + 1/5 - 1/7 + 1/9 (... etc as far as possible.)
PI = x * 4
У тебя есть Пи !!!
Это самый простой из известных мне методов.
Значение PI медленно сходится к фактическому значению Pi (3.141592165 ......). Если вы повторяете больше раз, тем лучше.
Вот хороший подход (из основной записи Википедии на пи ); он сходится гораздо быстрее, чем простая формула, рассмотренная выше, и вполне поддается рекурсивному решению, если вы намерены использовать рекурсию как учебное упражнение. (Предполагая, что вы прошли обучение, я не даю никакого реального кода.)
Основная формула та же, что и выше, но этот подход усредняет частичные суммы для ускорения сходимости.
Определите двухпараметрическую функцию pie (h, w) так, чтобы:
pie(0,1) = 4/1
pie(0,2) = 4/1 - 4/3
pie(0,3) = 4/1 - 4/3 + 4/5
pie(0,4) = 4/1 - 4/3 + 4/5 - 4/7
... and so on
Итак, ваша первая возможность изучить рекурсию - это написать код, который " горизонтальный " вычисление как " width " параметр увеличивается (для " высоты " нуля).
Затем добавьте второе измерение с этой формулой:
pie(h, w) = (pie(h-1,w) + pie(h-1,w+1)) / 2
который используется, конечно, только для значений h больше нуля.
Приятной особенностью этого алгоритма является то, что вы можете легко смоделировать его с помощью электронной таблицы, чтобы проверить код при изучении результатов, полученных с помощью постепенно увеличивающихся параметров. К тому времени, когда вы вычисляете pie (10,10), у вас будет приблизительное значение для pi, которое достаточно для большинства инженерных целей.
Enumerable.Range(0, 100000000).Aggregate(0d, (tot, next) => tot += Math.Pow(-1d, next)/(2*next + 1)*4)
using System;
namespace Strings
{
class Program
{
static void Main(string[] args)
{
/* decimal pie = 1;
decimal e = -1;
*/
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start(); //added this nice stopwatch start routine
//leibniz formula in C# - code written completely by Todd Mandell 2014
/*
for (decimal f = (e += 2); f < 1000001; f++)
{
e += 2;
pie -= 1 / e;
e += 2;
pie += 1 / e;
Console.WriteLine(pie * 4);
}
decimal finalDisplayString = (pie * 4);
Console.WriteLine("pie = {0}", finalDisplayString);
Console.WriteLine("Accuracy resulting from approximately {0} steps", e/4);
*/
// Nilakantha formula - code written completely by Todd Mandell 2014
// π = 3 + 4/(2*3*4) - 4/(4*5*6) + 4/(6*7*8) - 4/(8*9*10) + 4/(10*11*12) - (4/(12*13*14) etc
decimal pie = 0;
decimal a = 2;
decimal b = 3;
decimal c = 4;
decimal e = 1;
for (decimal f = (e += 1); f < 100000; f++)
// Increase f where "f < 100000" to increase number of steps
{
pie += 4 / (a * b * c);
a += 2;
b += 2;
c += 2;
pie -= 4 / (a * b * c);
a += 2;
b += 2;
c += 2;
e += 1;
}
decimal finalDisplayString = (pie + 3);
Console.WriteLine("pie = {0}", finalDisplayString);
Console.WriteLine("Accuracy resulting from {0} steps", e);
stopwatch.Stop();
TimeSpan ts = stopwatch.Elapsed;
Console.WriteLine("Calc Time {0}", ts);
Console.ReadLine();
}
}
}
public static string PiNumberFinder(int digitNumber)
{
string piNumber = "3,";
int dividedBy = 11080585;
int divisor = 78256779;
int result;
for (int i = 0; i < digitNumber; i++)
{
if (dividedBy < divisor)
dividedBy *= 10;
result = dividedBy / divisor;
string resultString = result.ToString();
piNumber += resultString;
dividedBy = dividedBy - divisor * result;
}
return piNumber;
}
В любом производственном сценарии я заставляю вас искать значение с желаемым количеством десятичных знаков и сохранять его как «const» там, где ваши классы могут получить его.
(если вы не пишете научное программное обеспечение для Pi) ...
Относительно...
...как это сделать с точки зрения обучения?
Вы пытаетесь научиться программировать научными методами?или для создания производственного программного обеспечения?Я надеюсь, что сообщество сочтет это обоснованным вопросом, а не придиркой.
В любом случае, я думаю, что написание собственного Pi - решаемая проблема.Дмитрий уже показал константу 'Math.PI'.Решите другую проблему в том же пространстве!Используйте общие ньютоновские аппроксимации или что-нибудь более изящное.
@ Томас Каммейер:
Обратите внимание, что Atan (1.0) довольно часто жестко запрограммирован, поэтому 4 * Atan (1.0) на самом деле не является «алгоритмом», если вы вызываете библиотечную функцию Atan (довольно многие из уже предложенных действительно приступают к замене Atan ( x) по серии (или бесконечному произведению) для него, затем оценивая его при x = 1.
Кроме того, очень мало случаев, когда вам понадобится пи с большей точностью, чем несколько десятков битов (это может быть легко закодировано!). Я работал над приложениями в математике, где, чтобы вычислить некоторые (довольно сложные) математические объекты (которые были полиномиальными с целыми коэффициентами), мне пришлось выполнять арифметику с действительными и комплексными числами (включая вычисление числа Пи) с точностью до несколько миллионов бит ... но это не очень часто "в реальной жизни":)
Вы можете посмотреть следующий пример код .
Мне нравится этот документ , в котором объясняется, как рассчитывать & # 960; основанный на разложении в ряд Тейлора для Арктангента.
Статья начинается с простого предположения, что
Atan (1) = & # 960; / 4 радиана
Атан (х) может быть итеративно оценен с помощью ряда Тейлора
atan (x) = x - x ^ 3/3 + x ^ 5/5 - x ^ 7/7 + x ^ 9/9 ...
В статье указывается, почему это не особенно эффективно, и продолжается внесение ряда логических улучшений в технику. Они также предоставляют пример программы, которая вычисляет & # 960; до нескольких тысяч цифр, с исходным кодом, включая математические процедуры бесконечной точности.
Следующая ссылка показывает, как вычислить константу pi на основе ее определения как интеграла, который может быть записан как предел суммирования, это очень интересно: https://sites.google.com/site/rcorcs/posts/calculationthepiconstant Файл & Quot; Pi как целое & Quot; объясняет этот метод, используемый в этом посте.
Во-первых, обратите внимание, что C # может использовать поле Math.PI .NET Framework:
https: // msdn.microsoft.com/en-us/library/system.math.pi(v=vs.110).aspx р>
Приятной особенностью здесь является то, что это двойная точность с полной точностью, которую вы можете использовать или сравнить с вычисленными результатами. Вкладки по этому URL имеют аналогичные константы для C ++, F # и Visual Basic. Р>
Чтобы рассчитать больше мест, вы можете написать свой собственный код с расширенной точностью. Тот, который быстро кодировать и достаточно быстро и легко программировать:
Pi = 4 * [4 * арктан (1/5) - арктан (1/239)]
Эта формула и многие другие, включая те, которые сходятся с удивительно высокой скоростью, например, 50 цифр в семестре, в Wolfram:
Число ПИ (π) может быть рассчитан с помощью бесконечный ряд.Вот два примера:
Серия Грегори-Лейбница:
π/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - ...
Метод C # :
public static decimal GregoryLeibnizGetPI(int n)
{
decimal sum = 0;
decimal temp = 0;
for (int i = 0; i < n; i++)
{
temp = 4m / (1 + 2 * i);
sum += i % 2 == 0 ? temp : -temp;
}
return sum;
}
Серия Нилаканта:
π = 3 + 4 / (2x3x4) - 4 / (4x5x6) + 4 / (6x7x8) - 4 / (8x9x10) + ...
Метод C #:
public static decimal NilakanthaGetPI(int n)
{
decimal sum = 0;
decimal temp = 0;
decimal a = 2, b = 3, c = 4;
for (int i = 0; i < n; i++)
{
temp = 4 / (a * b * c);
sum += i % 2 == 0 ? temp : -temp;
a += 2; b += 2; c += 2;
}
return 3 + sum;
}
Входной параметр n
для обеих функций представляет собой количество итераций.
Ряд Нилаканты по сравнению с рядом Грегори-Лейбница сходится быстрее.Эти методы можно протестировать с помощью следующего кода:
static void Main(string[] args)
{
const decimal pi = 3.1415926535897932384626433832m;
Console.WriteLine($"PI = {pi}");
//Nilakantha Series
int iterationsN = 100;
decimal nilakanthaPI = NilakanthaGetPI(iterationsN);
decimal CalcErrorNilakantha = pi - nilakanthaPI;
Console.WriteLine($"\nNilakantha Series -> PI = {nilakanthaPI}");
Console.WriteLine($"Calculation error = {CalcErrorNilakantha}");
int numDecNilakantha = pi.ToString().Zip(nilakanthaPI.ToString(), (x, y) => x == y).TakeWhile(x => x).Count() - 2;
Console.WriteLine($"Number of correct decimals = {numDecNilakantha}");
Console.WriteLine($"Number of iterations = {iterationsN}");
//Gregory-Leibniz Series
int iterationsGL = 1000000;
decimal GregoryLeibnizPI = GregoryLeibnizGetPI(iterationsGL);
decimal CalcErrorGregoryLeibniz = pi - GregoryLeibnizPI;
Console.WriteLine($"\nGregory-Leibniz Series -> PI = {GregoryLeibnizPI}");
Console.WriteLine($"Calculation error = {CalcErrorGregoryLeibniz}");
int numDecGregoryLeibniz = pi.ToString().Zip(GregoryLeibnizPI.ToString(), (x, y) => x == y).TakeWhile(x => x).Count() - 2;
Console.WriteLine($"Number of correct decimals = {numDecGregoryLeibniz}");
Console.WriteLine($"Number of iterations = {iterationsGL}");
Console.ReadKey();
}
Следующий результат показывает, что ряд Нилаканты возвращает шесть правильных десятичных чисел числа PI за сто итераций, тогда как ряд Грегори-Лейбница возвращает пять правильных десятичных чисел числа PI за миллион итераций:
Мой код можно протестировать >> здесь
Вот хороший способ: Рассчитайте ряд 1 / x ^ 2 для x от 1 до того, что вы хотите - чем больше число, тем лучше результат. Умножьте результат на 6 и до sqrt (). Вот код на C # (только основной):
static void Main(string[] args)
{
double counter = 0;
for (double i = 1; i < 1000000; i++)
{
counter = counter + (1 / (Math.Pow(i, 2)));
}
counter = counter * 6;
counter = Math.Sqrt(counter);
Console.WriteLine(counter);
}
public double PI = 22.0 / 7.0;