Here the problem is to get all 4 pairs factors of pretty big number 17309205.
Results should be
{1,1,1,17309205}
{1,1,3,5769735} etc..
I tried with 4 nested for loops but it took too long, so I tried a different idea.
My way of thinking is to find every possible pairs of factors and then filter it out for those containing 4 pairs. But now I only get one result.
And also the results isn't printed in the way it should be.
def s(target, numbers,memo={}):
if target == 0:
return 0
if target == 1:
return 1
if target < 0:
pass
if target in memo:
return memo[target]
result = []
for n in numbers:
if target%n !=0:
pass
else:
dominator = target/n
result = s(dominator, numbers, memo)
memo[target] = [result,[n,dominator]]
return memo[target]
v = list(range(2,17309205))
print(s(17309205,v))
I see a possible solution. First you have to calculate all the prime factors of the number.
For that you need to create a function which yields prime numbers sequentially.
The function should store the primes in a list and test if the next non tested number is divisible by any prime in the list. If the number is not divisible by any prime in the list, then the number is prime.
For example when you have the list of primes [2,3,5,7] test divisibility of 8 by 2,3,5 and 7. It is divisible by 2 so it is not prime, then go with 9, etc... When reach 11 it is prime so put it in the list and yield 11.
Once calculated all the factor primes of the number you gave, you have to assign each factor to one of 4 places in a list. Then "put" all factors in its corrsponding place by multiplying the number in the place by the factor. You start with [1,1,1,1].
To get all 4 pair factors you need all possible assignments of factors. That is the set product of [0,1,2,3] with itself N times, where N is the number of factors.
(how many pairs are there for N factors?)
For example if there are 5 factors, then an element of the set product is [1, 0, 2, 3, 3] which assign first factor to place 1, second factor to place 0, third factor to place 2 and fourth a fifth factor to place 3.
For the set product we use itertools.product:
import itertools
assignments = itertools.product([0,1,2,3], repeat = len(factors))
for a in assignments:
pair = [1,1,1,1]
for i, place in enumerate(a):
pair[place] *= factor[i]
pairs.append(pair)
I can get desire results with below code but the problems is being too slow if target is too big.
target = 16
for a in range(1,target+1):
for b in range(1,target+1):
for c in range(1, target+1):
for d in range(1, target+1):
if a<=b<=c<=d and a*b*c*d == target:
print (a,b,c,d)
Related
import itertools as itt
layer_thickness=[1,2,3,4,5]
permu= itt.permutations(layer_thickness,5)
permu_list=list(permu)
for i in permu_list:
if sum(i)==15:
print(i)
Here, I want permutations of the elements in the layer_thickness and those sum of the permutations should be to 5. But the number of elements in prmutation is not constrained by any limit unless it gives the desired sum.
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 2 2 2 2 2 2 2 1, etc are should be an element also.
what modifications should I do to achieve that?
You cant create all permutation as list for any total - it will simply hug too much memory:
Assuming [1,2,3,4,5] as numbers, 1 is your lowest element.
Assuming you want to reach a total = 15 - your "biggest" solution is (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1).
To create all possible permutation of 5 numbers over 15 places you need to create 15**5 list entries: 15**5 = 2.562.890.625 possible number permutations
Storing each combination as 1 byte this would need 2.56 Terabyte of ram. Doubt you got that much.
The best you can do is to generate only numbers that work and quit out as soon as you reached your total. To do that for a fixed number set you can start with this question: Finding all possible combinations of numbers to reach a given sum
Using that and provide a "maximized" list of your numbers that can be added up to achieve your goal might lead to something:
def subset_sum(numbers, target, partial=[], partial_sum=0):
if partial_sum == target:
yield partial
if partial_sum >= target:
return
for i, n in enumerate(numbers):
remaining = numbers[i + 1:]
yield from subset_sum(remaining, target, partial + [n], partial_sum + n)
Credit: https://stackoverflow.com/a/4633515/7505395
def calc_nums_for_permutate(nums, total):
"""Creates a list of numbers from num - each single number is added as
many times as itself fits into total"""
return [n for n in nums for _ in range(total//n)]
if __name__ == "__main__":
nums = [1, 2, 3, 4, 5]
total = 15
print( *subset_sum( calc_nums_for_permutate(nums, total), total))
This will not work for all and any inputs - good luck with your runtime, this will still work reasonably well for a total = 10 - for a total = 15 it will take more time then I needed to format/copy paste and formulate this answer here.
I am trying to write a function that takes in a list of integers and finds the maximum number of factors that appear in that same list. For example, if I have a list with contents 9,5,6,3,2 then 9 has two factors that appear in the list (9 & 3), 5 has one factor that appears in the list (5), 6 has three factors that appear in the list (6, 3, 2), and so on. In this case, the output would be 3 (6 has the max number of factors that appear in the list with 3 total). This is what I have so far:
def divisibility(keys):
#sort keys in descending order - larger numbers w same divisors
#will always have more factors so we can remove them from the list if they are used
sortkeys = sorted(keys,reverse=True)
#compare max divisibility to divisibility of each number in keys
#(will always be at least 1 since a number is always divisible by itself)
max_divisibility = 1
while len(sortkeys) > 0:
divisibility = 1
#when done testing largest number, remove it from the list
removekeys = [sortkeys[0]]
for i in range(1,len(sortkeys)):
#test largest number
if sortkeys[0] % sortkeys[i] == 0:
divisibility += 1
#remove that number from the list
removekeys.append(sortkeys[i])
if divisibility > max_divisibility:
max_divisibility = divisibility
#remove keys in list
sortkeys = list(set(sortkeys).difference(removekeys))
return max_divisibility
This works sometimes, but in the case mentioned above it fails, because 3 is removed from the list on the first pass, so by the time we get to 6 there are only two factors of 6 left in the list (6 and 2). I know you could just check every single element of the list, but I'd really like to make this as efficient as possible, so any advice there would be appreciated.
A simpler algorithm is the following.
def factors(n, lst):
' Count of factors of n in list lst '
return sum(n % i == 0 for i in lst)
def divisibility(keys):
' Find element in keys with most factors '
return max(factors(k, keys) for k in keys)
lst = [9,5,6,3,2]
print(divisibility(lst)) # 3
You can use sum() in a comprehension to count the factors of each number and then max() to get the highest count:
def maxFactors(N): return max(sum(a%b==0 for b in N) for a in N)
maxFactors([9,5,6,3,2]) # 3
While it may be possible to reduce the complexity from O(n^2) to O(NlogN) or less, the actual performance may not be better because of the higher overhead required.
for example (using sorting and binary search):
from bisect import bisect_right
def maxFactors(N):
sN = sorted(N)
return max(sum(n%sN[i]==0 for i in range(0,bisect_right(sN,n))) for n in N)
This is actually slower than the previous one on the small example [9,5,6,3,2] but performs 3% better for a list of 50 values and 45% better on 500 values.
Some optimization could provide better performance when the data meets specific conditions such as when the ratio of maximum/minimum is smaller than the number of elements.
for example:
from collections import Counter
def maxFactors(N):
fRange = range(1,max(N)//min(N)+1)
cN = Counter(N)
return max(sum(cN[n//f] for f in fRange if n%f==0) for n in cN)
This would give an O(NxF) complexity where F is the ratio of maximum/minimum.
It is also slower than the first one on the small example but runs 8 times faster on range(25,75) and 38 times faster on range(250,750)
To get the best performance you could select one of these 3 based on a quick assessment of the data. For example, use the Counter() approach when the max/min ratio is less than len(N) or use the binary search when len(N) is large enough.
def maxFactors(N):
fRange = range(1,max(N)//min(N)+1)
if len(fRange)<len(N):
cN = Counter(N)
return max(sum(cN[n//f] for f in fRange if n%f==0) for n in cN)
if len(N) < 50:
return max(sum(a%b==0 for b in N) for a in N)
sN = sorted(N)
return max(sum(n%sN[i]==0 for i in range(0,bisect_right(sN,n))) for n in sN)
I like #DarrylG's answer, but still, here's a modified version of the algo in the question, which does print 3:
def divisibility2(keys):
#sort keys in descending order - larger numbers w same divisors
#will always have more factors so we can remove them from the list if they are used
sortkeys = sorted(keys)
#compare max divisibility to divisibility of each number in keys
#(will always be at least 1 since a number is always divisible by itself)
max_divisibility = 1
while len(sortkeys) > 0:
divisibility = 1
#when done testing largest number, remove it from the list
largest = sortkeys.pop()
for elem in sortkeys:
#test largest number
if largest % elem == 0:
divisibility += 1
if divisibility > max_divisibility:
max_divisibility = divisibility
return max_divisibility
I was looking at the code below and I can't seem to get my head around line 6 with the if all statement.
Could someone explain what it is doing and what happens in the first iteration when the list is empty. This is from Euler 7
def main():
number_prime_to_find = 1001
x = 2
list_of_primes = []
while (len(list_of_primes) < number_prime_to_find):
if all(x % prime for prime in list_of_primes):
list_of_primes.append(x)
The block:
if all(x % prime for prime in list_of_primes):
list_of_primes.append(x)
means "check that, for all primes in the list, x is not a multiple of that prime". It's effectively the same as (pseudo-code):
xIsAPrime = True
for each prime in list_of_primes:
if x % prime == 0:
xIsAPrime = False
break
if xIsAPrime:
list_of_primes.append(x)
If a candidate prime is not a multiple of any primes in the list, then that candidate prime is an actual prime, so is added to the list.
If the list of primes is empty (initial state), then the first number you check will be considered a prime (you're starting at 2 so that's correct).
The reason for this is that all(x) simply means "true if there are no falsey values in the iterable x, false otherwise". Since an empty iterable cannot have falsey values by definition, performing all() on it will always give true.
Following that (the addition of 2 to the list):
The next prime will then be the next number that's not a multiple of two, so we add 3;
The next prime after that will be the next number that's not a multiple of two or three, so we add 5;
The next prime after that will be the next number that's not a multiple of any in the set { 2, 3, 5 }, hence we add 7;
The next prime after that will be the next number that's not a multiple of any in the set { 2, 3, 5, 7 }, add 11;
And so on, until the list size gets big enought that the final item is the prime we want.
Unfortunately for you, that bit of code doesn't seem to ever change x (unless you've just left it off in the question) so it's unlikely to give you what you want, especially since you also don't seem to print or return it. You can rectify that with:
def main():
number_prime_to_find = 1001
list_of_primes = []
x = 2
while len(list_of_primes) < number_prime_to_find:
if all(x % prime for prime in list_of_primes):
list_of_primes.append(x)
x += 1 # Need to modify x
return list_of_primes[-1] # Probably also want to output it.
print(main())
As an aside, you could further optimise this by realising that, other then two and three, all primes are of the form 6n ± 1, for n > 0 (see here for a more detailed explanation as to why this is the case):
def main():
number_prime_to_find = 1001
list_of_primes = [2, 3]
(x, xadd) = (5, 2)
while len(list_of_primes) < number_prime_to_find:
if all(x % prime for prime in list_of_primes):
list_of_primes.append(x)
x += xadd # Skip impossible primes.
xadd = 6 - xadd # Alternate adding 2, 4: gives 7, 11, 13, 17, ...
return list_of_primes[-1]
print(main())
This is likely to be about (very roughly) three times faster, since you only need to check two candidates in each group of six numbers, rather than the original six candidates.
You may also want to consider making it a more generic function. While finding the 1001st prime is something we all want to do three or four times a day, you may want to avoid having to write a whole other function should some new starter on the team decide they want to get the 1002nd :-)
Something like this, I would imagine:
def getNthPrime(n):
# Cater for smart-alecs :-)
if n < 1: return None
# Initial list holds those that aren't 6n +/- 1.
primeList = [2, 3]
# Next candidate and candidate increment.
(candidate, delta) = (5, 2)
# Until we have enough primes in list.
while len(primeList) < n:
# Add to list if it's prime, move to next candidate.
if all(candidate % prime for prime in primeList):
primeList.append(candidate)
candidate += delta
delta = 6 - delta
# List big enough, return correct one. Note we use n-1 here
# rather than -1, since you may want prime #1 and the list
# STARTS with two primes. We subtract one because first prime
# is actually at index zero.
return primeList[n-1]
print(getNthPrime(1001))
New to Python and was trying to solve a challenge to find prime factors for large numbers. I soon ran into a roadblock with the logic I was going for. Broadly speaking, my logic was:
Find the factors for a given number and store them in a list
From that list, evaluate each number to check if prime, and if prime
store in a new list
So after #2 I would have a list of primes that were factors of the original number. Got stuck after I realized, I would have to determine the combination of elements in this second list that would make up the original number because all the elements although prime, couldn't necessarily be multiplied with each other to get the original number.
Anyway, after struggling for quite a bit I found the following code that helped with the function I was trying to create.
def prime_factors(n):
# Returns all the prime factors of a positive integer
factors = []
d = 2
while n > 1:
while n % d == 0: #if no remainder
factors.append(d)
n /= d
d = d + 1
if d*d > n:
if n > 1: factors.append(n)
break
return factors
pfs = prime_factors(600851475143)
print(pfs)
total = sum(pfs)
and gives output
[71, 839, 1471, 6857]
Needed some help understanding how this works and also how this is so fast with a 9 digit number. My previous attempts with some logic that either didn't work or would have my command prompt in hang state trying to calculate for the prime factors this big.
Your code is fast because it is using a primality test, an interesting topic.
Keep reading for another solution much simpler for newcomers,
This function will check whether the given integer n is prime or not, when we divide n by i (which is initialized to 2), if the result is 0, the result will be False, because a prime number can only be divided by 1 or itself, if not, i will be incremented (increased) by 1, if i is finally equal to n, the result will be True, because the result of the division of n by i doesn't equal to 0.
def prime(n):
for i in range(2,n): # letting i have multiple values starting from 2
if n % i == 0: # checking the remainder, if it's 0, then n is not prime,
return False # and so False will be returned,
else:
i += 1 # if n is still considered prime, i will become 3, then 4, and so on
# until it becomes equal to n
return True # if so, then n is prime, so True will be returned
Here we will have an empty list called factors in which we will store the prime factors, now take a look at the 4th line, you will see notice that: if n % i == 0 and i is prime, we will add it to our list called factors, we will do that only and only if the two expressions below are True, if the division of n by i doesn't equal to 0 OR/AND i is not prime (meaning prime(i) returned False), we will not then add i to our list, note that i in this function prime_factors is an integer which will be verified to be a factor of n and be prime too.
def prime_factors(n):
factors = [] #initializing an empty list that will soon be modified
for i in range(2,n):
if (n % i == 0) and (prime(i)): # if i is a factor of n and also i is prime
factors.append(i) # the values will be added to our list named factors
return factors # this list contains the prime factors values which, we return it
Giving n a value and then showing the prime factors
n = 81210459 # the value of n
pfs = prime_factors(n) # storing our list returned from this function is the variable pfs
print("prime factors of",n,"are:",pfs) # showing the results (this is obvious isn't it :D)
Output:
Prime factors of 81210459 are: [3, 11, 61, 40343]
This is a problem I've been pondering for quite some time.
What is the fastest way to find all numbers from a to b that are not divisible by any number from x to y?
Consider this:
I want to find all the numbers from 1 to 10 that are not divisible by 2 to 5.
This process will become extremely slow if I where to use a linear approach;
Like this:
result = []
a = 1
b = 10
x = 2
y = 5
for i in range(a,b):
t = False
for j in range(x,y):
if i%j==0:
t = True
break
if t is False:
result.append(i)
return result
Does anybody know of any other methods of doing this with less computation time than a linear solution?
If not, can anyone see how this might be done faster, as I am blank at this point...
Sincerely,
John
[EDIT]
The range of the number are 0 to >1,e+100
This is true for a, b, x and y
You only need to check prime values in the range of the possible divisors - for example, if a value is not divisible by 2, it won't be divisible by any multiple of 2 either; likewise for every other prime and prime multiple. Thus in your example you can check 2, 3, 5 - you don't need to check 4, because anything divisible by 4 must be divisible by 2. Hence, a faster approach would be to compute primes in whatever range you are interested in, and then simply calculate which values they divide.
Another speedup is to add each value in the range you are interested in to a set: when you find that it is divisible by a number in your range, remove it from the set. You then should only be testing numbers that remain in the set - this will stop you testing numbers multiple times.
If we combine these two approaches, we see that we can create a set of all values (so in the example, a set with all values 1 to 10), and simply remove the multiples of each prime in your second range from that set.
Edit: As Patashu pointed out, this won't quite work if the prime that divides a given value is not in the set. To fix this, we can apply a similar algorithm to the above: create a set with values [a, b], for each value in the set, remove all of its multiples. So for the example given below in the comments (with [3, 6]) we'd start with 3 and remove it's multiples in the set - so 6. Hence the remaining values we need to test would be [3, 4, 5] which is what we want in this case.
Edit2: Here's a really hacked up, crappy implementation that hasn't been optimized and has horrible variable names:
def find_non_factors():
a = 1
b = 1000000
x = 200
y = 1000
z = [True for p in range(x, y+1)]
for k, i in enumerate(z):
if i:
k += x
n = 2
while n * k < y + 1:
z[(n*k) - x] = False
n += 1
k = {p for p in range(a, b+1)}
for p, v in enumerate(z):
if v:
t = p + x
n = 1
while n * t < (b + 1):
if (n * t) in k:
k.remove(n * t)
n += 1
return k
Try your original implementation with those numbers. It takes > 1 minute on my computer. This implementation takes under 2 seconds.
Ultimate optimization caveat: Do not pre-maturely optimize. Any time you attempt to optimize code, profile it to ensure it needs optimization, and profile the optimization on the same kind of data you intend it to be optimized for to confirm it is a speedup. Almost all code does not need optimization, just to give the correct answer.
If you are optimizing for small x-y and large a-b:
Create an array with length that is the lowest common multiple out of all the x, x+1, x+2... y. For example, for 2, 3, 4, 5 it would be 60, not 120.
Now populate this array with booleans - false initially for every cell, then for each number in x-y, populate all entries in the array that are multiples of that number with true.
Now for each number in a-b, index into the array modulo arraylength and if it is true, skip else if it is false, return.
You can do this a little quicker by removing from you x to y factors numbers whos prime factor expansions are strict supersets of other numbers' prime factor expansions. By which I mean - if you have 2, 3, 4, 5, 4 is 2*2 a strict superset of 2 so you can remove it and now our array length is only 30. For something like 3, 4, 5, 6 however, 4 is 2*2 and 6 is 3*2 - 6 is a superset of 3 so we remove it, but 4 is not a superset of everything so we keep it in. LCM is 3*2*2*5 = 60. Doing this kind of thing would give some speed up on its own for large a-b, and you might not need to go the array direction if that's all you need.
Also, keep in mind that if you aren't going to use the entire result of the function every single time - like, maybe sometimes you're only interested in the lowest value - write it as a generator rather than as a function. That way you can call it until you have enough numbers and then stop, saving time.