Estimating value of 1/pi using Ramajunam equation, returning wrong value when comparing with (1/math.pi) - python-3.x

Edited for Updated code
#racraman. Love you man. You not only helped me improve my code but also to understand the Equation. Thanks for your time.
import math
# performing ramanujan's infinite series to
#generate a numerical approximation of 1/pi:
""" 1/pi = (2*sqrt(2))/9801) * (4*k)!*(1103+26390k)/(((k!)**4)*396**(4k)))"""
def factorial_1(k):
if k==0:
return 1
else:
result = k* factorial_1(k-1)
return result
def estimate_pi():
k=0
total=0
n=(2*math.sqrt(2)/9801)
limit=int(input("Enter the ending limit = ")) #k=0 until limit=infinity!!!!
while True:
m=factorial_1(4*k)*(1103+26390*k)
o=((factorial_1(k))**4)*(396**(4*k))
result=n*(m/o)
total+=result #assigning result to a new variable to keep track of changes
if k>limit:
break
k+=1 #updating value of k, to improve result & total for each loop.
return 1/total # Return's pi=3.14 only if k=0
print(estimate_pi())

The statement :
k = result
is the problem - the variable k cannot be both a loop counter and the running total.
Instead of that statement, you will need to simply decrement k, and also add result to a new running total variable that you initialise to 0 outside the loop.
You would also only want to print the result and return only after the loop has finished.
EDIT TO ADD :
Please don't use Answers in that way; that's not what they're for, and would be confusing for other readers to try to follow. The question is for containing all (ongoing) steps of defining the problem (just mark the appended updates with "EDIT TO ADD" as I have done with this comment); the answers are for solutions to that problem, to be accepted if they proved useful.
Ramanujan's formula most certainly works for increasing values of k - but you have to iterate starting at 0.
For example, let's say the user enters 5 for k.
What your code is currently doing is incrementing k - so calculating k = 5, 6, 7, ..... terminating when the step's result is 0. You're missing out k=0, 1, 2, 3, 4 - the major terms !
What you want to do instead is sum the results for k = 0, 1, 2, 3, 4, 5 so how about :
Have the user enter a variable limit instead of k
Start k at 0, and increment at each iteration
Terminate the loop when the step's result < epsilon, or k > limit
Incidentally, the n=(2*math.sqrt(2)/9801) is a constant, so can go outside the loop therefore get calculated only once.

#racraman. I'm Posting the updated code as an answer do that I could keep track of the Error's I've made for future references. Thanks for the Help.
# performing ramanujan's infinite series to
#generate a numerical approximation of 1/pi:
""" 1/pi = (2*sqrt(2))/9801) * (4*k)!*(1103+26390k)/(((k!)**4)*396**(4k)))"""
def factorial_1(k):
if k==0:
return 1
else:
result = k* factorial_1(k-1)
return result
def estimate_pi():
k=int(input("enter the value of k = "))
total=0
while True:
n=(2*math.sqrt(2)/9801)
m=factorial_1(4*k)*(1103+26390*k)
o=((factorial_1(k))**4)*(396**(4*k))
result=n*(m/o)
total+=result #assigning result to a new variable to keep track of changes
epsilon=1e-15
if abs(result)<epsilon:
break
k+=1 #updating value of k, to improve result & total for each loop.
return 1/total # Return's pi=3.14 only if k=0
print(estimate_pi())

Related

I need the code to stop after break and it should not print max(b)

Rahul was learning about numbers in list. He came across one word ground of a number.
A ground of a number is defined as the number which is just smaller or equal to the number given to you.Hence he started solving some assignments related to it. He got struck in some questions. Your task is to help him.
O(n) time complexity
O(n) Auxilary space
Input Description:
First line contains two numbers ‘n’ denoting number of integers and ‘k’ whose ground is to be check. Next line contains n space separated numbers.
Output Description:
Print the index of val.Print -1 if equal or near exqual number
Sample Input :
7 3
1 2 3 4 5 6 7
Sample Output :
2
`
n,k = 7,3
a= [1,2,3,4,5,6,7]
b=[]
for i in range(n):
if k==a[i]:
print(i)
break
elif a[i]<k:
b.append(i)
print(max(b))
`
I've found a solution, you can pour in if you've any different views
n,k = 7,12
a= [1,2,3,4,5,6,7]
b=[]
for i in range(n):
if k==a[i]:
print(i)
break
elif a[i]<k:
b.append(i)
else:
print(max(b))
From what I understand, these are the conditions to your question,
If can find number, print the number and break
If cannot find number, get the last index IF it's less than value k
Firstly, it's unsafe to manually input the length of iteration for your list, do it like this:
k = 3
a= [1,7,2,2,5,1,7]
finalindex = 0
for i, val in enumerate(a):
if val==k:
finalindex = i #+1 to index because loop will end prematurely
break
elif val>=k:
continue
finalindex = i #skip this if value not more or equal to k
print(finalindex) #this will either be the index of value found OR if not found,
#it will be the latest index which value lesser than k
Basically, you don't need to print twice. because it's mutually exclusive. Either you find the value, print the index or if you don't find it you print the latest index that is lesser than K. You don't need max() because the index only increases, and the latest index would already be your max value.
Another thing that I notice, if you use your else statement like in your answer, if you have two elements in your list that are larger than value K, you will be printing max() twice. It's redundant
else:
print(max(b))

My second for loop is not working in the below code

nums=[0,5,4,12]
n=len(nums)
temp=1
ans=[]
for i in range(n):
ans.append(temp)
temp*=nums[i]
temp=1
for i in range(n-1,-1):
ans[i]*=temp
temp*=nums[i]
print("yes")
print(ans)
Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements of nums except nums[i].
The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.
You must write an algorithm that runs in O(n) time and without using the division operation.
This is a solution for this leetcode question but my second for loop is not executing, and i don't know why.
Using range like this will result in zero iterations because the "step" parameter is 1. So because it is 1 it will think it should go upwards, but n is already above -1, so it should be like
range(n, -1, -1)
also you most probably want
import math
n = [0, 5, 4, 12]
ans = []
for num in n:
temp = n[:] # create a copy
temp.remove(num) # remove the number you are on
ans.append(math.prod(temp)) # use math.prod to multiply the rest together
return ans

How to extract numbers with repeating digits within a range

I need to identify the count of numbers with non-repeating digits in the range of two numbers.
Suppose n1=11 and n2=15.
There is the number 11, which has repeated digits, but 12, 13, 14 and 15 have no repeated digits. So, the output is 4.
Wrote this code:
n1=int(input())
n2=int(input())
count=0
for i in range(n1,n2+1):
lst=[]
x=i
while (n1>0):
a=x%10
lst.append(a)
x=x//10
for j in range(0,len(lst)-1):
for k in range(j+1,len(lst)):
if (lst[j]==lst[k]):
break
else:
count=count+1
print (count)
While running the code and after inputting the two numbers, it does not run the code but still accepts input. What did I miss?
The reason your code doesn't run is because it gets stuck in your while loop, it can never exit that condition, since n1 > 0 will never have a chance to be evaluated as False, unless the input itself is <= 0.
Anyway, your approach is over complicated, not quite readable and not exactly pythonic. Here's a simpler, and more readable approach:
from collections import Counter
n1 = int(input())
n2 = int(input())
count = 0
for num in range(n1, n2+1):
num = str(num)
digit_count = Counter(num)
has_repeating_digits = any((True for count in digit_count.values() if count > 1))
if not has_repeating_digits:
count += 1
print(count)
When writing code, in general you should try to avoid nesting too much stuff (in your original example you have 4 nested loops, that's readability and debugging nightmare), and try using self-describing variable names (so a, x, j, k, b... are kind of a no-go).
If in a IPython session you run import this you can also read the "Zen of Python", which kind of sums up the concept of writing proper pythonic code.

Number of sub sequences of length K having total sum S, given 2d array

I wish to find Number of sub sequences of length K having total sum S, given an array.
Sample Input:
a=[1,1,1,2,2] & K=2 & S=2
Sample Output:
3 {because a[0],a[1]; a[1]a[2]; a[0]a[2] are only three possible for the case}
I have tried to write a recursive loop in Python for starter but it isn't giving output as expected.Please can you help me find a loophole I might be missing on.
def rec(k, sum1, arr, i=0):
#print('k: '+str(k)+' '+'sum1: '+str(sum1)) #(1) BaseCase:
if(sum1==0 and k!=0): # Both sum(sum1) required and
return 0 # numbers from which sum is required(k)
if(k==0 and sum1 !=0): # should be simultaneously zero
return 0 # Then required subsequences are 1
if(k==0 and sum1==0 ): #
return 1 #
base_check = sum1!=0 or k!=0 #(2) if iterator i reaches final element
if(i==len(arr) and base_check): # in array we should return 0 if both k
return 0 # and sum1 aren't zero
# func rec for getting sum1 from k elements
if(sum1<arr[0]): # takes either first element or rejects it
ans=rec(k-1,sum1,arr[i+1:len(arr)],i+1) # so 2 cases in else loop
print(ans) # i is taken in as iterator to provide array
else: # input to rec func from 2nd element of array
ans=rec(k-1, sum1-arr[0], arr[i+1:len(arr)],i+1)+rec(k, sum1, arr[i+1:len(arr)],i+1)
#print('i: '+str(i)+' ans: '+str(ans))
return(ans)
a=[1,1,1,2,2]
print(rec(2,2,a))
I am still unable to process how to make changes. Once this normal recursive code is written I might go to DP approach accordinlgy.
Using itertools.combinations
Function itertools.combinations returns all the subsequences of a given lengths. Then we filter to keep only subsequences who sum up to the desired value.
import itertools
def countsubsum(a, k, s):
return sum(1 for c in itertools.combinations(a,k) if sum(c)==s)
Fixing your code
Your code looks pretty good, but there are two things that appear wrong about it.
What is this if for?
At first I was a bit confused about if(sum1<arr[0]):. I think you can (and should) always go to the else branch. After thinking about it some more, I understand you are trying to get rid of one of the two recursive calls if arr[0] is too large to be taken, which is smart, but this makes the assumption that all elements in the array are nonnegative. If the array is allowed to contain negative numbers, then you can include a large a[0] in the subsequence, and hope for a negative element to compensate. So if the array can contain negative numbers, you should get rid of this if/else and always execute the two recursive calls from the else branch.
You are slicing wrong
You maintain a variable i to remember where to start in the array; but you also slice the array. Pretty soon your indices become wrong. You should use slices, or use an index i, but not both.
# WRONG
ans=rec(k-1, sum1-arr[0], arr[i+1:len(arr)],i+1)+rec(k, sum1, arr[i+1:len(arr)],i+1)
# CORRECT
ans = rec(k-1, sum1-arr[i], arr, i+1) + rec(k, sum1, arr, i+1)
# CORRECT
ans = rec(k-1, sum1-arr[0], arr[1:]) + rec(k, sum1, arr[1:])
To understand why using both slicing and an index gives wrong results, run the following code:
def iter_array_wrong(a, i=0):
if (a):
print(i, a)
iter_array_wrong(a[i:], i+1)
def iter_array_index(a, i=0):
if i < len(a):
print(i, a)
iter_array_index(a, i+1)
def iter_array_slice(a):
if a:
print(a)
iter_array_slice(a[1:])
print('WRONG')
iter_array_wrong(list(range(10)))
print()
print('INDEX')
iter_array_index(list(range(10)))
print()
print('SLICE')
iter_array_slice(list(range(10)))
Also note that a[i:len(a)] is exactly equivalent to a[i:] and a[0:j] is equivalent to a[:j].
Clean version of the recursion
Recursively count the subsequences who use the first element of the array, and the subsequences who don't use the first element of the array, and add the two counts. To avoid explicitly slicing the array repeatedly, which is an expensive operation, we keep a variable start to remember we are only working on subarray a[start:].
def countsubsum(a, k, s, start=0):
if k == 0:
return (1 if s == 0 else 0)
elif start == len(a):
return 0
else:
using_first_element = countsubsum(a, k-1, s-a[start], start+1)
notusing_first_elem = countsubsum(a, k, s, start+1)
return using_first_element + notusing_first_elem

find the first occurrence of a number greater than k in a sorted array

For the given sorted list,the program should return the index of the number in the list which is greater than the number which is given as input.
Now when i run code and check if it is working i am getting 2 outputs. One is the value and other output is None.
If say i gave a input of 3 for the below code.The expected output is index of 20 i.e., 1 instead i am getting 1 followed by None.
If i give any value that is greater than the one present in the list i am getting correct output i.e., "The entered number is greater than the numbers in the list"
num_to_find = int(input("Enter the number to be found"))
a=[2,20,30]
def occur1(a,num_to_find):
j = i = 0
while j==0:
if a[len(a)-1] > num_to_find:
if num_to_find < a[i]:
j=1
print(i)
break
else:
i = i + 1
else:
ret_state = "The entered number is greater than the numbers in the list"
return ret_state
print(occur1(a,num_to_find))
This code is difficult to reason about due to extra variables, poor variable names (j is typically used as an index, not a bool flag), usage of break, nested conditionals and side effect. It's also inefficient because it needs to visit each element in the list in the worst case scenario and fails to take advantage of the sorted nature of the list to the fullest. However, it appears working.
Your first misunderstanding is likely that print(i) is printing the index of the next largest element rather than the element itself. In your example call of occur1([2, 20, 30], 3)), 1 is where 20 lives in the array.
Secondly, once the found element is printed, the function returns None after it breaks from the loop, and print dutifully prints None. Hopefully this explains your output--you can use return a[i] in place of break to fix your immediate problem and meet your expectations.
Having said that, Python has a builtin module for this: bisect. Here's an example:
from bisect import bisect_right
a = [1, 2, 5, 6, 8, 9, 15]
index_of_next_largest = bisect_right(a, 6)
print(a[index_of_next_largest]) # => 8
If the next number greater than k is out of bounds, you can try/except that or use a conditional to report the failure as you see fit. This function takes advantage of the fact that the list is sorted using a binary search algorithm, which cuts the search space in half on every step. The time complexity is O(log(n)), which is very fast.
If you do wish to stick with a linear algorithm similar to your solution, you can simplify your logic to:
def occur1(a, num_to_find):
for n in a:
if n > num_to_find:
return n
# test it...
a = [2, 5, 10]
for i in range(11):
print(i, " -> ", occur1(a, i))
Output:
0 -> 2
1 -> 2
2 -> 5
3 -> 5
4 -> 5
5 -> 10
6 -> 10
7 -> 10
8 -> 10
9 -> 10
10 -> None
Or, if you want the index of the next largest number:
def occur1(a, num_to_find):
for i, n in enumerate(a):
if n > num_to_find:
return i
But I want to stress that the binary search is, by every measure, far superior to the linear search. For a list of a billion elements, the binary search will make about 20 comparisons in the worst case where the linear version will make a billion comparisons. The only reason not to use it is if the list can't be guaranteed to be pre-sorted, which isn't the case here.
To make this more concrete, you can play with this program (but use the builtin module in practice):
import random
def bisect_right(a, target, lo=0, hi=None, cmps=0):
if hi is None:
hi = len(a)
mid = (hi - lo) // 2 + lo
cmps += 1
if lo <= hi and mid < len(a):
if a[mid] < target:
return bisect_right(a, target, mid + 1, hi, cmps)
elif a[mid] > target:
return bisect_right(a, target, lo, mid - 1, cmps)
else:
return cmps, mid + 1
return cmps, mid + 1
def linear_search(a, target, cmps=0):
for i, n in enumerate(a):
cmps += 1
if n > target:
return cmps, i
return cmps, i
if __name__ == "__main__":
random.seed(42)
trials = 10**3
list_size = 10**4
binary_search_cmps = 0
linear_search_cmps = 0
for n in range(trials):
test_list = sorted([random.randint(0, list_size) for _ in range(list_size)])
test_target = random.randint(0, list_size)
res = bisect_right(test_list, test_target)[0]
binary_search_cmps += res
linear_search_cmps += linear_search(test_list, test_target)[0]
binary_search_avg = binary_search_cmps / trials
linear_search_avg = linear_search_cmps / trials
s = "%s search made %d comparisons across \n%d searches on random lists of %d elements\n(found the element in an average of %d comparisons\nper search)\n"
print(s % ("binary", binary_search_cmps, trials, list_size, binary_search_avg))
print(s % ("linear", linear_search_cmps, trials, list_size, linear_search_avg))
Output:
binary search made 12820 comparisons across
1000 searches on random lists of 10000 elements
(found the element in an average of 12 comparisons
per search)
linear search made 5013525 comparisons across
1000 searches on random lists of 10000 elements
(found the element in an average of 5013 comparisons
per search)
The more elements you add, the worse the situation looks for the linear search.
I would do something along the lines of:
num_to_find = int(input("Enter the number to be found"))
a=[2,20,30]
def occur1(a, num_to_find):
for i in a:
if not i <= num_to_find:
return a.index(i)
return "The entered number is greater than the numbers in the list"
print(occur1(a, num_to_find))
Which gives the output of 1 (when inputting 3).
The reason yours gives you 2 outputs, is because you have 2 print statements inside your code.

Resources