If statement from a number to infinity - python

I'm writing a code which takes a string, then splits it up in its characters and then do something depending on the list length. The problem is I don't know how to write it:
If the length (in characters) = 1, 4, 7, 10... (up to 'infinity') execute x code
If the length (in characters) = 2, 5, 8, 11... (up to 'infinity') execute y code
If the length (in characters) = 3, 6, 9, 12... (up to 'infinity') execute x code
What I mean with infinity is either infinity or a big enough number that no human person would be able to write
I've been working with python for a while I and understand how loops/if statements work, but I've never needed to provided an condition like this one where the condition itself comprehends an range of specific numbers up to infinity. One solution would be write three lists with a lots of numbers in them, but I wanna know if there is an easier and less pain taking way of doing it.
decoded is the variable assigned to the input (which is further up on the code)
The mathematical expression for it would be, for the first statement, 1 + 3n, for the second, 2 +3n, and for the third, 3 +3n, being n a real number between 0 and infinity
decodedl = list(decoded)
if len(decodel) == 1 #rest of the stament here
#execute x chunk of code
if len(decodel) == 2 #rest of the stament here
#execute y chunk of code
if len(decodel) == 3 #rest of the stament here
#execute z chunk of code
The expected result is the following:
If I input, for example: 'Hello how are you', then the code should execute code chunk y, as the length of the generated list would be 17: But as it is now, it would do nothing as the length of the list isn't 1, 2 nor 3.

This is a logical puzzle to solve. Instead of checking membership in all numbers, just observe the pattern as you stated (1 + 3n, 2 + 3n, and 3+ 3n) and build your logic from that. Numbers divisible by 3 have 0 as remainder.
if decodel: #make sure you eliminate empty string case
if len(decodel)%3 == 1: #1 + 3n
pass
elif len(decodel)%3 == 2: #2 + 3n
pass
else: #3n
pass

As you said, what you need is a mathematical expression that matches 1 + 3n in the first case, 2 + 3n in the second and 3 + 3n in the last case.
In order to do so, you can think the problem in the following way:
The first set of numbers will be formed by all those numbers whose remainder after dividing it by 3 is 1 (Ex. 1, 4, 7, 10...)
The second set of numbers will be formed by all those numbers whose remainder after dividing it by 3 is 2 (Ex. 2, 5, 8, 11...)
The third set of numbers will be formed by all those numbers whose remainder after dividing it by 3 is 0 (Ex. 3, 6, 9, 12...)
So now that you figured out how to "classify" those numbers, what we are missing is the mathematical operator that gives you the remainder after dividing A by B.
That mathematical operator is called modulo (more info here). In python the symbol we use is %.
For example, 13 % 3 = 1 and 81 % 3 = 0.
So, to sum up, what you can do to solve your problem is the following:
if len(decodel) % 3 == 1:
#Numbers with remainder 1 when dividing by 3 (i.e. 1, 4, 7...)
elif len(decodel) % 3 == 2:
#Numbers with remainder 2 when dividing by 3 (i.e. 2, 5, 8...)
elif len(decodel) % 3 == 0:
#Numbers that have no remainder when dividing by 3 (i.e. 3, 6, 9...)
Just as a side note, you don't need to know this while programming, but what we found here are called in Discrete Mathematics "congruence class modulo n", in our case the 3 possible congruence classes modulo 3: [0], [1] and [2].

To get the requested behaviour, you can use the modulo operator(%) with argument 3. This will remove the number 3 from it as many times as possible and stops just before it would get to a negative number. What remains will always be 1 for the first set, 2 for the second and 0 for the last. So in code:
if len(decodel)%3 == 1:
#The length of decodel is either 1, 4, 7, 10.....
elif len(decodel)%3 == 2:
#The length of decodel is either 2, 5, 8, 11...
elif len(decodel)%3 == 0:
#The length of decodel is either 3, 6, 9, 12...

If you want to remove the non-alpha characters, this would be useful.
Also, we don't have to compute the remainder in every if checks!
import re
a= 'how good is this?'
a = re.sub("[^a-zA-Z ]","", a)
print(a)
#compute the remainder
rem=len(a)%3
if rem == 1:
print('2 + 3n')
elif rem == 2:
print('2 + 3n')
else:
print('3n')
output:
how good is this
2 + 3n

Related

Python program to make a list even odd parity

Task: count the number of operations required to make an array's values alternate between even and odd.
Given: items = [6, 5, 9, 7, 3] (Example test case)
Operations we can do: make n number of operations: floor(item/2)
My code
def change(expected):
return 1 if (expected == 0) else 0
def getMinimumOperations(items, expected):
countOp = 0
for i in items:
if (int(i % 2 == 0) != expected):
countOp += 1
expected = change(expected)
return countOp
def minChangeToGetMinOp(items):
minStack = [getMinimumOperations(items, 1), getMinimumOperations(items, 0)]
return min(minStack)
if __name__ == "__main__":
items = [6, 5, 9, 7, 3]
print(minChangeToGetMinOp(items))
ANS: 3
What I'm asking: A good approach to solve this
Taking the comment that the division by 2 can be repeated multiple times on the same input number -- until its parity is as expected (or the number becomes 0 and the odd parity is required), the program would need an inner loop:
def getMinimumOperations(items, expected):
countOp = 0
for i in items:
while i % 2 != expected:
if not i:
return float("inf") # Not possible
i //= 2
countOp += 1
expected = 1 - expected
return countOp
def minChangeToGetMinOp(items):
return min(getMinimumOperations(items, 1), getMinimumOperations(items, 0))
if __name__ == "__main__":
items = [6, 5, 9, 7, 3]
print(minChangeToGetMinOp(items)) # 3
This seems like more of a math/logic problem than a Python problem.
To make a list's elements alternate between odd and even, either all the elements at even indices should be even and the rest odd, or all the elements at even indices should be odd and the rest even.
It appears you're not looking to change the order of elements, but to add or subtract one to make them comply.
So, to make the list comply, either you need to change all even elements at odd indices and all odd elements at even indices, or vice versa.
The answer therefore:
def needed_changes(xs):
c = sum((i + n) % 2 for i, n in enumerate(xs))
return min(c, len(xs) - c)
if __name__ == '__main__':
xs = [6, 5, 9, 7, 3]
print(needed_changes(xs))
Output:
2
The solution adds each element to its index, and then takes the mod 2 of it, so that each element becomes either 1 if the index or the value is odd, or 0 if both are odd, or both are even. As a result, the sum of those 1's will be the number of elements that need to be changed - but if that number is more than half, you could just change the rest of the elements instead, which is what the last lines decides.
Edit: From your comment it became clear that operations allowed are limited and the only operation allowed is x = floor(x / 2). This complicates the issue because this operation only results in an even outcome for every other two numbers. The pattern looks like this:
0 0 # 'even' to start with
1 0 # 'even' after 1 operation
2 1 # even to start with
3 1 # still odd after 1 op, after another 'even' (0)
4 2 # even to start with
5 2 # even after 1 op
6 3 # even to start with
7 3 # still odd after 2 ops, needs 3
8 4 # even to start with
..
So, each 4th number will be both odd, and its floor(x/2) will also be odd. If you apply the operation again, only half of those results will be even, etc.
Another way to look at the problem then: if a number's binary representation ends in a 0, it is even. If you take the floor(x/2) of a number, you're really just performing a "shift right" operation. So, if an number's binary representation has a 0 as the second least significant bit, performing the operation will make it even. And so on.
So, a solution using only that operation:
def needed_changes(xs):
ns = []
for x in xs:
n = 0
while x % 2:
n, x = n + 1, x >> 1
ns.append(n)
return min(sum(ns[0::2]), sum(ns[1::2]))
Or, if you don't like building a whole new list and want the solution to save space:
def needed_changes(xs):
ns = [0, 0]
for i, x in enumerate(xs):
n = 0
while x % 2:
n, x = n + 1, x >> 1
ns[i % 2] += n
return min(ns)

How can I get a sum from some elements of a list? [duplicate]

I have a list of numbers. I also have a certain sum. The sum is made from a few numbers from my list (I may/may not know how many numbers it's made from). Is there a fast algorithm to get a list of possible numbers? Written in Python would be great, but pseudo-code's good too. (I can't yet read anything other than Python :P )
Example
list = [1,2,3,10]
sum = 12
result = [2,10]
NOTE: I do know of Algorithm to find which numbers from a list of size n sum to another number (but I cannot read C# and I'm unable to check if it works for my needs. I'm on Linux and I tried using Mono but I get errors and I can't figure out how to work C# :(
AND I do know of algorithm to sum up a list of numbers for all combinations (but it seems to be fairly inefficient. I don't need all combinations.)
This problem reduces to the 0-1 Knapsack Problem, where you are trying to find a set with an exact sum. The solution depends on the constraints, in the general case this problem is NP-Complete.
However, if the maximum search sum (let's call it S) is not too high, then you can solve the problem using dynamic programming. I will explain it using a recursive function and memoization, which is easier to understand than a bottom-up approach.
Let's code a function f(v, i, S), such that it returns the number of subsets in v[i:] that sums exactly to S. To solve it recursively, first we have to analyze the base (i.e.: v[i:] is empty):
S == 0: The only subset of [] has sum 0, so it is a valid subset. Because of this, the function should return 1.
S != 0: As the only subset of [] has sum 0, there is not a valid subset. Because of this, the function should return 0.
Then, let's analyze the recursive case (i.e.: v[i:] is not empty). There are two choices: include the number v[i] in the current subset, or not include it. If we include v[i], then we are looking subsets that have sum S - v[i], otherwise, we are still looking for subsets with sum S. The function f might be implemented in the following way:
def f(v, i, S):
if i >= len(v): return 1 if S == 0 else 0
count = f(v, i + 1, S)
count += f(v, i + 1, S - v[i])
return count
v = [1, 2, 3, 10]
sum = 12
print(f(v, 0, sum))
By checking f(v, 0, S) > 0, you can know if there is a solution to your problem. However, this code is too slow, each recursive call spawns two new calls, which leads to an O(2^n) algorithm. Now, we can apply memoization to make it run in time O(n*S), which is faster if S is not too big:
def f(v, i, S, memo):
if i >= len(v): return 1 if S == 0 else 0
if (i, S) not in memo: # <-- Check if value has not been calculated.
count = f(v, i + 1, S, memo)
count += f(v, i + 1, S - v[i], memo)
memo[(i, S)] = count # <-- Memoize calculated result.
return memo[(i, S)] # <-- Return memoized value.
v = [1, 2, 3, 10]
sum = 12
memo = dict()
print(f(v, 0, sum, memo))
Now, it is possible to code a function g that returns one subset that sums S. To do this, it is enough to add elements only if there is at least one solution including them:
def f(v, i, S, memo):
# ... same as before ...
def g(v, S, memo):
subset = []
for i, x in enumerate(v):
# Check if there is still a solution if we include v[i]
if f(v, i + 1, S - x, memo) > 0:
subset.append(x)
S -= x
return subset
v = [1, 2, 3, 10]
sum = 12
memo = dict()
if f(v, 0, sum, memo) == 0: print("There are no valid subsets.")
else: print(g(v, sum, memo))
Disclaimer: This solution says there are two subsets of [10, 10] that sums 10. This is because it assumes that the first ten is different to the second ten. The algorithm can be fixed to assume that both tens are equal (and thus answer one), but that is a bit more complicated.
I know I'm giving an answer 10 years later since you asked this, but i really needed to know how to do this an the way jbernadas did it was too hard for me, so i googled it for an hour and I found a python library itertools that gets the job done!
I hope this help to future newbie programmers.
You just have to import the library and use the .combinations() method, it is that simple, it returns all the subsets in a set with order, I mean:
For the set [1, 2, 3, 4] and a subset with length 3 it will not return [1, 2, 3][1, 3, 2][2, 3, 1] it will return just [1, 2, 3]
As you want ALL the subsets of a set you can iterate it:
import itertools
sequence = [1, 2, 3, 4]
for i in range(len(sequence)):
for j in itertools.combinations(sequence, i):
print(j)
The output will be
()
(1,)
(2,)
(3,)
(4,)
(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
(1, 2, 3)
(1, 2, 4)
(1, 3, 4)
(2, 3, 4)
Hope this help!
So, the logic is to reverse sort the numbers,and suppose the list of numbers is l and sum to be formed is s.
for i in b:
if(a(round(n-i,2),b[b.index(i)+1:])):
r.append(i)
return True
return False
then, we go through this loop and a number is selected from l in order and let say it is i .
there are 2 possible cases either i is the part of sum or not.
So, we assume that i is part of solution and then the problem reduces to l being l[l.index(i+1):] and s being s-i so, if our function is a(l,s) then we call a(l[l.index(i+1):] ,s-i). and if i is not a part of s then we have to form s from l[l.index(i+1):] list.
So it is similar in both the cases , only change is if i is part of s, then s=s-i and otherwise s=s only.
now to reduce the problem such that in case numbers in l are greater than s we remove them to reduce the complexity until l is empty and in that case the numbers which are selected are not a part of our solution and we return false.
if(len(b)==0):
return False
while(b[0]>n):
b.remove(b[0])
if(len(b)==0):
return False
and in case l has only 1 element left then either it can be part of s then we return true or it is not then we return false and loop will go through other number.
if(b[0]==n):
r.append(b[0])
return True
if(len(b)==1):
return False
note in the loop if have used b..but b is our list only.and i have rounded wherever it is possible, so that we should not get wrong answer due to floating point calculations in python.
r=[]
list_of_numbers=[61.12,13.11,100.12,12.32,200,60.00,145.34,14.22,100.21,14.77,214.35,200.32,65.43,0.49,132.13,143.21,156.34,11.32,12.34,15.67,17.89,21.23,14.21,12,122,134]
list_of_numbers=sorted(list_of_numbers)
list_of_numbers.reverse()
sum_to_be_formed=401.54
def a(n,b):
global r
if(len(b)==0):
return False
while(b[0]>n):
b.remove(b[0])
if(len(b)==0):
return False
if(b[0]==n):
r.append(b[0])
return True
if(len(b)==1):
return False
for i in b:
if(a(round(n-i,2),b[b.index(i)+1:])):
r.append(i)
return True
return False
if(a(sum_to_be_formed,list_of_numbers)):
print(r)
this solution works fast.more fast than one explained above.
However this works for positive numbers only.
However also it works good if there is a solution only otherwise it takes to much time to get out of loops.
an example run is like this lets say
l=[1,6,7,8,10]
and s=22 i.e. s=1+6+7+8
so it goes through like this
1.) [10, 8, 7, 6, 1] 22
i.e. 10 is selected to be part of 22..so s=22-10=12 and l=l.remove(10)
2.) [8, 7, 6, 1] 12
i.e. 8 is selected to be part of 12..so s=12-8=4 and l=l.remove(8)
3.) [7, 6, 1] 4
now 7,6 are removed and 1!=4 so it will return false for this execution where 8 is selected.
4.)[6, 1] 5
i.e. 7 is selected to be part of 12..so s=12-7=5 and l=l.remove(7)
now 6 are removed and 1!=5 so it will return false for this execution where 7 is selected.
5.)[1] 6
i.e. 6 is selected to be part of 12..so s=12-6=6 and l=l.remove(6)
now 1!=6 so it will return false for this execution where 6 is selected.
6.)[] 11
i.e. 1 is selected to be part of 12..so s=12-1=1 and l=l.remove(1)
now l is empty so all the cases for which 10 was a part of s are false and so 10 is not a part of s and we now start with 8 and same cases follow.
7.)[7, 6, 1] 14
8.)[6, 1] 7
9.)[1] 1
just to give a comparison which i ran on my computer which is not so good.
using
l=[61.12,13.11,100.12,12.32,200,60.00,145.34,14.22,100.21,14.77,214.35,145.21,123.56,11.90,200.32,65.43,0.49,132.13,143.21,156.34,11.32,12.34,15.67,17.89,21.23,14.21,12,122,134]
and
s=2000
my loop ran 1018 times and 31 ms.
and previous code loop ran 3415587 times and took somewhere near 16 seconds.
however in case a solution does not exist my code ran more than few minutes so i stopped it and previous code ran near around 17 ms only and previous code works with negative numbers also.
so i thing some improvements can be done.
#!/usr/bin/python2
ylist = [1, 2, 3, 4, 5, 6, 7, 9, 2, 5, 3, -1]
print ylist
target = int(raw_input("enter the target number"))
for i in xrange(len(ylist)):
sno = target-ylist[i]
for j in xrange(i+1, len(ylist)):
if ylist[j] == sno:
print ylist[i], ylist[j]
This python code do what you asked, it will print the unique pair of numbers whose sum is equal to the target variable.
if target number is 8, it will print:
1 7
2 6
3 5
3 5
5 3
6 2
9 -1
5 3
I have found an answer which has run-time complexity O(n) and space complexity about O(2n), where n is the length of the list.
The answer satisfies the following constraints:
List can contain duplicates, e.g. [1,1,1,2,3] and you want to find pairs sum to 2
List can contain both positive and negative integers
The code is as below, and followed by the explanation:
def countPairs(k, a):
# List a, sum is k
temp = dict()
count = 0
for iter1 in a:
temp[iter1] = 0
temp[k-iter1] = 0
for iter2 in a:
temp[iter2] += 1
for iter3 in list(temp.keys()):
if iter3 == k / 2 and temp[iter3] > 1:
count += temp[iter3] * (temp[k-iter3] - 1) / 2
elif iter3 == k / 2 and temp[iter3] <= 1:
continue
else:
count += temp[iter3] * temp[k-iter3] / 2
return int(count)
Create an empty dictionary, iterate through the list and put all the possible keys in the dict with initial value 0.
Note that the key (k-iter1) is necessary to specify, e.g. if the list contains 1 but not contains 4, and the sum is 5. Then when we look at 1, we would like to find how many 4 do we have, but if 4 is not in the dict, then it will raise an error.
Iterate through the list again, and count how many times that each integer occurs and store the results to the dict.
Iterate through through the dict, this time is to find how many pairs do we have. We need to consider 3 conditions:
3.1 The key is just half of the sum and this key occurs more than once in the list, e.g. list is [1,1,1], sum is 2. We treat this special condition as what the code does.
3.2 The key is just half of the sum and this key occurs only once in the list, we skip this condition.
3.3 For other cases that key is not half of the sum, just multiply the its value with another key's value where these two keys sum to the given value. E.g. If sum is 6, we multiply temp[1] and temp[5], temp[2] and temp[4], etc... (I didn't list cases where numbers are negative, but idea is the same.)
The most complex step is step 3, which involves searching the dictionary, but as searching the dictionary is usually fast, nearly constant complexity. (Although worst case is O(n), but should not happen for integer keys.) Thus, with assuming the searching is constant complexity, the total complexity is O(n) as we only iterate the list many times separately.
Advice for a better solution is welcomed :)

How does this if all statement work? Euler 7

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))

Project euler 12 python code doesn't run, is it slow or what?

def num_of_divisors(x):
k=0
b=x
while x%2==0:
x=x//2
k=k+1
z=1
while x!=1:
for y in range(3,b,2):
a=0
while x%y==0:
x=x//y
a=a+1
z=z*(a+1)
return(z*(k+1))
def triangular(n):
return(n*(n+1)//2)
for n in range(1,10000):
jk=triangular(n)
if num_of_divisors(jk)>500:
print(triangular(n))
break
the problem is :
The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
Let us list the factors of the first seven triangle numbers:
1: 1
3: 1,3
6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28
We can see that 28 is the first triangle number to have over five divisors.
What is the value of the first triangle number to have over five hundred divisors?
The problem is to find the first triangular number with more than 500 divisors.
I separately checked the num_of_divisors() function , it works correctly . I also checked num_of_divisors(76576500), it returns 576 correctly.
The triangular function also works correctly.
But the above program doesn't run . So the problem is in the loop. But what is it? I mean , it runs but nothing happens. I thought it might be very slow so I replaced 500 with 1 , yet it doesn't work! What is wrong?
The while x!=1 is putting you in an infinite loop when the number is a prime number. That's because only the number itself can divide it, and your range does not get that number.
For example, when x is 3, 5, or 7 (all primes) -
In [12]: list(range(3,3,2))
Out[12]: []
In [14]: list(range(3,5,2))
Out[14]: [3]
In [15]: list(range(3,7,2))
Out[15]: [3, 5]
The only way to break out of the while x!=1 loop is when x==1, but that does not happen because x never gets divided by itself. You can avoid that by letting your range go further.

Finding numbers from a to b not divisible by x to y

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.

Categories

Resources