What is wrong with my python code on Wallis product? - python

So, I was asked to code the Wallis product and it wasn't supposed to be really complicated. So I made a code but apparently, it could only work for Wallis(1) and not the rest. Could anyone help me? Thank you!
def Wallis (n):
result = 1
for count in range(2, n+2, 2):
result = result * (count**2/((count-1)*(count+1)))
return result
Formula to compute Wallis product
(2*2)/(3*5) * (4*4)/(5*7) * (6*6)/(7*9) and so on until ((n*2) ** 2)/ ((n-1)* (n+1))
Output
Wallis(1) = (2*2)/(3*5) = 0.267
Wallis(2) = Wallis(1) * (4*4)/(5*7) = 0.122

As others said, your range() needs to be changed.
For n=3,
for count in range(2, n+2, 2):
print(i)
would print
2
4
but you needed 6 as well.
As Python documentation says
class range(start, stop[, step])
For a positive step, the contents of a range r are determined by the formula r[i] = start + step*i where i >= 0 and r[i] < stop.
meaning stop is not included.
Here step is 2, you could use
for count in range(2, 2*(n+1), 2):
And exponentiation is costlier than multiplication. So
count*count
is better than
count**2
So the modified version could be
def Wallis(n):
result=1
for count in range(2, 2*(n+1), 2):
result*=((count*count)/((count-1)*(count+1)))
return result
Note that the value returned by Wallis is only half the value of Pi.
You could modify the return statement to
return result*2
if you want.

Mainly your range command is wrong:
range(2, 2*n+2, 2)
Alternatively, you can move the complexity to the formula, i.e.:
for count in range(1, n):
result = result * (4*count*count/((2*count-1)*(2*count+1)))

The problem with your code is that count is equal to 2*n in your equation so in (count**2/((count-1)*(count+1))), count**2 is the same as (2*n)**2 however the following count-1 should be the same as n-1 but rather it is actually (2*n)-1. The same goes for count+1.
I made my own version which should help you (although I used a different equation which is at https://www.wikiwand.com/en/Wallis_product)
def wallis(limit):
result = 1
for x in range(2, limit, 2):
result *= (x / (x - 1)) * (x / (x + 1))
return result
As limit gets higher, it converges closer to half pi.

Related

Infinite sum with given precision

I've been trying to solve this infinite sum with a given precision problem.
You can see the description in the picture below
Here's what I tried so far:
import math
def infin_sum(x, eps):
sum = float(0)
prev = ((-1)*(x**2))/2
i = 2
while True:
current = prev + ((-1)**i) * (x**(2*i)) / math.factorial(2*i)
if(abs(current - prev) <= eps):
print(current)
return current
prev = current
i+=1
For the given sample input (0.2 for x and 0.00001 precision) my sum is 6.65777777777778e-05 and according to their tests it doesn't come close enough to the correct answer
You should use math.isclose() instead of abs() to check your convergence (given that it's how the result will be checked). given that each iteration adds or subtract a specific term, the delta between previous and next (Si-1 vs Si) will be equal to the last term added (so you don't need to track a previous value).
That infinite series is almost the one for cosine (it would be if i started at zero) so you can test your result against math.cos(x)-1. Also, I find it strange that the check for expected result is fixed within a precision 0.0001 but the sample input specifies a precision of 0.00001 (I guess more precise will be within 0.0001 but then, the validation is not really checking that the output is correct given the input?)
from math import isclose
def cosMinus1(x,precision=0.00001):
result = 0
numerator = 1
denominator = 1
even = 0
while not isclose(numerator/denominator,0,abs_tol=precision): # reach precision
numerator *= -x*x # +/- for even powers of x
even += 2
denominator *= even * (even-1) # factorial of even numbers
result += numerator / denominator # sum of terms
return result
print(cosMinus1(0.2))
# -0.019933422222222226
import math
expected = math.cos(0.2)-1
print(expected, math.isclose(expected,cosMinus1(0.2),abs_tol=0.0001))
# -0.019933422158758374 True
Since it's not a good idea to shadow sum, you had the right idea in calling it current, but you didn't initialise current to float(0), and forgot to sum it. This is your code with those problems fixed:
def infin_sum(x, eps):
current = float(0)
prev = ((-1) * (x ** 2)) / 2
i = 2
while True:
current = current + (((-1) ** i) * (x ** (2 * i))) / math.factorial(2 * i)
if (abs(current - prev) <= eps):
print(current)
return current
prev = current
i += 1
As a more general comment, printing inside a function like this is probably not the best idea - it makes the reusability of the function limited - you'd want to capture the return value and print it outside the function, ideally.
First, you are not summing the elements.
Recomputing everything from scratch is very wasteful and precision of floats is limited.
You can user Horner's method to refine the sum:
import math
def infin_sum(x, eps):
total = float(0)
e = 1
total = 0
i = 1
while abs(e) >= eps:
diff = (-1) * (x ** 2) / (2 * i) / (2 * i - 1)
e *= diff
total += e
i += 1
return total
if __name__ == "__main__":
x = infin_sum(0.2, 0.00001)
print(x)
Dont forget to add the result and not replace it:
prev += current
instead of
prev = current

Calculating the sum of a series?

This is my assignment and for the life of me i cant seem to think of a way to do it. This is the code I have so far:
sum = 0
k = 1
while k <= 0.0001:
if k % 2 == 1:
sum = sum + 1.0/k
else:
sum = sum - 1.0/k
k = k + 1
print()
This is my assignment :
Create a python program named sumseries.py that does the following:
Put comments at the top of your program with your name, date, and
description of what the program does.
Write a program to calculate and display the sum of the series:
1 - 1/2 + 1/3 - 1/4 + ...
until a term is reached that is less than 0.0001.
The answer with 10,000 iterations appears to be 0.6930971830599583
I ran the program with 1,000,000,000 (billion) iterations and came up with a number of 0.6931471810606472. I need to create a loop to programmably create the series.
Actually, you could write this shorter:
Answer = sum(1.0 / k if k % 2 else -1.0 / k for k in range(1, 10001))
What this code does:
the innermost part is a generator expression, which computes the elements of a series 'on the fly'
1.0 / k if k % 2 else -1.0 / k results in 1.0 / k if k is odd and -1.0 / k otherwise (a - b is the same as a + (-b))
for k in range(1, 10001) goes through all ks in range from 1 (included) to 10001 (excluded)
sum can compute the sum of any sequence (any iterable, to be precise), be it a list, a tuple, or a generator expression
The same without generator expressions:
Answer = 0
for k in range(1, 10001):
if k % 2:
Answer += 1.0 / k
else:
Answer -= 1.0 / k
# or simply:
# Answer += 1.0 / k if k % 2 else -1.0 / k
You're almost there, all you need to do is to replace
while k <= 0.0001:
with:
while term <= 0.0001:
term is naturally 1/k
To make the teacher happy, you must follow the details of the problem, as well as the spirit of the problem. The problem clearly states to print the sum, not all the partial sums. You will anger the teacher by submitting a solution that spews 10000 lines of crap not requested.
Some have suggested pre-calculating a loop limit of 10000, but that was not the requested algorithm. Instead, one is to calculate successive terms (1, -1/2, 1/3, -1/4, ...) until reaching a term less than 0.0001.
The reason the problem was specified that way is that one ends up with a more generally useful program, applicable to a wide class of term formulas. Not a fragile one that gets the wrong answer if the term formula is changed from (-1)**(k-1)/k, to say 1/k or 1/k^2.
The teacher's wording "term less than 0.0001" is imprecise and assumed some math knowledge. They want the magnitude (absolute value) of the term to be less than 0.0001. Otherwise, iteration would stop at the second term -1/2, as someone pointed out.
So, this answer would not be complete without a pompous pedantic solution that skips ahead a chapter. ;) Note that previous some answers will not work in Python2.x without a conversion to float.
def term(k):
return (-1)**(k - 1) / float(k)
err = 0.0001
def terms():
k = 1
t = term(k)
while abs(t) >= err:
yield t
k += 1
t = term(k)
print(sum(terms()))
Here is the answer your teacher is looking for for full credit.
until < .0001 means while >= 0.0001 This modifies your code the least, so makes it a correction of what you wrote
sum = 0
k = 1
while 1.0/k >= 0.0001:
if k % 2 == 1:
sum = sum + 1.0/k
else:
sum = sum - 1.0/k
k = k + 1
print(sum)
Absolutly simplest way would be the following
sum((-1)**(k) / k for k in range(1, 10001))

Programming a math function in python

This is what I have to program:
sen(x) = (x/1!) - (x^3/3!) + (x^5/5!) - (x^7/7!) + ...
So far I have this:
def seno(x, n):
for i in range(1, n+1, 2):
result = (x**i/math.factorial(i))
result1 = (x**i/math.factorial(i))
result2 = (x**i/math.factorial(i))
result3 = (x**i/math.factorial(i))
return math.sin(result-result1 + result2 - result3)
The thing I can’t understand is how to actually change the i value for each result.
Another thing is I can’t use any non-built-in function. So no imports except for the math.
EDIT: Thank you for the quick reply.
It looks like you're doing a Taylor Series approximation of Sine.
You probably shouldn't declare separate result1, result2, etc. Instead, you compute each value in a loop, and accumulate it in a single result variable.
def seno(x,n):
result = 0
sign = 1 # Sign starts out positive
for i in range(1, n+1, 2):
result += x**i/math.factorial(i)
sign *= -1 # use negative sign on odd terms
return result
Note that you don't actually call math.sin on the result. The whole point of using a Taylor Series approximation is to estimate the value of math.sin(x) without actually calling that function.
You can optimize this loop a bit more. You can do a strength reduction on math.factorial by accumulating the answer rather than recomputing the whole factorial value on each iteration. You can also do a similar strength reduction on the x**i term, and roll the sign-switching logic into the update logic for exp as well.
def seno(x,n):
result = 0.0
fact = 1.0 # start with '1!'
exp = x # start with 'x¹'
xx = x*x # xx = x²
for i in range(1, n+1, 2):
result += exp / fact
exp *= -xx # update exponential term to 'xⁱ', and swap sign
fact *= (i+1) * (i+2) # update factorial term to '(i+2)!'
return result
You are using the for loop incorrectly. Each iteration will compute one term of the series; you need to accumulate those values rather than trying to set 4 results at a time.
def seno(x, n):
sign = 1
result = 0
for i in range(1, n+1, 2):
term = x**i/math.factorial(i)
result += sign * term
sign *= -1 # Alternate the sign of the term
return result

Taking square of summed numbers

This code adds all natural numbers up to 10, then takes the square of that sum in Python. Where did I go wrong?
def square_of_sum():
sum = 0
for x in xrange(11):
if x <= 10:
x + sum = sum
x += 1
else:
print sum*sum
break
Ah, I see you like Project Euler :)
Solution
I think this is what you meant by your code:
def square_of_sum():
sum_ = 0
for x in xrange(1, 11):
sum_ += x
return sum_ ** 2
To rewrite this more idiomatically, use generator comprehensions and built-ins:
def square_of_sum():
return sum(range(11)) ** 2
If your performance conscious, you can eliminate the loop by noticing that your finding the sum of an arithmetic series:
def square_of_sum(x):
print (x * (x + 1) / 2) ** 2
Fixes
As to why your code isn't working, it's b'coz of many reasons.
First of all, I think you're confused about how the for loop in Python works. Basically, it just loops over an array. You didn't have to check and break when x became greater than 10, nor increment it. Read up on the Python docs on how to use the for loop. To see an example of when to use it, see the wiki page.
Secondly, variable assignments are done with the variable on the left and the expression to be evaluated on the right. So x + sum = sum should really have been sum = sum + x or sum += x for brevity.
Thirdly, sum is a built-in function. You probably didn't want to nor shouldn't over-shadow it, so rename your sum variable to something else.
And last, sum*sum is equivalent to just raising it to the power of 2 and you can do that using the ** operator as so: sum ** 2.
Hope this helped you understand.
To fix the errors in your code:
def square_of_sum():
s = 0
for x in xrange(11):
s += x
print s**2
or, more idiomatically,
def square_of_sum(n):
print sum(range(n + 1)) ** 2
or, to eliminate the loop:
def square_of_sum(n):
print (n * (n + 1) / 2) ** 2
A couple of problems. First of all, sum is a builtin function, so you probably don't want to name anything that, so use a variable called something like total instead.
Second, variables assignment is done with the variable on the left and the expression on the right, so x + total = total should be total = x + total, or total += x for brevity.
Third, since the case when x == 11 is basically just a return case, it should be outside the loop.
And, finally, total * total is equivalent to total ** 2; this is easier to use for things like
def square_of_sum():
total = 0
for x in xrange(11):
if x <= 10:
total += x
x += 1
print total ** 2
But, if I were you, I'd just use
sum(range(11))**2

floating points precision in complicated calculations

For calculating Catalan Numbers, I wrote two codes. One (def "Catalan") works recursively and returns the right Catalan Numbers.
dicatalan = {}
def catalan(n):
if n == 0:
return 1
else:
res = 0
if n not in dicatalan:
for i in range(n):
res += catalan(i) * catalan(n - i - 1)
dicatalan[n] = res
return dicatalan[n]
the other (def "catalanFormula") applies the implicit formula, but doesn't calculate accurately starting from n=30. the problem derives from floating points - for k=9 the program returns "6835971.999999999" instead of "6835972" and from this moment on accumulates mistakes till the final wrong answer.
(print line is for checking)
def catalanFormula(n):
result = 1
for k in range(2, n + 1):
result *= ((n + k) / k)
print (result)
return int(result)
I tried rounding and failed, tried Decimal import and still got nothing right.
I need the "catalanFormula" work perfectly as "catalan";
Any Ideas?
Thanks!
Try calculating the numerator and denominator separately and dividing them at the end. If you do this, you should be able to make it a little bit farther with floating-point.
I'm sure Python has a package for rational numbers. Using rationals is an even better idea.
See the bigfloat package.
from bigfloat import *
setcontext(quadruple_precision)
def catalanFormula(n):
result = BigFloat(1)
for k in range(2, n + 1):
result *= ((BigFloat(n) + BigFloat(k)) / BigFloat(k))
return result
catalanFormula(30)
Output:
BigFloat.exact('3814986502092304.00000000000000000043', precision=113)

Categories

Resources