Я ищу самый быстрый способ получить значение π, как личный вызов. В частности, я использую способы, которые не предполагают использования констант #define, таких как M_PI, или жесткого кодирования числа в.
В приведенной ниже программе тестируются различные известные мне способы. Версия со встроенной сборкой теоретически является самым быстрым вариантом, хотя явно не переносима. Я включил его в качестве основы для сравнения с другими версиями. В моих тестах со встроенными модулями версия 4 * atan(1) была самой быстрой на GCC 4.2, потому что она автоматически сворачивает atan(1) в константу. С указанием -fno-builtin версия atan2(0, -1) является самой быстрой.
Вот основная программа тестирования (pitimes.c):
#include <math.h>
#include <stdio.h>
#include <time.h>
#define ITERS 10000000
#define TESTWITH(x) { \
diff = 0.0; \
time1 = clock(); \
for (i = 0; i < ITERS; ++i) \
diff += (x) - M_PI; \
time2 = clock(); \
printf("%s\t=> %e, time => %f\n", #x, diff, diffclock(time2, time1)); \
}
static inline double
diffclock(clock_t time1, clock_t time0)
{
return (double) (time1 - time0) / CLOCKS_PER_SEC;
}
int
main()
{
int i;
clock_t time1, time2;
double diff;
/* Warmup. The atan2 case catches GCC's atan folding (which would
* optimise the ``4 * atan(1) - M_PI'' to a no-op), if -fno-builtin
* is not used. */
TESTWITH(4 * atan(1))
TESTWITH(4 * atan2(1, 1))
#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__))
extern double fldpi();
TESTWITH(fldpi())
#endif
/* Actual tests start here. */
TESTWITH(atan2(0, -1))
TESTWITH(acos(-1))
TESTWITH(2 * asin(1))
TESTWITH(4 * atan2(1, 1))
TESTWITH(4 * atan(1))
return 0;
}
И встроенная сборка (fldpi.c), которая будет работать только для систем x86 и x64:
double
fldpi()
{
double pi;
asm("fldpi" : "=t" (pi));
return pi;
}
И сценарий сборки, который собирает все конфигурации, которые я тестирую (build.sh):
#!/bin/sh
gcc -O3 -Wall -c -m32 -o fldpi-32.o fldpi.c
gcc -O3 -Wall -c -m64 -o fldpi-64.o fldpi.c
gcc -O3 -Wall -ffast-math -m32 -o pitimes1-32 pitimes.c fldpi-32.o
gcc -O3 -Wall -m32 -o pitimes2-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -fno-builtin -m32 -o pitimes3-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -ffast-math -m64 -o pitimes1-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall -m64 -o pitimes2-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall -fno-builtin -m64 -o pitimes3-64 pitimes.c fldpi-64.o -lm
Помимо тестирования между различными флагами компилятора (я сравнивал 32-битную и 64-битную версии, потому что оптимизации разные), я также пробовал переключать порядок тестов. Но, тем не менее, версия atan2(0, -1) каждый раз выходит на первое место.
@erik: Не во всех языках есть встроенная константа, такая как M_PI. Я пытался найти «авторитетный» способ получить значение числа пи (с плавающей запятой), которое (теоретически) работает на разных языках (и / или их встроенных библиотеках). В настоящее время я предпочитаю использовать atan2(0, -1), но, возможно, есть способы получше.
вопрос в том, зачем вам нет использовать константу? например определяется библиотекой или вами? Вычисление числа Пи - это пустая трата циклов ЦП, поскольку эта проблема решается снова и снова, и число значащих цифр намного больше, чем требуется для повседневных вычислений.
Есть только одно решение, которое быстрее, чем предварительное вычисление константы PI: предварительное вычисление всех значений появляется в формулах, например когда требуется окружность, вы можете предварительно вычислить 2 * PI вместо того, чтобы каждый раз умножать PI на 2 во время выполнения.
@ ChrisJester-Young Нет. Это не намерение ... Недавно я начал больше углубляться в правила ... и подумал, что внесу свой вклад в темы, которые я посещаю, чтобы напомнить пользователям, которые, возможно, забыли о длительных временных рамках. Я никоим образом не пытаюсь быть здесь полицией. Извиняюсь, если я наткнулся на грубость.
@Zeus В этом конкретном случае мой вопрос на самом деле был задуман как "забавный" вопрос по микрооптимизации (который в наши дни, вероятно, лучше подходит для Пазлы для программирования и код для гольфа), но общая предпосылка о "самом быстром способе вычисления числа Пи" казалось достаточно полезным, чтобы оставить этот вопрос здесь. Итак, на каком-то этапе я, вероятно, пересмотрю вопрос, следует ли мне просто принять лучший алгоритмический ответ (возможно, ответ Нлюкарони), независимо от того, связан ли он с микрооптимизацией.
@ HopelessN00b На диалекте английского языка, который я говорю, «оптимизация» - это пишется с буквой «s», а не «z» (которая произносится как «zed», кстати, а не «zee» ;-)). (Это не первый раз, когда мне приходилось отменять подобную правку, если вы посмотрите на историю обзоров.)
Ответ nlucaroni набрал 100 голосов (поздравляю), так что, вероятно, стоит отметить его зеленой галочкой. Наслаждаться! (Хотя, поскольку это вики сообщества и все такое, он не генерирует репутацию, поэтому даже не уверен, заметит ли это nlucaroni.)
@Pacerier См. en.wiktionary.org/wiki/boggle, а также en.wiktionary.org/wiki/mindboggling.
В идеале это должно привлечь внимание Mysticial, поскольку он является мировым рекордсменом по вычислению числа Пи по наибольшему количеству цифр. stackoverflow.com/questions/14283270/…
9801 / (1103√8) .. дает шесть десятичных знаков .. это самый быстрый способ вычислить PI? = 3,14159273
@signonsridhar Нет, мы говорим только о методах вычислений, которые дают тот же результат, что и M_PI, при усечении до двойной точности.
@Chris Jester-Young Я только что видел видео о Рамануджане, который дал такой способ вычисления PI. Я просто поделился им:>
Только что наткнулся на этот, который должен быть здесь для полноты: рассчитать PI в Piet Он имеет довольно приятное свойство, заключающееся в том, что точность может быть улучшена путем увеличения размера программы. Здесь немного понимает сам язык
Если Эта статья истинно, то созданный алгоритм, который Беллард может быть одним из самых быстрых доступных. Он создал число Пи в 2,7 ТРИЛЛИОНА цифр с помощью НАСТОЛЬНОГО ПК! ... и он опубликовал свою работать здесь Хорошая работа, Беллард, ты первопроходец! theregister.co.uk/2010/01/06/very_long_pi
Беллард был пионером во многих-многих отношениях ... сначала был LZEXE, вполне возможно, первый исполняемый компрессор (подумайте, что делает UPX, затем вернитесь во времени в 80-е), и, конечно же, теперь широко используются и QEMU, и FFMPEG. . Ох, и его запись в IOCCC .... :-P
Вы можете начать с изучения формул получения π, а затем попытаться записать это в файл C. Используя Формула Брент-Саламин, вы можете попытаться выяснить его алгоритм и каким-то образом преобразовать его как программу. Я пока не знаю, как это сделать, потому что кое-чего не понимаю ...


Вот общее описание метода вычисления числа Пи, которому я научился в старшей школе.
Я разделяю это только потому, что считаю, что это достаточно просто, чтобы любой мог запомнить его бесконечно долго, плюс он учит вас концепции методов «Монте-Карло», которые представляют собой статистические методы получения ответов, которые не сразу кажутся выводимые через случайные процессы.
Нарисуйте квадрат и впишите квадрант (четверть полукруга) внутри этого квадрата (квадрант с радиусом, равным стороне квадрата, чтобы он заполнял как можно большую часть квадрата)
Теперь бросьте дротик в квадрат и запишите, где он приземлится, то есть выберите случайную точку в любом месте квадрата. Конечно, он попал внутрь квадрата, но внутри полукруга? Запишите этот факт.
Повторите этот процесс много раз - и вы обнаружите, что существует отношение количества точек внутри полукруга к общему количеству брошенных точек, назовите это отношение x.
Поскольку площадь квадрата равна r, умноженному на r, вы можете сделать вывод, что площадь полукруга равна x, умноженному на r, умноженному на r (то есть, x умножить на r в квадрате). Следовательно, x умножить на 4 даст вам число пи.
Это не быстрый способ использовать. Но это хороший пример метода Монте-Карло. И если вы посмотрите вокруг, вы можете обнаружить, что с помощью таких методов можно решить многие проблемы, не связанные с вашими вычислительными навыками.
Это метод, который мы использовали для вычисления числа Пи в java-проекте в школе. Просто использовал рандомизатор, чтобы найти координаты x, y, и чем больше «дротиков» мы бросали, тем ближе к Пи мы подходили.
Метод Монте-Карло, как уже упоминалось, применяет некоторые отличные концепции, но, очевидно, не самый быстрый, ни по большому счету, ни по какой-либо разумной мере. Кроме того, все зависит от того, какую точность вы ищете. Самая быстрая π, о которой я знаю, - это число с жестко закодированными цифрами. Если посмотреть на число Пи и Пи [PDF], можно увидеть множество формул.
Вот метод, который быстро сходится - около 14 цифр на итерацию. PiFast, текущее самое быстрое приложение, использует эту формулу с БПФ. Я просто напишу формулу, так как код простой. Эту формулу почти нашел Рамануджан и обнаружен Чудновским. На самом деле он подсчитал несколько миллиардов цифр числа именно так - так что это не метод, которым нельзя пренебрегать. Формула быстро переполнится, и, поскольку мы делим факториалы, было бы выгодно отложить такие вычисления, чтобы удалить члены.


куда,

Ниже находится Алгоритм Брента – Саламина. Википедия упоминает, что когда а и б «достаточно близки», тогда (a + b) ² / 4t будет приближением к π. Я не уверен, что означает «достаточно близко», но, судя по моим тестам, одна итерация получила 2 цифры, две - 7, а три - 15, конечно, это с двойными числами, поэтому может быть ошибка, основанная на ее представлении и расчет истинный мог бы быть более точным.
let pi_2 iters =
let rec loop_ a b t p i =
if i = 0 then a,b,t,p
else
let a_n = (a +. b) /. 2.0
and b_n = sqrt (a*.b)
and p_n = 2.0 *. p in
let t_n = t -. (p *. (a -. a_n) *. (a -. a_n)) in
loop_ a_n b_n t_n p_n (i - 1)
in
let a,b,t,p = loop_ (1.0) (1.0 /. (sqrt 2.0)) (1.0/.4.0) (1.0) iters in
(a +. b) *. (a +. b) /. (4.0 *. t)
Наконец, как насчет пи-гольфа (800 цифр)? 160 символов!
int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5;for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);}
Предполагая, что вы пытаетесь реализовать первый самостоятельно, не будет ли sqr (k3) проблемой? Я почти уверен, что это приведет к иррациональному числу, которое вам придется оценить (IIRC, все корни, которые не являются целыми числами, являются иррациональными). Все остальное выглядит довольно просто, если вы используете арифметику с бесконечной точностью, но квадратный корень является преградой. Второй также включает sqrt.
по моему опыту, «достаточно близко» обычно означает, что используется приближение ряда Тейлора.
Если под самым быстрым вы подразумеваете самый быстрый ввод кода, вот решение сценарий игры в гольф:
;''6666,-2%{2+.2/@*/10.3??2*+}*`1000<~\;
На самом деле существует целая книга, посвященная (среди прочего) методам быстрый для вычисления \ pi: «Пи и AGM» Джонатана и Питера Борвейнов (доступно на Amazon).
Я немного изучил AGM и связанные с ним алгоритмы: это довольно интересно (хотя иногда и нетривиально).
Обратите внимание, что для реализации большинства современных алгоритмов вычисления \ pi вам понадобится арифметическая библиотека с множественной точностью (GMP - неплохой выбор, хотя с тех пор, как я последний раз использовал ее, прошло некоторое время).
Временная сложность лучших алгоритмов находится в O (M (n) log (n)), где M (n) - временная сложность умножения двух n-битных целых чисел (M (n) = O (n) log (n) log (log (n))) с использованием алгоритмов на основе БПФ, которые обычно необходимы при вычислении цифр \ pi, и такой алгоритм реализован в GMP).
Обратите внимание, что даже несмотря на то, что математика, лежащая в основе алгоритмов, может быть нетривиальной, сами алгоритмы обычно представляют собой несколько строк псевдокода, и их реализация обычно очень проста (если вы решили не писать свою собственную арифметику с высокой точностью :-)).
Формула BBP позволяет вам вычислить n-ю цифру - в базе 2 (или 16) - без необходимости даже сначала беспокоиться о предыдущих n-1 цифрах :)
Мне очень нравится эта программа, потому что она приближает π, глядя на свою собственную область.
IOCCC 1988: westley.c
#define _ -F<00||--F-OO--; int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ }
Если вы замените _ на -F
или, если вы замените _ на «if (предыдущий символ - '-') {OO--;} F--;»
Эта программа была великолепна в 1998 году, но была сломана, потому что современные препроцессоры более либеральны с вставкой пробелов вокруг расширений макросов, чтобы предотвратить работу подобных вещей. К сожалению, это реликвия.
Передайте --traditional-cpp в cpp, чтобы получить желаемое поведение.
@Pat, если вам интересно, почему я его отредактировал, потому что я видел этот ответ в очереди LQP stackoverflow.com/review/low-quality-posts/16750528, поэтому, чтобы избежать удаления, я добавил код в ссылку на ответ.
(Скопировано из DSource.org)
/** Calculate pi at compile time
*
* Compile with dmd -c pi.d
*/
module calcpi;
import meta.math;
import meta.conv;
/** real evaluateSeries!(real x, real metafunction!(real y, int n) term)
*
* Evaluate a power series at compile time.
*
* Given a metafunction of the form
* real term!(real y, int n),
* which gives the nth term of a convergent series at the point y
* (where the first term is n==1), and a real number x,
* this metafunction calculates the infinite sum at the point x
* by adding terms until the sum doesn't change any more.
*/
template evaluateSeries(real x, alias term, int n=1, real sumsofar=0.0)
{
static if (n>1 && sumsofar == sumsofar + term!(x, n+1)) {
const real evaluateSeries = sumsofar;
} else {
const real evaluateSeries = evaluateSeries!(x, term, n+1, sumsofar + term!(x, n));
}
}
/*** Calculate atan(x) at compile time.
*
* Uses the Maclaurin formula
* atan(z) = z - z^3/3 + Z^5/5 - Z^7/7 + ...
*/
template atan(real z)
{
const real atan = evaluateSeries!(z, atanTerm);
}
template atanTerm(real x, int n)
{
const real atanTerm = (n & 1 ? 1 : -1) * pow!(x, 2*n-1)/(2*n-1);
}
/// Machin's formula for pi
/// pi/4 = 4 atan(1/5) - atan(1/239).
pragma(msg, "PI = " ~ fcvt!(4.0 * (4*atan!(1/5.0) - atan!(1/239.0))) );
К сожалению, касательные - это арктангенсы, основанные на пи, что в некоторой степени делает это вычисление недействительным.
Это «классический» метод, очень простой в реализации. Эта реализация на Python (не самом быстром языке) делает это:
from math import pi
from time import time
precision = 10**6 # higher value -> higher precision
# lower value -> higher speed
t = time()
calc = 0
for k in xrange(0, precision):
calc += ((-1)**k) / (2*k+1.)
calc *= 4. # this is just a little optimization
t = time()-t
print "Calculated: %.40f" % calc
print "Constant pi: %.40f" % pi
print "Difference: %.40f" % abs(calc-pi)
print "Time elapsed: %s" % repr(t)
Вы можете найти дополнительную информацию здесь.
В любом случае, самый быстрый способ получить точное значение числа пи в Python:
from gmpy import pi
print pi(3000) # the rule is the same as
# the precision on the previous code
Вот фрагмент исходного кода для метода gmpy pi, я не думаю, что в этом случае код так же полезен, как комментарий:
static char doc_pi[] = "\
pi(n): returns pi with n bits of precision in an mpf object\n\
";
/* This function was originally from netlib, package bmp, by
* Richard P. Brent. Paulo Cesar Pereira de Andrade converted
* it to C and used it in his LISP interpreter.
*
* Original comments:
*
* sets mp pi = 3.14159... to the available precision.
* uses the gauss-legendre algorithm.
* this method requires time o(ln(t)m(t)), so it is slower
* than mppi if m(t) = o(t**2), but would be faster for
* large t if a faster multiplication algorithm were used
* (see comments in mpmul).
* for a description of the method, see - multiple-precision
* zero-finding and the complexity of elementary function
* evaluation (by r. p. brent), in analytic computational
* complexity (edited by j. f. traub), academic press, 1976, 151-176.
* rounding options not implemented, no guard digits used.
*/
static PyObject *
Pygmpy_pi(PyObject *self, PyObject *args)
{
PympfObject *pi;
int precision;
mpf_t r_i2, r_i3, r_i4;
mpf_t ix;
ONE_ARG("pi", "i", &precision);
if (!(pi = Pympf_new(precision))) {
return NULL;
}
mpf_set_si(pi->f, 1);
mpf_init(ix);
mpf_set_ui(ix, 1);
mpf_init2(r_i2, precision);
mpf_init2(r_i3, precision);
mpf_set_d(r_i3, 0.25);
mpf_init2(r_i4, precision);
mpf_set_d(r_i4, 0.5);
mpf_sqrt(r_i4, r_i4);
for (;;) {
mpf_set(r_i2, pi->f);
mpf_add(pi->f, pi->f, r_i4);
mpf_div_ui(pi->f, pi->f, 2);
mpf_mul(r_i4, r_i2, r_i4);
mpf_sub(r_i2, pi->f, r_i2);
mpf_mul(r_i2, r_i2, r_i2);
mpf_mul(r_i2, r_i2, ix);
mpf_sub(r_i3, r_i3, r_i2);
mpf_sqrt(r_i4, r_i4);
mpf_mul_ui(ix, ix, 2);
/* Check for convergence */
if (!(mpf_cmp_si(r_i2, 0) &&
mpf_get_prec(r_i2) >= (unsigned)precision)) {
mpf_mul(pi->f, pi->f, r_i4);
mpf_div(pi->f, pi->f, r_i3);
break;
}
}
mpf_clear(ix);
mpf_clear(r_i2);
mpf_clear(r_i3);
mpf_clear(r_i4);
return (PyObject*)pi;
}
Обновлено: У меня были проблемы с вырезанием и вставкой и отступами, вы можете найти исходник здесь.
В этой версии (в Delphi) нет ничего особенного, но она как минимум быстрее, чем версия, которую Ник Ходж опубликовал в своем блоге :). На моей машине для выполнения миллиарда итераций требуется около 16 секунд, что дает значение 3,1415926525879 (точная часть выделена жирным шрифтом).
program calcpi;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
start, finish: TDateTime;
function CalculatePi(iterations: integer): double;
var
numerator, denominator, i: integer;
sum: double;
begin
{
PI may be approximated with this formula:
4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 .......)
//}
numerator := 1;
denominator := 1;
sum := 0;
for i := 1 to iterations do begin
sum := sum + (numerator/denominator);
denominator := denominator + 2;
numerator := -numerator;
end;
Result := 4 * sum;
end;
begin
try
start := Now;
WriteLn(FloatToStr(CalculatePi(StrToInt(ParamStr(1)))));
finish := Now;
WriteLn('Seconds:' + FormatDateTime('hh:mm:ss.zz',finish-start));
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
В прежние времена, с маленькими размерами слов и медленными или отсутствующими операциями с плавающей запятой, мы делали такие вещи:
/* Return approximation of n * PI; n is integer */
#define pi_times(n) (((n) * 22) / 7)
Для приложений, которые не требуют большой точности (например, видеоигры), это очень быстро и достаточно точно.
Для большей точности используйте 355 / 113. Очень точно для размера задействованных чисел.
Пи ровно 3! [Проф. Фринк (Симпсоны)]
Шутка, но вот один на C# (требуется .NET-Framework).
using System;
using System.Text;
class Program {
static void Main(string[] args) {
int Digits = 100;
BigNumber x = new BigNumber(Digits);
BigNumber y = new BigNumber(Digits);
x.ArcTan(16, 5);
y.ArcTan(4, 239);
x.Subtract(y);
string pi = x.ToString();
Console.WriteLine(pi);
}
}
public class BigNumber {
private UInt32[] number;
private int size;
private int maxDigits;
public BigNumber(int maxDigits) {
this.maxDigits = maxDigits;
this.size = (int)Math.Ceiling((float)maxDigits * 0.104) + 2;
number = new UInt32[size];
}
public BigNumber(int maxDigits, UInt32 intPart)
: this(maxDigits) {
number[0] = intPart;
for (int i = 1; i < size; i++) {
number[i] = 0;
}
}
private void VerifySameSize(BigNumber value) {
if (Object.ReferenceEquals(this, value))
throw new Exception("BigNumbers cannot operate on themselves");
if (value.size != this.size)
throw new Exception("BigNumbers must have the same size");
}
public void Add(BigNumber value) {
VerifySameSize(value);
int index = size - 1;
while (index >= 0 && value.number[index] == 0)
index--;
UInt32 carry = 0;
while (index >= 0) {
UInt64 result = (UInt64)number[index] +
value.number[index] + carry;
number[index] = (UInt32)result;
if (result >= 0x100000000U)
carry = 1;
else
carry = 0;
index--;
}
}
public void Subtract(BigNumber value) {
VerifySameSize(value);
int index = size - 1;
while (index >= 0 && value.number[index] == 0)
index--;
UInt32 borrow = 0;
while (index >= 0) {
UInt64 result = 0x100000000U + (UInt64)number[index] -
value.number[index] - borrow;
number[index] = (UInt32)result;
if (result >= 0x100000000U)
borrow = 0;
else
borrow = 1;
index--;
}
}
public void Multiply(UInt32 value) {
int index = size - 1;
while (index >= 0 && number[index] == 0)
index--;
UInt32 carry = 0;
while (index >= 0) {
UInt64 result = (UInt64)number[index] * value + carry;
number[index] = (UInt32)result;
carry = (UInt32)(result >> 32);
index--;
}
}
public void Divide(UInt32 value) {
int index = 0;
while (index < size && number[index] == 0)
index++;
UInt32 carry = 0;
while (index < size) {
UInt64 result = number[index] + ((UInt64)carry << 32);
number[index] = (UInt32)(result / (UInt64)value);
carry = (UInt32)(result % (UInt64)value);
index++;
}
}
public void Assign(BigNumber value) {
VerifySameSize(value);
for (int i = 0; i < size; i++) {
number[i] = value.number[i];
}
}
public override string ToString() {
BigNumber temp = new BigNumber(maxDigits);
temp.Assign(this);
StringBuilder sb = new StringBuilder();
sb.Append(temp.number[0]);
sb.Append(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);
int digitCount = 0;
while (digitCount < maxDigits) {
temp.number[0] = 0;
temp.Multiply(100000);
sb.AppendFormat("{0:D5}", temp.number[0]);
digitCount += 5;
}
return sb.ToString();
}
public bool IsZero() {
foreach (UInt32 item in number) {
if (item != 0)
return false;
}
return true;
}
public void ArcTan(UInt32 multiplicand, UInt32 reciprocal) {
BigNumber X = new BigNumber(maxDigits, multiplicand);
X.Divide(reciprocal);
reciprocal *= reciprocal;
this.Assign(X);
BigNumber term = new BigNumber(maxDigits);
UInt32 divisor = 1;
bool subtractTerm = true;
while (true) {
X.Divide(reciprocal);
term.Assign(X);
divisor += 2;
term.Divide(divisor);
if (term.IsZero())
break;
if (subtractTerm)
this.Subtract(term);
else
this.Add(term);
subtractTerm = !subtractTerm;
}
}
}
Вместо того, чтобы определять пи как константу, я всегда использую acos(-1).
cos (-1) или acos (-1)? :-P Это (последний) - один из тестовых примеров в моем исходном коде. Это один из моих предпочтительных (наряду с atan2 (0, -1), который на самом деле совпадает с acos (-1), за исключением того, что acos обычно реализуется в терминах atan2), но некоторые компиляторы оптимизируют для 4 * atan (1) !
Если вы хотите использовать приближение, 355 / 113 подходит для 6 десятичных цифр и имеет дополнительное преимущество, заключающееся в том, что его можно использовать с целочисленными выражениями. В наши дни это не так важно, потому что «математический сопроцессор с плавающей запятой» перестал иметь какое-либо значение, но когда-то это было очень важно.
В интересах полноты, версия шаблона C++, которая для оптимизированной сборки будет вычислять приближение PI во время компиляции и будет встроена в одно значение.
#include <iostream>
template<int I>
struct sign
{
enum {value = (I % 2) == 0 ? 1 : -1};
};
template<int I, int J>
struct pi_calc
{
inline static double value ()
{
return (pi_calc<I-1, J>::value () + pi_calc<I-1, J+1>::value ()) / 2.0;
}
};
template<int J>
struct pi_calc<0, J>
{
inline static double value ()
{
return (sign<J>::value * 4.0) / (2.0 * J + 1.0) + pi_calc<0, J-1>::value ();
}
};
template<>
struct pi_calc<0, 0>
{
inline static double value ()
{
return 4.0;
}
};
template<int I>
struct pi
{
inline static double value ()
{
return pi_calc<I, I>::value ();
}
};
int main ()
{
std::cout.precision (12);
const double pi_value = pi<10>::value ();
std::cout << "pi ~ " << pi_value << std::endl;
return 0;
}
Обратите внимание, что для I> 10 оптимизированные сборки могут быть медленными, как и для неоптимизированных запусков. Я считаю, что для 12 итераций существует около 80 тыс. Вызовов value () (при отсутствии запоминания).
Я запускаю это и получаю "pi ~ 3.14159265383"
Что ж, это с точностью до 9dp. Вы возражаете против чего-то или просто делаете наблюдение?
как называется алгоритм, используемый здесь для вычисления PI?
@ sebastião-miranda Формула Лейбница, с усреднением ускорения улучшить сходимость. pi_calc<0, J> вычисляет каждый последующий член формулы, а неспециализированный pi_calc<I, J> вычисляет среднее значение.
Если вы хотите, чтобы вычислить было приближением значения π (по какой-то причине), вам следует попробовать алгоритм двоичного извлечения. Белларда улучшение BBP дает PI за O (N ^ 2).
Если вы хотите, чтобы получать было приближенным значением π для выполнения вычислений, тогда:
PI = 3.141592654
Конечно, это только приблизительное значение и не совсем точное значение. Это меньше, чем на 0,00000000004102. (четыре десятиллионных, примерно 4 / 10,000,000,000).
Если вы хотите выполнить математика с π, возьмите карандаш и бумагу или пакет компьютерной алгебры и используйте точное значение π, π.
Если вам действительно нужна формула, это весело:
Ваша формула зависит от того, как вы определяете ln в комплексной плоскости. Она не должна быть непрерывной вдоль одной линии в комплексной плоскости, и довольно часто эта линия является отрицательной действительной осью.
С парными:
4.0 * (4.0 * Math.Atan(0.2) - Math.Atan(1.0 / 239.0))
Это будет точно до 14 знаков после запятой, что достаточно для заполнения двойного числа (неточность, вероятно, связана с усечением остальных десятичных знаков в арктангенсах).
Также Сет, это 3.141592653589793238463, а не 64.
Используйте формулу типа Мачина
176 * arctan (1/57) + 28 * arctan (1/239) - 48 * arctan (1/682) + 96 * arctan(1/12943)
[; \left( 176 \arctan \frac{1}{57} + 28 \arctan \frac{1}{239} - 48 \arctan \frac{1}{682} + 96 \arctan \frac{1}{12943}\right) ;], for you TeX the World people.
Реализовано в схеме, например:
(+ (- (+ (* 176 (atan (/ 1 57))) (* 28 (atan (/ 1 239)))) (* 48 (atan (/ 1 682)))) (* 96 (atan (/ 1 12943))))
Следующие ответы как это сделать максимально быстро - с наименьшими вычислительными усилиями. Даже если вам не нравится ответ, вы должны признать, что это действительно самый быстрый способ получить значение PI.
Способ САМЫЙ БЫСТРЫЙ получить значение Пи:
1) выберите свой любимый язык программирования 2) загрузите свою математическую библиотеку 3) и обнаруживаем, что Pi уже определен там - готов к использованию!
Если у вас под рукой нет математической библиотеки ..
Способ ВТОРОЙ БЫСТРЫЙ (более универсальное решение):
найдите Pi в Интернете, например здесь:
http://www.eveandersson.com/pi/digits/1000000 (1 миллион цифр .. какова ваша точность с плавающей запятой?)
или здесь:
http://3.141592653589793238462643383279502884197169399375105820974944592.com/
или здесь:
http://en.wikipedia.org/wiki/Pi
Очень быстро найти нужные цифры для любой точной арифметики, которую вы хотели бы использовать, и, определив константу, вы можете быть уверены, что не тратите драгоценное время процессора.
Это не только отчасти юмористический ответ, но и в действительности, если бы кто-нибудь пошел дальше и вычислил значение Пи в реальном приложении ... это было бы довольно большой тратой времени ЦП, не так ли? По крайней мере, я не вижу настоящего приложения для повторного вычисления этого.
Уважаемый модератор: обратите внимание, что OP спросил: «Самый быстрый способ получить значение PI»
Уважаемый Тило: обратите внимание, что OP сказал: «Я ищу самый быстрый способ получить значение π, как личный вызов. В частности, я использую способы, которые не связаны с использованием констант #define, таких как M_PI, или жесткого кодирования числа в.
Уважаемый @Max: обратите внимание, что OP отредактировал их исходный вопрос после Я ответил на него - вряд ли это моя вина;) Мое решение по-прежнему является самым быстрым способом и решает проблему с любой желаемой точностью с плавающей запятой и элегантно без циклов процессора :)
Ой, извините, я не понял. Просто подумайте, не будет ли у жестко запрограммированных констант меньше точности, чем у вычисления числа пи? Я предполагаю, что это зависит от того, на каком языке это язык и насколько автор хочет ввести все цифры :-)
Вычисление π из площади круга :-)
<input id = "range" type = "range" min = "10" max = "960" value = "10" step = "50" oninput = "calcPi()">
<br>
<div id = "cont"></div>
<script>
function generateCircle(width) {
var c = width/2;
var delta = 1.0;
var str = "";
var xCount = 0;
for (var x=0; x <= width; x++) {
for (var y = 0; y <= width; y++) {
var d = Math.sqrt((x-c)*(x-c) + (y-c)*(y-c));
if (d > (width-1)/2) {
str += '.';
}
else {
xCount++;
str += 'o';
}
str += " "
}
str += "\n";
}
var pi = (xCount * 4) / (width * width);
return [str, pi];
}
function calcPi() {
var e = document.getElementById("cont");
var width = document.getElementById("range").value;
e.innerHTML = "<h4>Generating circle...</h4>";
setTimeout(function() {
var circ = generateCircle(width);
e.innerHTML = "<pre>" + "π = " + circ[1].toFixed(2) + "\n" + circ[0] +"</pre>";
}, 200);
}
calcPi();
</script>Чтобы получить вывод стандартных констант, таких как число Пи, или стандартных концепций, мы должны сначала использовать встроенные методы, доступные на используемом вами языке. Он вернет значение самым быстрым и лучшим способом. Я использую python, чтобы максимально быстро получить значение числа пи.
math_pi.py
import math
print math.pi
Запускаем скрипт с утилитой времени linux /usr/bin/time -v python math_pi.py
Выход:
Command being timed: "python math_pi.py"
User time (seconds): 0.01
System time (seconds): 0.01
Percent of CPU this job got: 91%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03
acos_pi.py
import math
print math.acos(-1)
Запускаем скрипт с утилитой времени linux /usr/bin/time -v python acos_pi.py
Выход:
Command being timed: "python acos_pi.py"
User time (seconds): 0.02
System time (seconds): 0.01
Percent of CPU this job got: 94%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03
bbp_pi.py
from decimal import Decimal, getcontext
getcontext().prec=100
print sum(1/Decimal(16)**k *
(Decimal(4)/(8*k+1) -
Decimal(2)/(8*k+4) -
Decimal(1)/(8*k+5) -
Decimal(1)/(8*k+6)) for k in range(100))
Запускаем скрипт с утилитой времени linux /usr/bin/time -v python bbp_pi.py
Выход:
Command being timed: "python c.py"
User time (seconds): 0.05
System time (seconds): 0.01
Percent of CPU this job got: 98%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.06
Итак, лучший способ - использовать встроенные методы, предоставляемые языком, потому что они самые быстрые и лучшие для получения результата. В python используйте math.pi
Алгоритм Чудновского довольно быстр, если вы не против вычисления квадратного корня и пары обратных чисел. Он сходится к двойной точности всего за 2 итерации.
/*
Chudnovsky algorithm for computing PI
*/
#include <iostream>
#include <cmath>
using namespace std;
double calc_PI(int K=2) {
static const int A = 545140134;
static const int B = 13591409;
static const int D = 640320;
const double ID3 = 1./ (double(D)*double(D)*double(D));
double sum = 0.;
double b = sqrt(ID3);
long long int p = 1;
long long int a = B;
sum += double(p) * double(a)* b;
// 2 iterations enough for double convergence
for (int k=1; k<K; ++k) {
// A*k + B
a += A;
// update denominator
b *= ID3;
// p = (-1)^k 6k! / 3k! k!^3
p *= (6*k)*(6*k-1)*(6*k-2)*(6*k-3)*(6*k-4)*(6*k-5);
p /= (3*k)*(3*k-1)*(3*k-2) * k*k*k;
p = -p;
sum += double(p) * double(a)* b;
}
return 1./(12*sum);
}
int main() {
cout.precision(16);
cout.setf(ios::fixed);
for (int k=1; k<=5; ++k) cout << "k = " << k << " PI = " << calc_PI(k) << endl;
return 0;
}
Полученные результаты:
k = 1 PI = 3.1415926535897341
k = 2 PI = 3.1415926535897931
k = 3 PI = 3.1415926535897931
k = 4 PI = 3.1415926535897931
k = 5 PI = 3.1415926535897931
В основном ответ оптимизатора скрепок для C и гораздо более упрощенный:
#include <stdio.h>
#include <math.h>
double calc_PI(int K) {
static const int A = 545140134;
static const int B = 13591409;
static const int D = 640320;
const double ID3 = 1.0 / ((double) D * (double) D * (double) D);
double sum = 0.0;
double b = sqrt(ID3);
long long int p = 1;
long long int a = B;
sum += (double) p * (double) a * b;
for (int k = 1; k < K; ++k) {
a += A;
b *= ID3;
p *= (6 * k) * (6 * k - 1) * (6 * k - 2) * (6 * k - 3) * (6 * k - 4) * (6 * k - 5);
p /= (3 * k) * (3 * k - 1) * (3 * k - 2) * k * k * k;
p = -p;
sum += (double) p * (double) a * b;
}
return 1.0 / (12 * sum);
}
int main() {
for (int k = 1; k <= 5; ++k) {
printf("k = %i, PI = %.16f\n", k, calc_PI(k));
}
}
Но для большего упрощения этот алгоритм использует формулу Чудновского, которую я могу полностью упростить, если вы действительно не понимаете код.
Резюме: мы получим число от 1 до 5 и добавим его к функции, которую мы будем использовать для получения PI. Затем вам дается 3 числа: 545140134 (A), 13591409 (B), 640320 (D). Затем мы будем использовать D как double, умножая себя 3 раза на другой double (ID3). Затем мы возьмем квадратный корень из ID3 в другой double (b) и присвоим 2 числа: 1 (p), значение B (a). Обратите внимание, что C не чувствителен к регистру. Затем будет создана double (сумма) путем умножения значений p, a и b, все в doubles. Затем цикл до тех пор, пока число, указанное для функции, не запустится и добавит значение A к a, значение b будет умножено на ID3, значение p будет умножено на несколько значений, которые, я надеюсь, вы поймете, а также разделится на несколько значений как Что ж. Сумма снова сложится на p, a и b, и цикл будет повторяться до тех пор, пока значение номера цикла не станет больше или равно 5. Позже сумма умножается на 12 и возвращается функцией, дающей нам результат ЧИСЛО ПИ.
Ладно, это было долго, но я думаю, вы это поймете ...
Почему вы считаете использование atan (1) отличным от использования M_PI? Я бы понял, почему вы хотите это сделать, если вы использовали только арифметические операции, но с atan я не вижу в этом смысла.