Самые высокие уникальные простые множители в диапазоне

Я пытаюсь масштабировать решение, которое у меня есть, для практического вопроса Hackerrank.

Таким образом, вопрос заключается в том, чтобы найти максимальное количество простых множителей в диапазоне.

Для 1..500 это 4 for 210 -> 2(1), 3(1), 5(1), 7(1)
Для 1..5000 это 5 for 2310 -> 2(1), 3(1), 5(1), 7(1), 11(1)
Для 1..50000 это 6 for 30030 -> 2(1), 3(1), 5(1), 7(1), 11(1), 13(1)

это мое решение

require 'prime'
max = 0
for d in 1..n
    pfs = d.prime_division.count
    max = pfs if pfs > max
end
puts max

Это займет вечность для n = 10000000000.

Возможно, я смотрю на решение с неправильной точки зрения.
Как масштабировать это решение?


person iGbanam    schedule 11.01.2017    source источник
comment
Нужно ли вам найти максимальное количество уникальных простых множителей в диапазоне? Потому что ваш код, кажется, делает именно это.   -  person Coolness    schedule 11.01.2017
comment
Да. И это то, что делает мой код. Но это не масштабируется, @Coolness   -  person iGbanam    schedule 11.01.2017
comment
Да, просто вы можете отредактировать это (на случай, если кто-то посмотрит на это в будущем), потому что неуникальная проблема - это другая :)   -  person Coolness    schedule 11.01.2017
comment
Зачем спешить с выбором ответа?   -  person Cary Swoveland    schedule 11.01.2017
comment
@CarySwoveland: При всем уважении: вы ответили на этот вопрос после того, как я закончил свой ответ. Структура вашего кода очень похожа на мой последний метод (я, конечно, не имею в виду, что вы его скопировали: просто он не добавляет большой ценности после ответа на вопрос), и ваш метод на самом деле не возвращает желаемый результат. Так что, пожалуйста, не расстраивайтесь: вы отомстите за другой вопрос.   -  person Eric Duminil    schedule 12.01.2017
comment
@ Эрик, я не предлагаю выбрать мой ответ. Я часто упрекаю тех, кто задает вопросы за быстрый выбор, независимо от того, отправил ли я ответ, работаю над ним, не собираюсь отвечать или представил ответ, который был выбран (в этом случае я обычно предлагаю удалить зеленый) )! Обычно я указываю на недостатки быстрого выбора (это может препятствовать другим ответам и, imo, неуважительно по отношению к тем, кто все еще работает над ответами) и спрашиваю, есть ли какой-либо аргумент в пользу быстрого выбора.   -  person Cary Swoveland    schedule 12.01.2017
comment
Понятно. Спасибо за объяснение.   -  person Eric Duminil    schedule 12.01.2017


Ответы (2)


Решение

Числа в вашем примере — это просто произведения первых простых чисел, что на самом деле имеет смысл, если вы хотите минимизировать произведение при максимальном количестве факторов.

Дополнительную информацию см. в этой целочисленной последовательности:

a(n) — наименьшее число N с n различными простыми множителями.

Код

require 'prime'

n = 50000

p (1..n).find{|i| Prime.take(i+1).inject(:*) > n}
#=> 6

С n = 10000000000 :

p (1..n).find{|i| Prime.take(i+1).inject(:*) > n}
#=> 10

Объяснение

Он вычисляет произведение первых i+1 простых чисел, пока оно не станет больше n. В этом случае i является желаемым результатом.

Обратите внимание, что i всегда меньше n, поэтому поиска в диапазоне (1..n) будет более чем достаточно. find останавливает поиск, как только блок возвращает истинное значение, поэтому не имеет значения, является ли range.max n или даже Float::INFINITY.

Не очень эффективно вычислять произведение для каждого i, но решение находится так быстро, что, вероятно, не имеет значения: произведение первых k простых чисел растет быстрее, чем k!, поэтому требуется меньше O(Γ**-1(n)) шагов.

Для какого числа?

Если вы хотите узнать, для какого числа это:

p Prime.inject { |product, prime|
  new_product = prime * product
  break product if new_product > n
  new_product
}
#=> 6469693230

или просто :

p Prime.take(10).inject(:*)
#=> 6469693230
person Eric Duminil    schedule 11.01.2017
comment
Это гениально. Я думал, что знаю Enumerable. Научите меня, мастер - person iGbanam; 11.01.2017
comment
Отличное решение. Мне любопытно, как (1..n).find() работает так быстро? Оптимизирован ли он для бинарного поиска при сортировке интервала? Я предполагаю, что (1..n) не выделяет массив, поскольку здесь это заняло бы слишком много времени. Он звонит Range#bsearch? - person Coolness; 11.01.2017
comment
@Coolness: я обновил ответ. (1..n).find начинается с 1 и повторяется до тех пор, пока блок не станет истинным. Это происходит очень быстро, потому что произведение m первых простых чисел растет быстрее, чем экспоненциально. Максимальный диапазон на самом деле не имеет значения, и массив не создается. См. (1..Float::INFINITY).find{|x| x > 100} - person Eric Duminil; 12.01.2017
comment
n = Float::INFINITY будет маленькой проблемой. Обратите внимание, что вам не нужно каждый раз брать первые i+1 простых числа. Просто умножьте произведение первого i на следующее. Согласитесь, вы не сэкономите много времени. - person Cary Swoveland; 12.01.2017
comment
Я никогда не говорил о n = Float::INFINITY - person Eric Duminil; 12.01.2017
comment
@CarySwoveland: я обновил ответ. Временная сложность настолько низка, что я бы предпочел иметь красивую и читаемую строчку, чем пытаться оптимизировать для арахиса. Нет. n != range.max, но range.max можно определить как n. РЕДАКТИРОВАТЬ: Вы получили это. - person Eric Duminil; 12.01.2017
comment
Чтобы было понятнее, я бы предложил 1.step.find {...}. - person Cary Swoveland; 12.01.2017
comment
@CarySwoveland: Я никогда не видел 1.step, интересно! Я не нахожу это более ясным, хотя. - person Eric Duminil; 12.01.2017
comment
Я предпочитаю 1.step или менее привлекательную 1..Float::INFINITY 1..n, потому что последний требует, чтобы читатель спросил, почему n?. Об этом свидетельствует первый комментарий спрашивающего выше. Если бы диапазон был бесконечным, было бы очевидно, что блок будет просто повторяться, пока не найдет true. Я думаю, что это был Ruby v2.1, когда аргумент limit для Числовой#шаг стал необязательным. - person Cary Swoveland; 12.01.2017
comment
В этом случае пользователь, вероятно, спросил бы: что такое 1.step.find? :) - person Eric Duminil; 12.01.2017
comment
Я надеюсь, что это так! Тогда бы они узнали что-то новое о Руби. - person Cary Swoveland; 12.01.2017
comment
Давайте продолжим обсуждение в чате. - person Eric Duminil; 12.01.2017

Максимальное количество различных простых чисел между 2 и n равно m таким образом, что P(m) <= n < P(m+1), где P(m) = p1*p2*...*pm, pi является ith по величине простым числом. Доказательство (от противного) простое. Предположим, что q1*q2*...*qm+1 — это любая другая последовательность возрастающих простых чисел. Так как pi<= qi для i = 1...m+1, то произведение q должно превышать n.

require 'prime'

def max_number_primes(n)
  return 0 if n < 2
  t = 1
  Prime.each_with_object([]) do |p, a|
    tmp = p*t
    return a if tmp > n
    a << p
    t = tmp
  end
end

max_number_primes(50)              #=> [2, 3, 5]
max_number_primes(50_000)          #=> [2, 3, 5, 7, 11, 13] 
max_number_primes(10_000_000_000)  #=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
person Cary Swoveland    schedule 11.01.2017
comment
Я уже много раз говорил об этом: Project Euler, HackerRank и как бы они ни назывались не проблемами программирования. Если вы используете их для изучения программирования, вы делаете это неправильно. Это математические задачи. Они учат вас математике, а не программированию. Ответ на проблему Project Euler / HackerRank / и т. д. обычно должен включать доказательство или, по крайней мере, некоторые уравнения или математические аргументы. Этот ответ является хорошим примером этого. +1 - person Jörg W Mittag; 11.01.2017