简体   繁体   中英

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 xy and large ab:

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 xy, populate all entries in the array that are multiples of that number with true.

Now for each number in ab, 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 ab, 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM