Problema con ciclos (ejercicio de pi)

Iniciado por xuhipoint, 29 Marzo 2014, 01:48 AM

0 Miembros y 1 Visitante están viendo este tema.

xuhipoint

El ejercicio a programar es el siguiente:
Desarrolla una función que calcule el valor de π a partir de una serie indicada a
continuación, de la cual calcularemos la cantidad de términos indicados por el usuario.
π = 4 – 4/3 + 4/5 – 4/7 + 4/9 - ... ± 4/n
Mi idea fue crear el siguiente algoritmo:
Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;
int main (){
int b=0,a=1,i,c=0 ;
int n;
cout<<"Ingrese el numero"<<endl;
cin>>n;
if(n%2==0){
cout<<"numero invalido"<<endl;
}else{
for(i=1;i<=n;i++){
if(i%2==0){
while(a<=n){
b=b-(4/a);
                 a=a+2;
}
cout<<b<<endl;
}else{
while(a<=n){
c=c+(4/a);
a=a+2;
}
cout<<c<<endl;
}
}
}
}

Lo que no entiendo es porque no me da, según mi lógica y un pseudocodigo que hice eso debería funcionar...Por favor, podrían decirme en que me equivoque.

engel lex

#1
uff una serie de errores XD...


primero
Código (cpp) [Seleccionar]
if(n%2==0){
cout<<"numero invalido"<<endl;


por que si es par es invalido? es una progresion, toma presicion al avanzar, la paridad no importa en valores altos...

Código (cpp) [Seleccionar]
int b=0,a=1,i,c=0 ;
for(i=1;i<=n;i++){
 if(i%2==0){
   while(a<=n){
     b=b-(4/a);
     a=a+2;
   }
   cout<<b<<endl;
 }else{
   while(a<=n){
     c=c+(4/a);
     a=a+2;
   }
   cout<<c<<endl;
 }
}


el error es que es una sumatoria.... una UNICA sumatoria.... tu lo que estás haciendo son 2 sumatorias... una negativa y otra positiva... para ambos debe ser "b" el valor afectado

otra cosa...

sabemos que pi es un numero decimal, especialmente esta operación requiere de decimales... si te fijas la haces con enteros.... jamás dará resultado...




personalmente lo habría hecho así :P (super compacto XD)

Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

int main(){
   int precision, i = 0;
   float pi = 4;
   cout << "ingrese un grado de precision: ";
   cin >> precision;
   while (i++ < precision)pi += ((i%2==0?4:-4)/(1+i*2.0f));
   printf ("\npi es: %.10f \n" , pi);
   system("pause");
   return 0;  
}


desgloso un poquito:
-en la división hago algo similar a

Código (cpp) [Seleccionar]
if(i%2==0) {
   pi = pi + (4 / (i*2.0f) );
}else{
   pi = pi - (4 / (i*2.0f) );
}


este 2.0f es para que se force como flotante el resultado de la división, si no, la división quedará como entero (por ejemplo si haces 4/i, al ser i entero, el resultado es entero)

-en la impresión de datos no uso cout, sino printf con el valor "%.10f" esto significa que va a imprimir 10 decimales (aunque sean ceros)

espero sea de ayuda inténtalo correr con un valor de 1000000000 :P mi maquina lo corre en unos 20 seg y el valor dudo que lo puedas tener más aproximado XD
El problema con la sociedad actualmente radica en que todos creen que tienen el derecho de tener una opinión, y que esa opinión sea validada por todos, cuando lo correcto es que todos tengan derecho a una opinión, siempre y cuando esa opinión pueda ser ignorada, cuestionada, e incluso ser sujeta a burla, particularmente cuando no tiene sentido alguno.

leosansan

#2
Cita de: xuhipoint en 29 Marzo 2014, 01:48 AM
El ejercicio a programar es el siguiente:
Desarrolla una función que calcule el valor de π a partir de una serie indicada a
continuación, de la cual calcularemos la cantidad de términos indicados por el usuario.
π = 4 – 4/3 + 4/5 – 4/7 + 4/9 - ... ± 4/n
..................................................
Lo que no entiendo es porque no me da, según mi lógica y un pseudocodigo que hice eso debería funcionar...Por favor, podrían decirme en que me equivoque.

* Para empezar las instrucciones:

Código (cpp) [Seleccionar]
if(n%2==0){
 cout<<"numero invalido"<<endl;



están de más ya que se puede pedir un número par de términos de la sucesión. Otra cosa es que los términos de la sucesión sólo contengan números impares, pero yo puedo pedir tres o cuatro términos.

* En:


Código (cpp) [Seleccionar]
if(i%2==0){
 while(a<=n){
   b=b-(4/a);
   a=a+2;
 }


te sobra el while. Tal como lo tienes calcularías para cada i todos los términos de la sucesión. Y en el otro while lo mismo, además no tienes por cambiar el nombre de la variable, sigue usando b en lugar de c.

* Deberías declarar como float, ya que son valores que implican el cálculo de  decimales, a las variables b y a.

* Te falta el return de la función main e indentar el código de forma correcta.

Con esto el código corregido y funcionando;

Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;
int main (){
 int n,i ;
 float  b=4,a=3;
 cout<<"Ingrese el numero de terminos: ";
 cin>>n;
 cout<<endl<<endl<<"a["<<1<< "]= "<<b<<endl;
 for(i=1;i<n;i++){
   if(i%2!=0){
     b=b-(4/a);
     a=a+2;
     cout<<"a["<<i+1<< "]= "<<b<<endl;
   }
   else{
     b=b+(4/a);
     a=a+2;
     cout<<"a["<<i+1<< "]= "<<b<<endl;
   }
 }
 return 0;
}}


También podrías habar hecho un for que recorra sólo los impares y te ahorrarías un else y el uso del operador "%":

Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;
int main (){
  int N,n,signo=-1 ;
  float  b=1,i;
  cout<<"Ingrese el numero de terminos: ";
  cin>>n;
  N=2*n-1;
  for(i=3;i<=N;i+=2,signo*=-1)
    b+=(1/i)*signo;
  cout<<"Pi= " <<4*b<<endl;
  return 0;
}


Citar
Ingrese el numero de terminos: 9


a[1]= 4
a[2]= 2.66667
a[3]= 3.46667
a[4]= 2.89524
a[5]= 3.33968
a[6]= 2.97605
a[7]= 3.28374
a[8]= 3.01707
a[9]= 3.25237

Process returned 0 (0x0)


Claro que si de lo que se trata es de calcular Pi y no es necesario imprimir cada termino del sumatorio, podrías hacer:

Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;
int main (){
 int n,signo=-1 ;
 float  b=1,i;
 cout<<"Ingrese el numero de terminos: ";
 cin>>n;
 for(i=3;i<=2*n-1;i+=2,signo*=-1)
   b+=(1/i)*signo;
 cout<<"Pi= " <<4*b<<endl;
 return 0;
}


Citar
Ingrese el numero de terminos: 1000000
Pi= 3.1416

Process returned 0 (0x0)   execution time

¡¡¡¡ Saluditos! ..... !!!!



 

Yoel Alejandro

Bueno, reconociendo el término general de la sumatoria (algo que siempre es bueno hacer):

  4 * (-1)^n
--------------
  2 * (n - 1)

para n desde 1 hasta infinito (o hasta el máximo N impuesto por el usuario), quedaría el código siguiente donde la idea (debida a leosansan en un post similar) de usar una bandera de signo que conmute de +1 a -1 en cada iteración nos evita tener que estar viendo si n es par o impar, quedaría:
Código (cpp) [Seleccionar]

int main()
{
   double numero;
   int n, N;
   int signo;

   cout << "numero de terminos: ";
   cin >> N;
   cout << endl;

   numero = 4;
   signo = -1;
   for ( n = 1; n <= N; i++, signo*=-1 ) {
      cout << "a[" << i << "] = " << numero << endl;
      numero += signo * 4. / (2*n + 1);
   }

   return 0;
}


donde el punto en "4." obliga la división entre doubles en lugar de enteros (gracias de nuevo por la acotación leosansan).
Saludos, Yoel.
P.D..-   Para mayores dudas, puedes enviarme un mensaje personal (M.P.)

leosansan

#4
Amigo yoel_alejandro de corazón que todo es en plan buen rollo, nada de suspicacias, please!.

Antes que nada creo que se te fue un gazapo en la expresión:

Cita de: yoel_alejandro en 29 Marzo 2014, 17:48 PM
...............................................................

 4 * (-1)^n
--------------
 2 * (n - 1)

..........................................

Creo que tu intención era poner:

Citar

 4 * (-1)^n
--------------
(2*n - 1)


Por otro lado:

Cita de: yoel_alejandro en 29 Marzo 2014, 17:48 PM
.......................................
donde el punto en "4." obliga la división entre doubles en lugar de enteros (gracias de nuevo por la acotación leosansan).


Esta vez no me ha hecho falta lo del "4.", es decir lo del punto decimal, al declarar la variable i como float.

Por otro lado, al ser el mismo problema que en el otro tema del número Pi, he observado esta vez que me puedo ahorrar dos operaciones en cada ciclo del bucle for usando 1/i donde i es impar al empezar el bucle en tres e incrementar de dos en dos:


Código (cpp) [Seleccionar]
..................................
 N=2*n-1;
 for(i=3;i<=N;i+=2,signo*=-1)
    b+=(1/i)*signo;
 ......................................


que se corresponden con las que hacíamos en 2*n-1: una multiplicación y una resta en cada ciclo. Y teniendo en cuenta que para que la serie propuesta nos dé 3.1416 hay que tomar un millón, n=1 000 000, de sumandos, nos queda que con el nuevo for que propongo en el último mensaje, me ahorro dos millones de operaciones, de ahí que lo cambiará respecto del otro tema.

¡¡¡¡ Saluditos! ..... !!!!




engel lex

#5
tiempo de ejecucion... mi codigo...

Código (cpp) [Seleccionar]
#include <iostream>
#include <windows.h>
#include <time.h>
using namespace std;
int main(){
   int precision, i = 0;
   float pi = 4;
   cout << "ingrese un grado de precision: ";
   cin >> precision;
   clock_t tStart = clock();
   while (i++ < precision)pi += ((i%2==0?4:-4)/(1+i*2.0f));
   printf ("\npi es: %.10f \n" , pi);
   printf("Time taken: %.4fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);
   system("pause");
   return 0;  
}

numero introducido: 10.000.000
resultado: 3.1415970325
tiempo: 0.2270
(con 1.000.000.000 tarda 22 sec)

codigo leosansan (modif para el tiempo)

Código (cpp) [Seleccionar]
#include <iostream>
#include <windows.h>
#include <time.h>
using namespace std;

int main(){
   int n,signo=-1 ;
   float  b=1,i;
   cout<<"Ingrese el numero de terminos: ";
   cin>>n;
   clock_t tStart = clock();
   for(i=3;i<=2*n-1;i+=2,signo*=-1)
       b+=(1/i)*signo;
   cout<<"Pi= " <<4*b<<endl;
   printf("Time taken: %.4fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);
   system("pause");
   return 0;  
}

numero introducido: 10.000.000
resultado: 3.1416
tiempo: 0.0980
->anotacion... si metes 100.000.000 o más pasa más de 50 segundos sin hacer nada, me cansé de esperar


el codigo de yoel_alejando (modificado un par de errores, con "n" e "i" y que solo haga una impresion final

Código (cpp) [Seleccionar]
#include <iostream>
#include <windows.h>
#include <time.h>
using namespace std;

int main(){
   double numero;
  int n, N;
  int signo;
  cout << "numero de terminos: ";
  cin >> N;
  cout << endl;
   clock_t tStart = clock();
   numero = 4;
  signo = -1;
  for ( n = 1; n <= N; n++, signo*=-1 ) {
     numero += signo * 4. / (2*n + 1);
  }
  cout << "pi es: " << numero << endl;
   printf("Time taken: %.4fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);
   system("pause");
   return 0;  
}

numero introducido: 10.000.000
resultado: 3.14159
tiempo: 0.0960
(con 1.000.000.000 tarda 10 sec)

el codigo de  yoel_alejandro es el ganador :P quedó muy eficiente :P ya veo que hiperresumir todo no es tan bueno

chicos usen printf es por el bien de todos o bueno de todos los decimales que fueron calculados en vano! XD
El problema con la sociedad actualmente radica en que todos creen que tienen el derecho de tener una opinión, y que esa opinión sea validada por todos, cuando lo correcto es que todos tengan derecho a una opinión, siempre y cuando esa opinión pueda ser ignorada, cuestionada, e incluso ser sujeta a burla, particularmente cuando no tiene sentido alguno.

leosansan



Es por pasar el tiempo, ¡ehh!.

Las salidas de leosansan, engel lex y  yoel_alejandro por un millon, diez y mil millones, pero eso si en las mismas condiciones , incluso en el tipo de variables para lo que tuve que modificar mi código y declarar i como int, así estamos todos iguales:




















Como se ve estamos todos en un puño sin apenas diferencias.


¡¡¡¡ Saluditos! ..... !!!!




engel lex

leosansan XD lo hice por ocio XD pero a mi por alguna razon en todas las corridas la de yoel me daba unos 10 segundos menos que el mio en 1.000.000.000 y el tuyo no andaba en más de 10.000.000 D:
El problema con la sociedad actualmente radica en que todos creen que tienen el derecho de tener una opinión, y que esa opinión sea validada por todos, cuando lo correcto es que todos tengan derecho a una opinión, siempre y cuando esa opinión pueda ser ignorada, cuestionada, e incluso ser sujeta a burla, particularmente cuando no tiene sentido alguno.

leosansan

#8
El motivo que veo es que tenia declarada i como float. Al hacerlo como int todo va sobre ruedas.

Lo cierto es lo que comenté, estamos en centésimas de diferencias. ;)



¡¡¡¡ Saluditos! ..... !!!!



Yoel Alejandro

Vaya, ustedes sí que tienen ocio de ponerse a medir el tiempo con cada uno de los algoritmos, jaja!

Bueno, yo uso la filosofía del mínimo recurso, me parece lo más lógico declarar n como entero, pues la función que realiza es la de un entero. Tengan en cuenta que comparar dos float es más complicado, a nivel de máquina, que comparar dos enteros. Los float llevan mantisa y exponente, y supongo que nivel de bits primero se comparan los exponentes, y si son iguales se pasa a las mantisas. En cambio en los enteros se comparan todos sus bits de una vez y punto.

A pesar de ello yo hubiera supuesto que el manejo de leosansan, de sumar dos al contador n+=2 sería más eficiente que calcular 2*n+1 (suma y multiplicación incluida). Pero el ensayo demostró que fueron equivalentes, jeje, no se por qué.

Por otra parte, es cierto que resumir el código fuente no necesariamente reduce el tiempo de ejecución. Para lo segundo hay que ponerse a pensar en lo que traduce el compilador, y por ende la manera como se ejecuta el código a nivel de la máquina (los tipos de datos involucrados, etc). Es un punto que precisamente yo venía sosteniendo desde otro post.

Por supuesto, estas son cosas para entretenernos los usuarios más avanzados, no son cosas para considerar aún por la autora del tema quién está apenas comenzando a programar (algún día lo hará), jeje.

Saludos a todos  :D

Saludos, Yoel.
P.D..-   Para mayores dudas, puedes enviarme un mensaje personal (M.P.)