Нахождение чисел от а до b, не кратных х до у

Это проблема, над которой я размышлял довольно давно.

Как быстрее всего найти все числа от а до b, которые не делятся ни на одно число от х до у?

Учти это:

Я хочу найти все числа от 1 до 10, которые не делятся на 2 и 5. Этот процесс станет очень медленным, если я буду использовать линейный подход; Как это:

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

Кто-нибудь знает какие-либо другие методы сделать это с меньшим временем вычислений, чем линейное решение?

Если нет, может ли кто-нибудь увидеть, как это может быть сделано быстрее, так как я пока ничего не понимаю...

С уважением, Джон

[ИЗМЕНИТЬ]

Диапазон числа от 0 до >1,e+100

Это верно для a, b, x и y


person JohnWO    schedule 12.04.2013    source источник
comment
Вы оптимизируете для больших (b-a), больших b, больших (y-x), больших y или для многократного вызова с небольшими числами? Я подозреваю, что ответ будет варьироваться в зависимости от этих вопросов   -  person Patashu    schedule 12.04.2013
comment
Это часть проблемы: a, b, x, y постепенно растут.   -  person JohnWO    schedule 12.04.2013
comment
Разве вы не хотели написать 1e100 вместо 1,e+100? Если это так, то найти очень быстрый метод будет сложно, так как набор чисел не помещается в память или даже на жесткий диск (пока). Если подсчет чисел разумен (скажем, около 1e8, чтобы они помещались в памяти), то можно получить быстрый подход, обменяв память на скорость.   -  person Eric O Lebigot    schedule 12.04.2013


Ответы (2)


Вам нужно только проверить простые значения в диапазоне возможных делителей - например, если значение не делится на 2, оно также не будет делиться ни на одно кратное 2; аналогично для любого другого простого числа и простого кратного. Таким образом, в вашем примере вы можете проверить 2, 3, 5 - вам не нужно проверять 4, потому что все, что делится на 4, должно делиться на 2. Следовательно, более быстрым подходом было бы вычисление простых чисел в любом интересующем вас диапазоне, а затем просто вычислить, какие значения они делят.

Еще одно ускорение заключается в добавлении каждого значения в интересующем вас диапазоне к set: когда вы обнаружите, что оно делится на число в вашем диапазоне, удалите его из набора. Затем вы должны тестировать только те числа, которые остались в наборе — это остановит многократное тестирование чисел.

Если мы объединим эти два подхода, мы увидим, что мы можем создать set всех значений (например, набор со всеми значениями от 1 до 10) и просто удалить кратные каждому простому числу во втором диапазоне из этого набора.

Редактировать: как указал Паташу, это не совсем сработает, если простое число, которое делит данное значение, отсутствует в наборе. Чтобы исправить это, мы можем применить алгоритм, аналогичный приведенному выше: создать set со значениями [a, b], для каждого значения в set удалить все его кратные. Итак, для примера, приведенного ниже в комментариях (с [3, 6]), мы начнем с 3 и удалим его кратные в наборе - так что 6. Следовательно, оставшиеся значения, которые нам нужно проверить, будут [3, 4, 5], что нам и нужно в данном случае.

Edit2: Вот действительно взломанная, дрянная реализация, которая не была оптимизирована и имеет ужасные имена переменных:

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

Попробуйте свою оригинальную реализацию с этими числами. На моем компьютере это занимает > 1 минуты. Эта реализация занимает менее 2 секунд.

person Yuushi    schedule 12.04.2013
comment
Это неверно, например 7*11 не делится ни на 2, ни на 3, ни на 4, ни на 5, но и не является простым числом. - person Patashu; 12.04.2013
comment
@Patashu Вы неправильно поняли то, что я сказал (хотя я согласен, что не очень хорошо это сформулировал). Я имею в виду, что в диапазоне, скажем, [2, 5] вам нужно проверить только 2, 3, 5 - тестирование для 2 будет проверять для 4 и всех других кратных двум. Точно так же для проверки делимости в [2, 10] вам нужно будет только проверить 2, 3, 5, 7. - person Yuushi; 12.04.2013
comment
так что вам нужно только проверить, делится ли он на простые числа, что он говорит: P - person Joran Beasley; 12.04.2013
comment
@Yuushi Хорошо, теперь я понимаю, какие простые числа вы имеете в виду. - person Patashu; 12.04.2013
comment
@Yuushi Что делать, если у вас есть 3, 4, 5, 6? 4 не простое число, но вам нужно оставить его, потому что вы не тестируете 2. - person Patashu; 12.04.2013
comment
@Yuushi Я дал ответ с некоторыми собственными мыслями. Трудно дать лучший ответ на вопрос, не зная, для какого варианта использования мы оптимизируем - person Patashu; 12.04.2013
comment
Спасибо, но это тоже линейный подход, и на самом деле он медленнее, чем в приведенном примере; По крайней мере с питоном. - person JohnWO; 12.04.2013
comment
@ user2272969 Вы уверены, что это медленнее, чем в примере? Представьте, что x-y достаточно мал (4-10 чисел), а ab огромен (миллиарды и более). Мое решение оптимизировано для этого случая: вы выполняете дополнительную работу, чтобы один раз сделать x-y, а затем для каждой записи в a-b вам нужно выполнить только один мод и один поиск в массиве. Я предполагаю, что потребуется некая странная золотая середина между тем, чтобы x-y был достаточно большим и не слишком большим, и тем, чтобы ab было действительно огромным, но... Вот видите, вот почему я не люблю преждевременную оптимизацию XD - person Patashu; 12.04.2013
comment
@user2272969 user2272969 Возможно, медленнее с очень маленьким примером, как у вас. Сделайте числа намного больше, и это намного быстрее. - person Yuushi; 12.04.2013
comment
Отличный кусок кода, но он ведет себя не так, как ожидалось. Попробуйте изменить значения на: a = 1 b = 10000 x = 2 y = 10000 - person JohnWO; 12.04.2013
comment
@user2272969 user2272969 У меня нормально работает? Дает тот же ответ, что и ваш код. - person Yuushi; 12.04.2013
comment
Как это сделать для одного номера, предоставляется только x? - person Rohit-Pandey; 14.10.2017

Предупреждение об окончательной оптимизации: не оптимизируйте преждевременно. Каждый раз, когда вы пытаетесь оптимизировать код, профилируйте его, чтобы убедиться, что он нуждается в оптимизации, и профилируйте оптимизацию на том же типе данных, для которого вы собираетесь его оптимизировать, чтобы убедиться, что это ускорение. Почти весь код не нуждается в оптимизации. оптимизация, просто чтобы дать правильный ответ.

Если вы оптимизируете для малых xy и больших ab:

Создайте массив с наименьшим общим кратным из всех x, x+1, x+2... y. Например, для 2, 3, 4, 5 будет 60, а не 120.

Теперь заполните этот массив логическими значениями - сначала false для каждой ячейки, затем для каждого числа в x-y заполните все записи в массиве, кратные этому числу, значением true.

Теперь для каждого числа в a-b индексируйте массив по модулю длины массива, и если это правда, пропустите, если оно ложно, вернитесь.

Вы можете сделать это немного быстрее, удалив из ваших чисел с множителями от x до y, разложения простых множителей которых являются строгими надмножествами разложений простых множителей других чисел. Я имею в виду, что если у вас есть 2, 3, 4, 5, 4, это 2 * 2 строгое надмножество 2, поэтому вы можете удалить его, и теперь длина нашего массива составляет всего 30. Для чего-то вроде 3, 4, 5, 6 однако 4 равно 2*2, а 6 равно 3*2. 6 является надмножеством 3, поэтому мы удаляем его, но 4 не является надмножеством всего, поэтому оставляем его. НОК равно 3*2*2*5 = 60. Выполнение такого рода вещей само по себе дало бы некоторое ускорение для больших ab, и вам может не понадобиться идти в направлении массива, если это все, что вам нужно.

Кроме того, имейте в виду, что если вы не собираетесь использовать весь результат функции каждый раз — например, иногда вас интересует только наименьшее значение — напишите его как генератор, а не как функцию. Таким образом, вы можете звонить до тех пор, пока у вас не будет достаточно номеров, а затем остановиться, сэкономив время.

person Patashu    schedule 12.04.2013
comment
Спасибо за ответ! Это намного быстрее, чем приведенный пример, когда, как вы заявили: оптимизация для малых xy и больших ab Проблемы возникают, когда диапазон xy становится большим. Просто чтобы не возникало путаницы: то, что вы обозначили как x-y, я обозначил как a-b. - person JohnWO; 12.04.2013
comment
@ user2272969 Я должен использовать ту же схему именования, что и вы. - person Patashu; 12.04.2013