Finding the product of first Million natural numbers in Python - python

I have just started with Python programming language. I tried to write a function which takes input either a list or multiple integers to find their product. I am trying to find the product of first million natural numbers but its displaying an MemoryError.
def product(*arg):
answer=1
if type(arg) == tuple:
arg=str(arg)
arg=arg.lstrip('[(')
arg=arg.rstrip('],)')
arg=arg.split(',')
for i in arg:
answer*=int(i)
return answer
else:
for i in arg:
answer*=int(i)
return answer
j=range(1,1000000,1)
j=list(j)
print(product(j))
Steps:
I convert the range object into list object if i am to pass a list as
argument
Now, within the function, i try to split the tuple by converting it
string.
I convert the resultant string into a list and then loop over the
elements to find the product
Q1: How to avoid the memory error as i try to find the product of first Million natural numbers?
Q2 How to improve this code?

You can use a Generator in Python.
def generate_product():
r = 1
for i in range(1,1000000):
r *= i + 1
yield r
list(generate_product())[0]
It is more memory efficient and better in terms of performance.

To calculate the product of all numbers from 1 to 1 million use a simple loop:
r = 1
for l in range(1,1000000):
r*=(i+1)
print(res)
But keep in mind that the result will be a pretty big number.
That means that your calculation might take long and the resulting number will need a lot memory.
EDIT Then i missread your question a little. This is a function that multiplies the elements in a list:
def multiply_list_elements(_list):
result = 1
for element in _list:
result*=element
return result
multiply_list_elements([1,2,3,4])
>>> 24
The memory error probably came from the huge number as #ZabirAlNazi calculated so nicely.

All of the solution is fine, but one point to make - your question is equivalent to find the factorial of 1 million.
The number of digits of n! = log10(1) + log10(2) + ... log10(n)
import math
num_dig = 1
for i in range(1,1000000):
num_dig += math.log10(i)
print(num_dig)
So, the number of digits in your answer is 5565703 (approx.).
That's only the final n, if you also want the intermediate results it will require squared memory O(m^2).
import math
ans = 1
for i in range(2,1000001):
ans *= i
print(ans)
N.B: You can approximate with logarithms and Stirling numbers with faster run-time.

A very simple solution would be:
def prod_of():
p=1
for i in range(1,1000000):
p* = i
print(p)

Related

how to iterate a list consisting of long integer value using python?

I'm trying to iterate a list consisting of long integer number (more than 10 digits) but while doing it with for-loop it is taking a longer time and the shell-window get stuck during execution.
for example:
i have a list as mention below:
my_list: [7,31,127,2047,8191,131071,524287,838607,536870911,2147483647]
and i want to iterate the above list within a function with lucas lehmer primality test formula and want to derive the boolean values using the lucas lehmer series where if the number has 0 in its p-2 index than the number from my_list should be consider as prime and return 1 (True) else 0 (False)
def ll_list(p):
ll_myList = [4]
for i in range(1,p-1):
number = ((ll_myList[i-1])**2 -2) % (2**p - 1)
ll_myList.append(number)
#validating if the lucas-lehmer series (ll_myList) is a prime or not
if ll_myList[p-2] == 0:
return int(True)
else:
return int(False)
now when i call the ll_list function the shell window crashes while printing the list for each number mention in the my_list
prime_ll = [ll_list(i) for i in my_list]
print(prime_ll, '\n')
is there any way to speedup the iteration and at the same time i want to print the list prime_ll?
This code won't decrease the runtime of your program by much, but I think it should theoretically run faster. I basically translated the pseudo-code on the Wikipedia article you referenced.
def lucas_lehmer(p):
s = 4
m = (1 << p) - 1
for _ in range(p - 2):
s = (s**2 - 2) % m
return not s
The not s part basically checks if s is equal to 0. If it is, not s will return True; False otherwise.
The reason why it takes so long to execute this code is that there are two for loops at play: the one within the lucas_lehmer function, and the one that goes through each element in my_list. Of course, this is coupled with the fact that the numbers in your example are large by orders of magnitude.
I also found a link containing other efficient implementations of this algorithm that might be of interest to you.

Elements of Programming Interview 5.15 (Random Subset Computation)

Algorithm problem:
Write a program which takes as input a positive integer n and size
k <= n; return a size-k subset of {0, 1, 2, .. , n -1}. The subset
should be represented as an array. All subsets should be equally
likely, and in addition, all permutations of elements of the array
should be equally likely. You may assume you have a function which
takes as input a nonnegative integer t and returns an integer in the
set {0, 1,...,t-1}.
My original solution to this in pseudocode is as follows:
Set t = n, and output the result of the random number generator into a set() until set() has size(set) == t. Return list(set)
The author solution is as follows:
def online_sampling(n, k):
changed_elements = {}
for i in range(k):
rand_idx = random.randrange(i, n)
rand_idx_mapped = changed_elements.get(rand_idx, rand_idx)
i_mapped = changed_elements.get(i, i)
changed_elements[rand_idx] = i_mapped
changed_elements[i] = rand_idx_mapped
return [changed_elements[i] for i in range(k)]
I totally understand the author's solution - my question is more about why my solution is incorrect. My guess is that it becomes greatly inefficient as t approaches n, because in that case, the probability that I need to keep running the random num function until I get a number that isn't in t gets higher and higher. If t == n, for the very last element to add to set there is just a 1/n chance that I get the correct element, and would probabilistically need to run the given rand() function n times just to get the last item.
Is this the correct reason for why my solution isn't efficient? Is there anything else I'm missing? And how would one describe the time complexity of my solution then? By the above rationale, I believe would be O(n^2) since probabilistically need to run n + n - 1 + n - 2... times.
Your solution is (almost) correct.
Firstly, it will run in O(n log n) instead of O(n^2), assuming that all operations with set are O(1). Here's why.
The expected time to add the first element to the set is 1 = n/n.
The expected time to add the second element to the set is n/(n-1), because the probability to randomly choose yet unchosen element is (n-1)/n. See geometric distribution for an explanation.
...
For k-th element, the expected time is n/(n-k). So for n elements the total time is n/n + n/(n-1) + ... + n/1 = n * (1 + 1/2 + ... + 1/n) = n log n.
Moreover, we can prove by induction that all chosen subsets will be equiprobable.
However, when you do list(set(...)), it is not guaranteed the resulting list will contain elements in the same order as you put them into a set. For example, if set is implemented as a binary search tree then the list will always be sorted. So you have to store the list of unique found elements separately.
UPD (#JimMischel): we proved the average case running time. There still is a possibility that the algorithm will run indefinitely (for example, if rand() always returns 1).
Your method has a big problem. You may return duplicate numbers if you random number generator create same number two times isn't it?
If you say set() will not keep duplicate numbers, your method has created members of set with different chance. So numbers in your set will not be equally likely.
Problem with your method is not efficiency, it does not create an equally likely result set. The author uses a variation of Fisher-Yates method for creating that subset which will be equally likely.

Calculating GCD - how to check every element in list

Multiple number inputs
below is just how I chose to start writing my code
def main():
numbers = input()
if numbers == "0":
exit()
else:
number_list = [int(i) for i in numbers.split()]
def calculate_gcd(number_list):
for i in range(1,smallest_number(number_list)+1):
for n in range(0,len(number_list)):
if number_list[n] % i == 0:
check_list += number_list[n]
more code - but not important for the question im asking
my code was hardly complete and only worked for max 3 size lists, sadly.
How I thought of logic
Read input -> split by space, put it in list
sort the list
make a variable (divisor), and set it to 1
while divisor <= sortedlist[0] (which is smallest in list)
5. if every element % divisor == 0, then gcd = divisor, then divisor+=1
loop until it is no longer true
Problem I found
It requires stupid effort, it will actually not run and give runtime error.
I'm having trouble finding a way to check No.5 (in bold)
I know that there is gcd function, but it only deals with two inputs.
and it comes down to same question, how do I make sure 'all' elements divides down to zero?
Any suggestion of making gcd logic and comment on No.5 (bold) ?
Thank you
Instead of tackling the bigger problem, why not tackle a smaller problem. How do you find the gcd of two numbers? Well, there are multiple algorithms for this. Let us use the iterative one:
def gcd_iterative(a, b):
while b:
a, b = b, a % b
return a
Now, one thing to realize is that, if you have multiple numbers, and you want to find the gcd of all numbers, then it is simply:
gcd(gcd(...(a, b), c), ...)
In simpler terms, if you want to find the gcd of three numbers (a, b, c), then you would do the following:
gcd = gcd_iterative(a, b)
gcd = gcd_iterative(gcd, c)
So, now if you have a list of numbers, lst, you can do the following:
>>> gcd = lst[0]
>>> for num in lst[1:]:
gcd = gcd_iterative(gcd, num)
>>> print(gcd)

How to to determine the number of ways a number can be broken down into sums of smaller numbers

My friend gave me this problem he was asked in an interview that he was not able to answer. After hours of thinking we were not able to come up with the solution.
Consider A number three. I need to write a program to count the different ways in which you can write the number as a sum of numbers less than the number.
For example:
If the number is 2, it can be written as sum(1,1)
if the number is 3, it can be written as sum(1,1,1), sum(1,2), sum(2,1)
if the number is 4, it can be written as sum(1,1,1,1), sum(1,3), sum(3,1), sum(1,2,1), sum(2,1,1), sum(1,1,2), sum(2,2). 7 different ways
If the number is 5, it can be written as sum(1,1,1,1,1), sum(1,1,1,2), sum(1,1,2,1), sum(1,2,1,1), sum(2,1,1,1) etc.
How can I write program to determine the number of ways a number can be broken down into sums of smaller numbers
I was able to come up with a solution for the problem if sum(1,2) and sum(2,1) are considered equivalent using the algorithm in http://www.programminglogic.com/integer-partition-algorithm/
But the problem is sum(1,2) and sum(2,1) are different. I can not see a pattern to do this at all.
Any help would be appreciated. I just want to know the solution.
Think of the number as a line of dots:
4 -> . . . .
Between two adjacent dots we can put a wall to break the number into smaller ones:
1+3 -> .|. . .
2+2 -> . .|. .
1+2+1 -> .|. .|.
For every of the n-1 gaps, we have the option of putting a wall or not, for a total of 2^(n-1) possibilities. However, putting no walls leaves us with just the number, which isn't allowed, so we remove that as a possibility for a final total of 2^(n-1)-1 solutions.
You need a partitioning solution to find all the possible answers. Take a look at the following approaches to do so:
Find all ways to sum given number (with repetitions allowed) from given set
fast method to list all possible combination of numbers which sum to a const number
and a good mathematical explanation:
http://mathworld.wolfram.com/PartitionFunctionP.html
A program for computing the number of different sums (as specified here) could look like this:
def SumsCount(n):
if n <= 1:
return 0
sumsCount = 0
# the first number i can be 1, 2, ..., n-1
for i in range(1,n):
# the rest must sum up to n-i
# or the rest is just the number (n-i)
sumsCount += SumsCount(n-i) + 1
return sumsCount
Here is a simple recursive equation to solve this problem:-
F(N) = (F(N-1)+1) + (F(N-2)+1) + F(N-3)...........F(1)+1
F(2) = 1
F(1) = 0
F(N-1) = (F(N-2)+1) + F(N-3)...........F(1)+1
F(N) = F(N-1) + F(N-1) + 1
F(N) = 2*F(N-1) + 1
Solving equations:
F(N) = 2^(N-2) + 2^(N-2) - 1
= 2^(N-1) - 1

Python: simplifying nested FOR loop?

I am wondering if there is a way to simplify the nested loop below. The difficulty is that the iterator for each loop depends on things from the previous loops. Here is the code:
# Find the number of combinations summing to 200 using the given list of coin
coin=[200,100,50,20,10,5,2,1]
total=[200,0,0,0,0,0,0,0]
# total[j] is the remaining sum after using the first (j-1) types of coin
# as specified by i below
count=0
# count the number of combinations
for i in range(int(total[0]/coin[0])+1):
total[1]=total[0]-i*coin[0]
for i in range(int(total[1]/coin[1])+1):
total[2]=total[1]-i*coin[1]
for i in range(int(total[2]/coin[2])+1):
total[3]=total[2]-i*coin[2]
for i in range(int(total[3]/coin[3])+1):
total[4]=total[3]-i*coin[3]
for i in range(int(total[4]/coin[4])+1):
total[5]=total[4]-i*coin[4]
for i in range(int(total[5]/coin[5])+1):
total[6]=total[5]-i*coin[5]
for i in range(int(total[6]/coin[6])+1):
total[7]=total[6]-i*coin[6]
count+=1
print count
I recommend looking at http://labix.org/python-constraint which is a Python constraint library. One of its example files is actually permutations of coinage to reach a specific amount, and it all handles it for you once you specify the rules.
You can get rid of all the int casting. An int/int is still an int in python ie integer division.
it looks like Recursion would clean this up nicly
count = 0
coin=[200,100,50,20,10,5,2,1]
total=[200,0,0,0,0,0,0,0]
def func(i):
global count,total,coin
for x in range(total[i-1]/coin[i-1]+1):
total[i]=total[i-1]-x*coin[i-1]
if (i == 7):
count += 1
else:
func(i+1)
func(1)
print count
combinations = set()
for i in range(len(coins)):
combinations = combinations | set(for x in itertools.combinations(coins, i) if sum(x) == 200)
print len(combinations)
It's a little slow, but it should work.

Categories

Resources