Эффективный алгоритм поиска общего делителя, ближайшего к некоторому значению?

У меня есть два числа, x1 и x2. Для числа y я хочу вычислить общий делитель x1 и x2 как можно ближе к y.

Есть ли эффективный алгоритм для этого?


Я считаю, что пришло время перефразировать мою проблему и быть более ясным. Дело не в целых числах... Итак, допустим, у нас есть два числа x1 и x2. Скажем, пользователь вводит число y. Я хочу найти число y', близкое к y, так что x1 % y' и x2 % y' очень малы (например, меньше, чем 0.02, но давайте назовем это число LIMIT). Другими словами, мне нужен не оптимальный алгоритм, а хорошее приближение.

Я благодарю вас всех за ваше время и усилия, это действительно любезно!


person Fatso    schedule 08.02.2012    source источник
comment
Думаю, лучше спросить в новой теме.   -  person Saeed Amiri    schedule 09.02.2012
comment
Хорошо, Саид, я сделал это: stackoverflow.com/questions/9210664/   -  person Fatso    schedule 09.02.2012


Ответы (4)


Я считаю, что для этой задачи не существует известного эффективного (полиномиального времени) алгоритма, потому что существует полиномиальное сокращение от целочисленная факторизация к этой проблеме. Поскольку не существует известного алгоритма с полиномиальным временем для целочисленной факторизации, не может быть известного алгоритма и для вашей задачи, поскольку в противном случае у нас действительно был бы алгоритм с полиномиальным временем для целочисленной факторизации.

Чтобы увидеть, как это работает, предположим, что у вас есть число n, которое вы хотите разложить на множители. Теперь, используя любой алгоритм, найдите общий делитель n и n, ближайший к n. Поскольку ни один нетривиальный делитель n не может быть больше n, это находит либо (1) наибольшее целое число, которое делит n, либо (2) число 1, если n простое. Затем вы можете разделить n на это число и повторить, чтобы получить все множители n. Поскольку n может иметь не более O(log n) множителей, это требует не более полиномиального количества итераций решателя для вашей задачи, поэтому у нас есть полиномиальное сокращение времени от целочисленной факторизации до этой задачи. Как упоминалось выше, это означает, что, по крайней мере, в открытой литературе не существует известного эффективного классического алгоритма решения этой задачи. Один может существовать, но это был бы действительно чрезвычайно важный результат.

Извините за отрицательный ответ, и надеюсь, что это поможет!

person templatetypedef    schedule 08.02.2012
comment
Общий делитель, ближайший к 0, легко: 1 (кроме n = 0) ;) Сделайте его делителем, ближайшим к n^0.4 или около того, тогда у вас будет сложная задача. - person Daniel Fischer; 08.02.2012
comment
@DanielFischer- Ах, да. Я предположил, что проблема связана с нетривиальным делителем. Я обновлю соответственно. - person templatetypedef; 08.02.2012
comment
Это не ответ на вопрос, это как сказать: для TSP нет быстрого пути, так что смотрите в монитор и ничего не пробуйте. Связь этого вопроса и целочисленной факторизации была очевидна, и я использую ее в своем ответе. Забавно, что вы получили 5 голосов за то, что ничего не сказали (возможно, для некоторых пользователей особенно важно знать, что это связано с целочисленной факторизацией! То, что я сказал за час до того, как вы, и я его используем). - person Saeed Amiri; 09.02.2012
comment
@SaeedAmiri- Это действительно ответ - вопрос ОП: существует ли эффективный алгоритм? и я предоставил формальное доказательство того, что проблема эквивалентна известной открытой проблеме. Это не говорит не пытаться; это просто говорит о том, что попытка решить эту проблему, вероятно, будет трудной, так же как попытка решить NP-полную проблему сложна. Более того, это не я краду ваш ответ; Я независимо пришел к этому заключению и формализовал его таким образом, чтобы показать, как твердость сохраняется. - person templatetypedef; 09.02.2012
comment
Нет, это не ответ. Если вы хотите получить ответ, вы можете обратиться к наиболее эффективным алгоритмам целочисленной факторизации (независимо от того, что они не являются P-решениями). Кроме того, кто сказал, что целочисленная факторизация - это NPC? Не очевидно, в NP или co-NP вы говорите, что в NPC? - person Saeed Amiri; 09.02.2012
comment
@SaeedAmiri- Мои извинения - я не хотел намекать, что это NP-полная проблема (это не так). Я использовал NP-полноту в качестве иллюстрации. Но что более важно, я сожалею, что расстроил вас. Я не хочу, чтобы между нами были военные действия - я уважаю ваш интеллект и очень впечатлен качеством ваших ответов на этом сайте. - person templatetypedef; 09.02.2012
comment
Я принял этот ответ, потому что он отвечает на этот вопрос. Имейте в виду, что я попросил алгоритм аппроксимации в отдельном вопросе: title="приближение общего делителя, ближайшего к некоторому значению"> stackoverflow.com/questions/9210664/ - person Fatso; 09.02.2012

Это эффективно, поскольку я могу это понять:

from fractions import gcd
primes=[i for i in range(2,1000) if all(i%j!=0 for j in range(2,i))] #ensure you have enough primes.. (can improve efficency here)


def f(x1,x2,y):
    _gcd=gcd(x1,x2)
    if _gcd==1:
        return 1
    factors=(i for i in range(2,_gcd+1) if _gcd%i==0) #can improve efficiency here.. e.g. only go up to root(gcd)
    r1=999999999
    r2=999999999
    for i in factors:
        r1=min(r1,y%i)
        r2=min(r2,i-y%i)
    return y-r1 if r1<=r2 else y+r2


print f(8,4,3)
print f(16,12,5)
print f(997,53,44)
print f(2300*2,2300*3,57)

"""
2
4
1
56
"""
person robert king    schedule 08.02.2012

Я думаю, вы можете сделать это с помощью жадного алгоритма, сначала найдите НОД с помощью обычных алгоритмов, назовите его d (который вычисляется в логарифмическом времени), затем найдите множители d каждый раз, разделите d на наименьший доступный множитель (создайте d') и сравните |d'-y| с |d-y| если меньше, продолжайте в том же духе (и замените d' на d), в противном случае умножьте d' на наименьший исключенный множитель и снова сравните его расстояние с y.

person Saeed Amiri    schedule 08.02.2012
comment
then find factors of d - Как вы собираетесь сделать это быстро? Пока мы факторизуем, мы должны вместо этого факторизовать меньшие числа (x1 и x2), после чего найти LCM тривиально. - person BlueRaja - Danny Pflughoeft; 08.02.2012
comment
@BlueRaja-DannyPflughoeft, может быть, я не могу понять ваш вопрос, но найти множители определенного числа не так уж сложно, просто нужно выполнить итерацию для sqrt (d), и я думаю, что в большинстве случаев d - это небольшое число, поэтому найти это фактор не сложный, также это можно сделать быстрее с помощью алгоритма, похожего на сито. - person Saeed Amiri; 08.02.2012
comment
@Saeed: Как вы можете предположить, что d - это небольшое число? Факторинг считается сложной проблемой - на этом основано публичное шифрование RSA! - person BlueRaja - Danny Pflughoeft; 08.02.2012
comment
@BlueRaja-DannyPflughoeft, если вы хотите говорить академически, да, это кажется сложным, но, во-первых, этот сайт не для научных целей, поэтому алгоритмы, которые не связаны с битовым размером, в большинстве случаев неплохие, я не думаю, что OP работает с 200-значными целыми числами, поэтому факторинг в этом случае не является сложной проблемой. Если это была академическая проблема для выпускников, она должна добавить некоторую информацию об исследованиях OP в этой области, но кажется, что это похоже на вопрос для студентов, и это не так. нужно упорно работать. ОП может определить, есть ли у него 200-значное целое число или нет. - person Saeed Amiri; 08.02.2012
comment
@BlueRaja-DannyPflughoeft, я надеюсь, что вы дадите лучший ответ, чем мой, затем скажите, что мой ответ медленный, и тогда я смогу понять ваш отрицательный голос, в настоящее время ваш отрицательный голос действительно потрясающий! - person Saeed Amiri; 08.02.2012
comment
-1 Это неэффективный алгоритм, так как перечисление всех факторов может потребовать геркулесовых усилий (например, возьмем n как составное число, образованное произведением двух 400-значных простых чисел, и это практически невозможно будет завершено в любой разумный период времени) - person templatetypedef; 08.02.2012
comment
@SaeedAmiri- я извиняюсь за то, что расстроил вас, но, пожалуйста, не кричите на меня. Я читал комментарии, и мой комментарий должен был показать, что даже после того, как вы ответили на сообщение Blue Raja, я не согласен с вашими рассуждениями. Я проголосовал за этот ответ, а не за другие, потому что он все еще имел неотрицательную оценку, и я хотел дать понять сообществу, что считаю этот ответ неверным. - person templatetypedef; 09.02.2012
comment
@templatetypedef, это неэффективный алгоритм, опять забавная вещь, я бросил вызов BlueRaja за лучшее решение два часа назад, и все же он не предложил лучшего решения. Также я призываю вас найти лучшее решение, чем мое, а затем сказать, что это не так. эффективный алгоритм, он эффективен сейчас, потому что он лучше, чем другие алгоритмы, представленные здесь. Если вы можете найти лучший ответ, добро пожаловать. - person Saeed Amiri; 09.02.2012
comment
@SaeedAmiri- Вопрос ОП заключается в том, существует ли эффективный алгоритм. Это открытая проблема. Создание неэффективного алгоритма для задачи не дает ответа на вопрос. Это лучше, чем вообще отсутствие алгоритма, и я согласен с вами в этом вопросе, но это не ответ на вопрос ОП. - person templatetypedef; 09.02.2012
comment
давайте продолжим это обсуждение в чате - person Saeed Amiri; 09.02.2012
comment
@SaeedAmiri Я проголосовал за ваш ответ, потому что считаю, что люди злоупотребляют отрицательным голосованием по этому вопросу. Для этого нет идеального алгоритма, но ваше решение снижает сложность по сравнению с простым решением грубой силы. - person The Real Baumann; 09.02.2012
comment
Саид Амири, позвольте мне сказать, что я очень ценю ваш ответ и усилия, и я проголосовал за вас за это. Спасибо! - person Fatso; 09.02.2012
comment
@Korion, спасибо, я думаю, что минусы здесь просто из-за недопонимания. - person Saeed Amiri; 09.02.2012
comment
@Korion, я хочу сказать GCD, я написал LCM, извините за эту ошибку, теперь я это вижу. (мое имя было правильным, d для GCD, но первая строка была неправильной). - person Saeed Amiri; 09.02.2012
comment
@Real: я проголосовал против, потому что его оригинальный алгоритм был на порядок медленнее, чем то, что он написал сейчас. - person BlueRaja - Danny Pflughoeft; 10.02.2012
comment
@BlueRaja - Дэнни Пфлугхофт, было очевидно, что я говорил о НОД, а не о LCM, по крайней мере, я выбрал d для НОД, а не о LCM, и это была опечатка (как я могу пригласить вас на вызов, когда мой алгоритм работает медленно?), но неважно , вы можете написать это до того, как понизите голосование, а не через 2 дня. Также теперь вы можете удалить свой отрицательный голос. - person Saeed Amiri; 10.02.2012

  1. Найдите НОД x1 и x2.
  2. Если GCD <= Y, то вернуть GCD
  3. Текущий лучший ответ – GCD, с лучшим расстоянием – GCD - y.
  4. Перебрать все числа Y +/- [0 ... лучшее расстояние]
  5. Возвращает первое целое число, кратное как x1, так и x2

Чтобы найти НОД

public int getGCD( int a, int b )
{
   return (b==0) ? a : gcd(b, a%b);
}

Чтобы найти ближайший делитель к y...

public int closestDivisor( int a, int b, int y ){
    int gcd = getGCD( a, b );
    if( gcd <= y ) return gcd;
    int best = gcd - y;
    for( int i = 0; i < best; i++ )
    {
        if( gcd % (i-y) == 0 ) return i - y;
        if( gcd % (i+y) == 0 ) return i + y;
    }
    return gcd;
}

Я считаю, что единственной доступной дополнительной оптимизацией будет факторизация gcd (возможно, с использованием сита?), Как предложил @trinithis.

person The Real Baumann    schedule 08.02.2012
comment
-1 ваш алгоритм, по сути, представляет собой простое решение грубой силы, но с дополнительными шагами. Вы просто проверяете все номера Y +- n, пока не найдете один. - person BlueRaja - Danny Pflughoeft; 08.02.2012
comment
@BlueRaja-DannyPflughoeft, если Y значительно больше, чем GCD( x1, x2 ), то это очень удобная оптимизация. - person The Real Baumann; 08.02.2012
comment
Мне бы очень хотелось увидеть обоснование того, почему я проголосовал за этот ответ. Решение работает, и нет более эффективного простого решения. Ожидание несуществующей серебряной пули не кажется достаточной причиной, чтобы отклонить мой ответ. - person The Real Baumann; 09.02.2012
comment
Просто чтобы вы знали, я не голосовал против (на самом деле я голосовал за)! На этом сайте всегда много противников. Я очень благодарен за ваши усилия. - person Fatso; 09.02.2012