Determining Practical Numbers - python

So I am given a fraction and must determine if the denominator is a practical number. Description of a practical number: What is a practical number?
I already have a function that returns all the factors of the number:
def factorFinder(x):
#Pre: Valid Integer for students
#Post: Returns list of factors for that number
#Takes the number of students as argument and returns all factors for that number
lst = []
for i in range(1, x+1):
if x % i == 0:
lst.append(i)
return(lst)
and a function that given a list of factors as arguments returns a list of sums of all its sublists:
import itertools
def sumsOfSublists(lst):
# Pre: a non-empty list
# Post: returns a list of sums of all possible list sublists of length >= 2
# given a list, returns a sorted list containing sums of
# all combinations of sublists of length 2..list_length
sumList = []
for i in range(2, len(lst)+1):
zlist = list(itertools.combinations(lst,i))
for el in zlist:
sumList.append(sum(el))
return sorted(sumList)
I don't know how to go about testing if the number is practical or not. I don't really understand the concept of a "practical number" even after reading about it on multiple websites. So I guess really, this is more of a math question if anything.
Also given two number I need to print out the egyptian fractions for them. (i.e) given 7/8 should print the following: (1/2)+(1/4)+(1/8). All numerators must be 1.

Looks like you've done all the heavy lifting you just need a check:
def is_practical(n):
s = set(sumsOfSublists(factorFinder(n)))
return all(i in s for i in range(1, n))
Is there any reason you are sorting the sums into a list? sets are much quicker to look up. See https://wiki.python.org/moin/TimeComplexity

I reformulate your wiki-article and hope that it is helpful to you - because as AChampion has said - you have already done all the heavy lifting ;)
n is is a practical number if:
let l = [1, 2, ... n-1] // all number smaller than n
let divs = [i for i in range(1,n) if n%i==0] // all divisors of n (I associate a factor is prime cause of the term factorization of a number, so probably you should change factor to divisor in your question)
Now you have to write all numbers of l as sum of numbers from divs, where you can use all numbers of divs one or zero times
for example n = 12 is a practical number because:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
divs = [1, 2, 3, 4, 6]
l[0] = divs[0], l[1] = divs[1], ... , l[10] = divs[0]+divs[3]+divs[4]=1+4+6=11 // l[1] = divs[0]+divs[0] is not allowed because you may use each number of divs only once
In opposite n = 3 is not a practical number because:
l = [1, 2]
divs = [1]
l[0] = divs[0], l[1] = mööööp
Here is also a straightforward implementation of my explanation (but you can also keep the nice work you have already done!):
from itertools import combinations
def isPracticalNumber(n):
l = [i for i in range(1, n)]
divs = [i for i in range(1, n) if n % i == 0]
possibleSums = set()
for i in range(1, len(divs)+1):
combinationsOfLengthI = combinations(divs, i)
for combi in combinationsOfLengthI:
possibleSums.add(sum(combi))
return all([i in possibleSums for i in l])
if __name__ == '__main__':
print(isPracticalNumber(12)) # True
print(isPracticalNumber(3)) # False
practicalNumbers = [1, 2, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 48, 54, 56, 60, 64, 66, 72, 78, 80, 84, 88, 90, 96, 100, 104, 108, 112, 120, 126, 128, 132, 140, 144, 150]
calculatedPracticalNumbers = [i for i in range(1, 151) if isPracticalNumber(i)]
print(len(calculatedPracticalNumbers) == len(practicalNumbers) and all([i in practicalNumbers for i in calculatedPracticalNumbers]))

Related

Finding all pairs that have the product equal to the target number

I know there is an exhaustive solution that iterates through every single number, but how to implement the divide and conquer approach?
With an array of integers without repeated numbers, and a target product integer number, return a set of numbers that include all pairs that have the product equal to the target number.
def product_pair(num_arr, product):
"""
:type num_arr: List[int]
:type product: int
:rtype: List[int]
"""
Example 1. Product_pair([3, 5, 9, 10, 23, 53], 20) => []
Example 2. Product_pair([10, 2, 9, 30, 5, 1], 10) => [10, 2, 5, 1]
Well I'm not so sure about divide and conquer, but this will be rather efficient and quite simple:
def product_pair(num_arr, product):
value_set = set(num_arr)
sol = [n for n in num_arr if product/n in value_set]
return sol
You can do this as follows:
def f(lst, n):
lst = list(filter(lambda x: x<=n, lst)) # Note 3
res = []
seen = set()
for i, x in enumerate(lst[:-1]): # Note 4
if x in seen:
continue
rem = n / x
if rem in lst[i+1:]: # Note 1, 2
seen.add(rem)
res.extend([x, int(rem)])
return res
which for your examples, produces:
print(f([3, 5, 9, 10, 23, 53], 20)) # -> []
print(f([10, 2, 9, 30, 5, 1], 10)) # -> [10, 1, 2, 5]
Notes
Optimized membership test; you only look for membership in the slice of the list after the current element. If there was something before, you would have already found it.
I am assuming here that your list of candidates only contains integers.
You can filter out numbers that are bigger than the target number. Those are impossible to find an integer complementary to.
It follows from Note 1 that there cannot be anything you haven't found already when reaching the last number on the list.
General
Duplicates in original list: e.g., having two 4s would not return a (4, 4) result for a target number of 16.
This is definitely not the fastest one can do, but it is not too slow either.

Getting prime numbers from a list

Given a list lst = [121, 4, 37, 441, 7, 16] , I would like to remove from it all numbers that repeat themselves, resulting in a new string that would be lst = [37,7] (the prime numbers of the original string).
So far I'd only managed to put out this code:
def func(lst,x):
y = []
for i in lst:
for x in range (1, i):
if (i % x) == 0 :
y.append(i)
return y
print(func(lst,3))
Instead of getting lst = [37,7], I'm getting this weird looking list:
[121, 121, 4, 4, 37, 441, 441, 441, 441, 441, 441, 441, 441, 7, 16, 16, 16, 16]
Is there any way I can make this work ?
As this feels like a homework question, I won't give working code, but a strategy. You want to ensure that only the numbers of the original list remain, or filter out the numbers that are not prime.
Slightly more formally, "for each number in the list, determine if it's prime, and if so, include it in a new list".
Your code is 90% there, but your kernel (the primality test) is not correct. The key to testing primality is to ensure that each possible integer divisor does not evenly divide the number in question.
For example, if testing 6, the list of possible "0 remainder" integer divisors is
[1, 2, 3, 4, 5, 6]
The first and last numbers (1 and 6) don't mean anything as far as primality (6/1 is 6, and 6/6 is 1). So, your list of possible divisors to test is now
[2, 3, 4, 5]
From here, I think an insight you're missing in your code is that for a number to be prime, all of its possible divisors must not produce an even number (i.e., remainder of 0).
from math import sqrt
def func(n)
list1=[]
list2=[]
for z in (all(i % x for x in range(2,int(sqrt(i)+1))) for i in n):
list1.append(z)
for i,j in enumerate(list1):
if j == True:
list2.append(n[i])
return list2

take a number and sum its digits

I'm working through this Kata and although I've looked through the solutions none are quite similar enough to mine to answer my question.
Problem Text: The number 89 is the first integer with more than one digit that fulfills the property partially introduced in the title of this kata. What's the use of saying "Eureka"? Because this sum gives the same number.
In effect: 89 = 8^1 + 9^2
The next number in having this property is 135.
See this property again: 135 = 1^1 + 3^2 + 5^3
We need a function to collect these numbers, that may receive two integers a, b that defines the range [a, b] (inclusive) and outputs a list of the sorted numbers in the range that fulfills the property described above.
def sum_dig_pow(a, b): # range(a, b + 1) will be studied by the function
# your code here
lst = []
n = 1
tot = 0
for i in range(a,b):
if i > 9:
spl = str(i).split()
for item in spl:
tot += int(item) ** n
n += 1
if tot == i:
lst.append(i)
else:
lst.append(i)
return lst
Tests are returning "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] should equal [1, 2, 3, 4, 5, 6, 7, 8, 9, 89]".
I cannot figure out why it's passing 10 and not appending 89. I'm sure there's a more efficient way to do this as well but I'm still learning so want to be working in basics of loops, conditionals,etc.
This line is incorrect:
spl = str(i).split()
The split method will split a string on spaces by default and return a list. So passing i=10 gives back spl = ['10'], a list with one element. Instead, just iterate over each of the digits in the string.
for item in str(i):
...
Follow up: you can shorten your code by using enumerate to count the index of each digit.
def sum_dig_pow(a,b):
return [sum(int(y)**(i+1) for i,y in enumerate(str(x))) for x in range(a,b)]
Rather than spending a lot of time converting things from numbers to strings and back, try using arithmetic. To iterate over the digits of a number n, take n modulo ten (to get the least-significant digit) and then divide by ten (to peel off that least-significant digit). For example, the digits of 123 (in reverse order) are [(123 % 10), (12 % 10), (1 % 10)]
Thinking of it in terms of functions, first get the digits:
def digits_of_n(n):
result = []
while n > 0:
result.append(n % 10)
n = n / 10 # in python 3, use 3 // 10 for integer division
return reversed(result) # reverse list to preserve original order
then get the sum of the powers:
def sum_of_ith_powers(numbers):
result = 0
for i, n in enumerate(numbers): # the digits are ordered most-significant to least, as we would expect
result += n ** 1
return result
now you can just call sum_of_ith_powers(digits_of_n(n)) and you have an answer. If you like, you can give that operation a name:
def sum_of_digit_powers(n):
return sum_of_ith_powers(digits_of_n(n))
and you can then name the function that solves the kata:
def solve_kata(a, b):
return [sum_of_digit_powers(n) for n in range (a, b)]
You can use a generator, sum and enumerate in order to simplify your code like this example:
def sum_dig_pow(a,b):
for k in range(a,b+1):
if k > 9:
number_sum = sum(int(j)**i for i,j in enumerate(str(k), 1))
if k is number_sum:
yield k
else:
yield k
print(list(sum_dig_pow(1,10)))
print(list(sum_dig_pow(1,90)))
print(list(sum_dig_pow(1,10000)))
print(list(sum_dig_pow(10,1000)))
print(list(sum_dig_pow(1,900000)))
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175]
[89, 135, 175]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175]
li = []
def sum_dig_pow(a, b):
for i in range(a, b+1):
sum1 = 0
for ind, val in enumerate(str(i), 1):
sum1 += pow(int(val), int(ind))
if i == sum1:
li.append(i)
return li
print(sum_dig_pow(1, 11))

Generating a list of EVEN numbers in Python

Basically I need help in generating even numbers from a list that I have created in Python:
[1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, ...]
I have tried a couple different methods, but every time I print, there are odd numbers mixed in with the evens!
I know how to generate even/odd numbers if I were to do a range of 0-100, however, getting only the even numbers from the previous mentioned list has me stumped!
P.S. I've only been using python for a couple days, if this turns out to be extremely simple, thanks in advance!
EDIT: Thanks for all the replies, with your help I've gotten through this little problem.
Here is what I ended up with to complete a little excercise asking to sum the even numbers of fibonacci sequence:
F = [1, 2]
while F[-1] < 4000000
F.append(F[-1] + F[-2])
sum(F[1::3])
4613732
Use a list comprehension (see: Searching a list of objects in Python)
myList = [<your list>]
evensList = [x for x in myList if x % 2 == 0]
This is good because it leaves list intact, and you can work with evensList as a normal list object.
Hope this helps!
The following sample should solve your problem.
Newlist = []
for x in numList:
if x % 2 == 0:
print x
Newlist.append(x)
You can do this with a list comprehension:
evens = [n for n in numbers if n % 2 == 0]
You can also use the filter function.
evens = filter(lambda x: x % 2 == 0,numbers)
If the list is very long it may be desirable to create something to iterate over the list rather than create a copy of half of it using ifilter from itertools:
from itertools import ifilter
evens = ifilter(lambda x: x % 2 == 0,numbers)
Or by using a generator expression:
evens = (n for n in numbers if n % 2 == 0)
In your specific case my_list[1::3] will work. There are always two odd integers between even integers in fibonacci: even, odd, odd, even, odd, odd.....
>>> my_list = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368]
>>>
...
>>> my_list[1::3]
[2, 8, 34, 144, 610, 2584, 10946, 46368]
Just check this
A = [i for i in range(101)]
B = [x for x in A if x%2 == 0]
print B
iterate through the list and use the modulo operator to check even
for number in list:
if (number % 2) == 0:
##EVEN
Just for fun, checking if number%2 != 1 also works ;)
evens=[x for x in evens_and_odds if number%2 != 1 ]
Note that you can do some clever things to separate out evens and odds in one loop:
evens=[]
odds=[]
numbers=[ evens, odds ]
for x in evens_and_odds:
numbers[x%2 == 1].append(x)
print evens
print odds
The above trick works because logical expressions (==, >, etc.) operating on numbers True (1) and/or False (0).
You can use list comprehension to generate a new list that contains only the even members from your original list.
data = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
then:
new_data = [i for i in data if not i%2]
yields
[2, 8, 34, 144]
Or alternatively use a generator expression if you don't need all of the numbers
at once:
new_data = (i for i in data if not i%2)
The values then would be availabe as needed, for instance if you used a for loop:
e.g.,
for val in new_data:
print val
The advantage of the generator expression is that the whole list is not generated and stored in memory at once, but values are generated as you need them which makes less demand on memory. There are other important differences you might want to read up on at some point if you are interested.
Instead of generating all Fibonacci numbers then filtering for evens, why not generate just the even values?
def even_fibs():
a,b = 1,2
while True:
yield b
a,b = a+2*b, 2*a+3*b
generates [2, 8, 34, 144, 610, 2584, 10946 ...]
then your sum code becomes:
total = 0
for f in even_fibs():
if f >= 4000000:
break
else:
total += f
or
from itertools import takewhile
total = sum(takewhile(lambda n: n<4000000, even_fibs()))
a = range(0,1000)
b = []
for c in a:
if c%2==0:
b.append(c)
print b
You could do this using the filter function as follows:
F = [1, 2]
while F[-1] < 4000000:
F.append(F[-1] + F[-2])
print(F)
print('\n')
#create the variable that could store the sorted values from the list you have created.
sorted_number=list(filter(lambda x:x%2==0,F))
print(sorted_number)
You could use a for and if loop using the length function, like this:
for x in range(len(numList)):
if x%2 == 0:
print(x)
NewList.append(x)
Basically you should create a variable and put your list in and then sort your even numbers list by adding it only the even numbers
numbers = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, ...]
even = [e for e in numbers if e%2==0]
Here are some of the different ways to get even numbers:
CASE 1
in this case, you have to provide a range
lst = []
for x in range(100):
if x%2==0:
lst.append(x)
print(lst)
CASE 2
this is a function and you have to pass a parameter to check if it is an even no or not
def even(rangeno):
for x in range(rangeno):
if rangeno%2 == 0:
return rangeno
else:
return 'No an Even No'
even(2)
CASE 3
checking the values in the range of 100 to get even numbers through function with list comprehension
def even(no):
return [x for x in range(no) if x%2==0]
even(100)
CASE 4
This case checks the values in list and prints even numbers through lambda function. and this case is suitable for the above problem
lst = [2,3,5,6,7,345,67,4,6,8,9,43,6,78,45,45]
no = list(filter(lambda x: (x % 2 == 0), lst))
print(no)

Odd behavior of stacked filter() calls

So I'm getting some interesting behaviour from some filters stacked within a for loop.
I'll start with a demonstration:
>>> x = range(100)
>>> x = filter(lambda n: n % 2 == 0, x)
>>> x = filter(lambda n: n % 3 == 0, x)
>>> list(x)
[0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]
Here we get the expected output. We have a range within a filter within a filter, and the filter conditions are stacking as we want them to. Now here comes my problem.
I have written a function for calculating the relative primes of a number. It looks like this:
def relative_primes(num):
'''Returns a list of relative primes, relative to the given number.'''
if num == 1:
return []
elif is_prime(num):
return list(range(1, num))
result = range(1, num)
for factor in prime_factors(num):
# Why aren't these filters stacking properly?
result = filter(lambda n: n % factor != 0, result)
return list(result)
For whatever reason, the filter is only being applied to the LAST factor in the list acquired from prime_factors(). Example:
>>> prime_factors(30)
[2, 3, 5]
>>> relative_primes(30)
[1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 29]
We can see that no multiples of 2 or 3 were removed from the list. Why is this happening? Why does the above example work, but the filters in the for loop don't?
In Python 3.x, filter() returns a generator instead of a list. As such, only the final value of factor gets used since all three filters use the same factor. You will need to modify your lambda slightly in order to make it work.
result = filter(lambda n, factor=factor: n % factor != 0, result)
The evaluation of the iterators is lazy. All filters will be evaluated only in the statement
return list(result)
By that time, the value of factor is the last prime factor. The lambda functions only contain a reference to the local name factor and will use whatever value is assigned to that name at the time of execution.
One way to fix this is to convert to a list in every iteration.
As a sidenote, a much easier implementation of this function is
from fractions import gcd
def relative_primes(n):
return [i for i in range(1, n) if gcd(n, i) == 1]
Edit: If you are after performance instead of simplicity, you can also try this one:
def relative_primes(n):
sieve = [1] * n
for i in range(2, n):
if not sieve[i] or n % i:
continue
sieve[::i] = [0] * (n // i)
return list(itertools.compress(range(n), sieve))
If I understood you correctly and Two integers are relatively prime if they share no common positive factors (divisors) except 1. Using the notation to denote the greatest common divisor, two integers a and b are relatively prime if gcd(a,b)==1. then you can use the fractions module in the following way.
from fractions import gcd
num = 30
relative_primes = filter(lambda x: gcd(x,num) == 1, xrange(1,num))

Categories

Resources