Domanda

Come posso calcolare il valore di PI usando C#?

Stavo pensando che sarebbe attraverso una funzione ricorsiva, in tal caso, come sarebbe e ci sono equazioni matematiche a sostegno?

Non sono troppo esigente riguardo alle prestazioni, soprattutto su come affrontarle dal punto di vista dell'apprendimento.

È stato utile?

Soluzione

Se vuoi la ricorsione:

PI = 2 * (1 + 1/3 * (1 + 2/5 * (1 + 3/7 * (...))))

Questo diventerebbe, dopo qualche riscrittura:

PI = 2 * F(1);

con F(i):

double F (int i) {
    return 1 + i / (2.0 * i + 1) * F(i + 1);
}

Isaac Newton (potresti averne sentito parlare prima ;)) ha inventato questo trucco.Tieni presente che ho omesso la condizione finale, per mantenerla semplice.Nella vita reale, ne hai bisogno.

Altri suggerimenti

Che ne dici di usare:

double pi = Math.PI;

Se desideri una precisione migliore, dovrai utilizzare un sistema algoritmico e il tipo Decimal.

Se dai un'occhiata più da vicino a questa ottima guida:

Modelli per la programmazione parallela:Comprensione e applicazione di modelli paralleli con .NET Framework 4

Troverai a pagina 70 questa simpatica implementazione (con piccole modifiche da parte mia):

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;
}

Ci sono un paio di trucchi davvero, davvero vecchi che sono sorpreso di non vedere qui.

atan(1) == PI/4, quindi una vecchia castagna quando una funzione arcotangente affidabile è presente è 4*atan(1).

Una stima molto carina e a rapporto fisso che fa sembrare sporco il vecchio Western 22/7 è 355/113, che è buono per diverse cifre decimali (almeno tre o quattro, credo).In alcuni casi, questo è sufficiente anche per l'aritmetica degli interi:moltiplicare per 355 quindi dividere per 113.

355/113 è anche facile da ricordare (almeno per alcuni):conta uno, uno, tre, tre, cinque, cinque e ricorda che stai nominando le cifre del denominatore e del numeratore (se dimentichi quale terzina va in cima, il pensiero di un microsecondo di solito lo raddrizzerà).

Tieni presente che 22/7 ti offre:3.14285714, che è sbagliato ai millesimi.

355/113 ti dà 3,14159292 che non è sbagliato fino ai diecimilionesi.

acc.a /usr/include/math.h sul mio box, M_PI è #define' come:3.14159265358979323846 il che è probabilmente un bene per quanto vada.

La lezione che si ottiene dalla stima di PI è che ci sono molti modi per farlo, Nessuno sarà mai perfetto e devi ordinarli in base all'uso previsto.

355/113 è una vecchia stima cinese e credo che sia anteriore a 22/7 di molti anni.Mi è stato insegnato da un professore di fisica quando ero studente.

Buona panoramica dei diversi algoritmi:

Non sono sicuro della complessità dichiarata per l'algoritmo Gauss-Legendre-Salamin nel primo collegamento (direi O(N log^2(N) log(log(N)))).

Ti incoraggio a provarlo, però, la convergenza è Veramente veloce.

Inoltre, non sono davvero sicuro del motivo per cui provare a convertire un algoritmo procedurale abbastanza semplice in uno ricorsivo?

Tieni presente che se sei interessato alle prestazioni, allora lavori con una precisione limitata (in genere, richiedendo un 'double', 'float',...output) non ha davvero senso, poiché la risposta ovvia in questo caso è semplicemente codificare il valore.

Ecco un articolo sul calcolo del PI in C#:

http://www.boyet.com/Articles/PiCalculator.html

Cos'è il PI?La circonferenza di un cerchio divisa per il suo diametro.

Nella computer grafica puoi tracciare/disegnare un cerchio con il centro a 0,0 a partire da un punto iniziale x,y, il punto successivo x',y' può essere trovato utilizzando una semplice formula:x' = x + y / h :y' = y - x' / h

h è solitamente una potenza di 2, quindi la divisione può essere eseguita facilmente con uno spostamento (o sottraendo dall'esponente su un doppio).h vuole anche essere il raggio r della tua circonferenza.Un punto di partenza semplice sarebbe x = r, y = 0, quindi contare c il numero di passi fino a x <= 0 per tracciare un quarto di cerchio.PI è 4 * c / r o PI è 4 * c / h

La ricorsione a grande profondità è solitamente poco pratica per un programma commerciale, ma la ricorsione in coda consente di esprimere un algoritmo in modo ricorsivo, mentre è implementato come un ciclo.Gli algoritmi di ricerca ricorsiva a volte possono essere implementati utilizzando una coda anziché lo stack del processo, la ricerca deve tornare indietro da un vicolo cieco e prendere un altro percorso: questi punti di backtrack possono essere messi in coda e più processi possono annullare la coda dei punti e provare altri percorsi.

Calcola così:

x = 1 - 1/3 + 1/5 - 1/7 + 1/9  (... etc as far as possible.)
PI = x * 4

Hai Pi !!!

Questo è il metodo più semplice che conosco.

Il valore di PI converge lentamente al valore effettivo di Pi (3,141592165......).Se ripeti più volte, meglio è.

Ecco un approccio carino (da la voce principale di Wikipedia su pi);converge molto più velocemente della semplice formula discussa sopra ed è abbastanza suscettibile a una soluzione ricorsiva se il tuo intento è perseguire la ricorsione come esercizio di apprendimento.(Supponendo che tu stia cercando l'esperienza di apprendimento, non fornirò alcun codice effettivo.)

La formula sottostante è la stessa di sopra, ma questo approccio calcola la media delle somme parziali per accelerare la convergenza.

Definire una funzione a due parametri, pie(h, w), tale che:

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

Quindi la tua prima opportunità per esplorare la ricorsione è codificare quel calcolo "orizzontale" man mano che il parametro "larghezza" aumenta (per "altezza" pari a zero).

Quindi aggiungi la seconda dimensione con questa formula:

pie(h, w) = (pie(h-1,w) + pie(h-1,w+1)) / 2

che viene utilizzato, ovviamente, solo per valori di h maggiori di zero.

La cosa bella di questo algoritmo è che puoi facilmente simularlo con un foglio di calcolo per controllare il tuo codice mentre esplori i risultati prodotti da parametri progressivamente più grandi.Quando calcoli la torta (10,10), avrai un valore approssimativo di pi greco sufficientemente buono per la maggior parte degli scopi ingegneristici.

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;
    }

In qualsiasi scenario di produzione, ti obbligherei a cercare il valore, al numero desiderato di punti decimali, e a memorizzarlo come "const" da qualche parte in cui le tue classi possano arrivarci.

(a meno che tu non stia scrivendo un software scientifico specifico per "Pi"...)

Per quanto riguarda...

...come procedere dal punto di vista dell'apprendimento.

Stai cercando di imparare a programmare metodi scientifici?o per produrre software di produzione?Spero che la community la consideri una domanda valida e non un pignolo.

In entrambi i casi, penso che scrivere il tuo Pi sia un problema risolto.Dmitry ha già mostrato la costante "Math.PI".Attacca un altro problema nello stesso spazio!Scegli approssimazioni di Newton generiche o qualcosa di fluido.

@Thomas Kammeyer:

Nota che Atan(1.0) è molto spesso hardcoded, quindi 4*Atan(1.0) non è realmente un 'algoritmo' se stai chiamando una funzione Atan della libreria (alcuni già suggeriscono infatti di procedere sostituendo Atan(x) con una serie (o prodotto infinito) per esso, quindi valutandolo in x=1.

Anche, ci sono pochissimi casi in cui avresti bisogno di pi greco con una precisione maggiore di poche decine di bit (che può essere facilmente codificato!).Ho lavorato su applicazioni in matematica in cui, per calcolare alcuni oggetti matematici (piuttosto complicati) (che erano polinomiali con coefficienti interi), dovevo fare aritmetica su numeri reali e complessi (incluso il calcolo del pi greco) con una precisione fino a un qualche milione di bit...ma questo non è molto frequente "nella vita reale" :)

Puoi cercare il seguente esempio codice.

mi piace questo articolo, che spiega come calcolare π sulla base di uno sviluppo in serie di Taylor per Arcotangente.

Il documento inizia con il semplice presupposto che

Atan(1) = π/4 radianti

Atan(x) può essere stimato iterativamente con la serie di Taylor

atan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9...

L'articolo sottolinea il motivo per cui ciò non è particolarmente efficiente e prosegue apportando una serie di perfezionamenti logici alla tecnica.Forniscono anche un programma di esempio che calcola π fino a poche migliaia di cifre, completo di codice sorgente, comprese le routine matematiche a precisione infinita richieste.

Il seguente link mostra come calcolare la costante pi greco in base alla sua definizione come integrale, che può essere scritta come limite di una sommatoria, è molto interessante:https://sites.google.com/site/rcorcs/posts/calculatingthepiconstantIl file "Pi come integrale" spiega questo metodo utilizzato in questo post.

Innanzitutto, tieni presente che C# può utilizzare il campo Math.PI del framework .NET:

https://msdn.microsoft.com/en-us/library/system.math.pi(v=vs.110).aspx

La caratteristica interessante qui è che si tratta di un doppio a precisione completa che puoi utilizzare o confrontare con i risultati calcolati.Le schede in quell'URL hanno costanti simili per C++, F# e Visual Basic.

Per calcolare più posti, puoi scrivere il tuo codice a precisione estesa.Uno che è veloce da codificare e ragionevolmente veloce e facile da programmare è:

Pi = 4 * [4 * arctan (1/5) - arctan (1/239)]

Questa formula e molte altre, incluse alcune che convergono a velocità sorprendentemente elevate, come 50 cifre per termine, si trovano in Wolfram:

Formule di Wolfram Pi

PI (π) può essere calcolato utilizzando serie infinita.Ecco due esempi:

Serie Gregory-Leibniz:

π/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - ...

Metodo 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;
}

Serie Nilakantha:

π = 3 + 4 / (2x3x4) - 4 / (4x5x6) + 4 / (6x7x8) - 4 / (8x9x10) + ...

Metodo 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;
}

Il parametro di input n per entrambe le funzioni rappresenta il numero di iterazioni.

La serie Nilakantha rispetto alla serie Gregory-Leibniz converge più rapidamente.I metodi possono essere testati con il seguente codice:

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();
}

Il seguente output mostra che la serie Nilakantha restituisce sei decimali corretti di PI con cento iterazioni mentre la serie Gregory-Leibniz restituisce cinque decimali corretti di PI con un milione di iterazioni:

enter image description here

Il mio codice può essere testato >> Qui

Ecco un modo carino:Calcola una serie di 1/x^2 per x da 1 a quello che desideri: maggiore è il numero, migliore sarà il risultato della torta.Moltiplicare il risultato per 6 e per sqrt().Ecco il codice in c# (solo principale):

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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top