10

I have two numbers, x1 and x2. For a number y, I want to calculate the common divisor of x1 and x2 as close as possible to y.

Is there an efficient algorithm for this?


I believe it's time to rephrase my problem and be more clear. This is not about integers... So, say we have two numbers x1 and x2. Say, the user inputs a number y. What I want to find, is a number y' close to y so that x1 % y' and x2 % y' are very small (smaller than 0.02, for example, but lets call this number LIMIT). In other words, I don't need an optimal algorithm, but a good approximation.

I thank you all for your time and effort, that's really kind!

Fatso
  • 1,278
  • 16
  • 46

4 Answers4

7

I believe that there is no known efficient (polynomial-time) algorithm for this problem because there is a polynomial-time reduction from integer factorization to this problem. Since there is no known polynomial-time algorithm for integer factorization, there cannot be a known algortihm for your problem either, since otherwise we would indeed have a polynomial-time algorithm for integer factorization.

To see how this works, suppose you have a number n that you'd like to factor. Now, using whatever algorithm you'd like, find the common factor of n and n closest to √n. Since no nontrivial divisor of n can be greater than √n, this finds either (1) the largest integer that divides n, or (2) the number 1 if n is prime. You can then divide n by this number and repeat to produce all the factors of n. Since n can have at most O(log n) factors, this requires at most polynomially many iterations of the solver for your problem, so we have a polynomial-time reduction from integer factorization to this problem. As mentioned above, this means that, at least in the open literature, there is no known efficient classical algorithm for solving this problem. One might exist, but it would be a really hugely important result.

Sorry for the negative answer, and hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 1
    The common divisor closest to 0 is easy: 1 (unless `n = 0`) ;) Make it the divisor closest to `n^0.4` or so, then you have a generally hard problem. – Daniel Fischer Feb 08 '12 at 19:38
  • @DanielFischer- Ah, yes. I assumed that the problem meant "nontrivial divisor." I'll update accordingly. – templatetypedef Feb 08 '12 at 19:40
  • 2
    This isn't answer to question, it's like say: There isn't any fast way for TSP, so watch your monitor and do not try anything. Relation of this question and integer factorization was obvious, and I use it in my answer. It's funny that you got 5 upvote for saying nothing (may be is special thing for some users to know it's related to integer factorization! What I said one hour before you and I use it). – Saeed Amiri Feb 08 '12 at 20:55
  • @SaeedAmiri- This is indeed an answer - the OP's question is "is there an efficient algorithm?" and I provided a formal proof that the problem is equivalent to a known open problem. It doesn't say not to try; it just says that attempting to solve this problem is likely to be difficult, just as attempting to solve an NP-complete problem is difficult. Moreover, this is not me stealing your answer; I independently arrived at this conclusion and formalized it in a way that shows how the hardness carries over. – templatetypedef Feb 08 '12 at 20:59
  • 2
    No this isn't answer, If you want to get an answer, you can refer to most efficient algorithms to integer factorization (no matter they aren't P solutions), Also who said integer factorization is NPC? It's not obvious is in NP or co-NP you say it's in NPC? – Saeed Amiri Feb 08 '12 at 21:07
  • @SaeedAmiri- My apologies - I did not mean to insinuate that this is an NP-complete problem (it's not). I was using NP-completeness as an illustration. More importantly, though, I am sorry for upsetting you. I don't want there to be any hostilities between us - I respect your intelligence and am very impressed with the quality of your answers on this site. – templatetypedef Feb 08 '12 at 21:10
  • I accepted this answer because it answers this question. Keep in mind that I asked for an approximation algorithm in a separate question : http://stackoverflow.com/questions/9210664/approximation-of-a-common-divisor-closest-to-some-value – Fatso Feb 09 '12 at 18:02
2

I think you can do it by greedy algorithm, first find GCD by common algorithms name it d (which is computable in logarithmic time) then find factors of d each time divide d to smallest available factor (create d'), and compare |d'-y| with |d-y| if is smaller continue in this way (and replace d' with d), else, multiply d' with smallest eliminated factor, and again compare its distance to y.

Saeed Amiri
  • 22,252
  • 5
  • 45
  • 83
  • 2
    `then find factors of d` - How do you expect to do that quickly? As long as we're factoring, we should factor the smaller numbers (`x1` and `x2`) instead, after which finding the LCM is trivial. – BlueRaja - Danny Pflughoeft Feb 08 '12 at 18:07
  • @BlueRaja-DannyPflughoeft,May be I can't understand your question, but finding factors of specific number is not so hard, it just needs iteration to sqrt(d), and I think in most cases `d` is small number, so finding this factor is not hard, Also it can be done faster with sieve like algorithm. – Saeed Amiri Feb 08 '12 at 18:11
  • 4
    @Saeed: How can you assume `d` is a small number? Factoring is considered a difficult problem - RSA public encryption relies on that fact! – BlueRaja - Danny Pflughoeft Feb 08 '12 at 18:15
  • @BlueRaja-DannyPflughoeft, If you want talk academically yes It seems to be hard, but first of all, this site is not for scientific purpose, so algorithms wich is not related to bit size is not bad in most cases, I don't think OP works with 200 digit integers, so doing factoring is not hard problem in this case, If it was graduate academic problem, It should add some information about OPs research in this field, but seems it's like undergrad question, and it doesn't need to hard work. OP can determines that he has 200 digit integer or not. – Saeed Amiri Feb 08 '12 at 18:22
  • @BlueRaja-DannyPflughoeft, I hope you provide better answer than mine, then say me my answer is slow and then I can understand your downvote, currently your downvote is really amazing! – Saeed Amiri Feb 08 '12 at 18:30
  • -1 This isn't an efficient algorithm, as listing off all the factors can require a herculean effort (take n to be a composite number formed by the product of two 400-digit primes, for example, and there's pretty much no way this is going to finish in any reasonable time period) – templatetypedef Feb 08 '12 at 19:43
  • @SaeedAmiri- I apologize for upsetting you, but please do not yell at me. I did read the comments, and my comment was to indicate that even after you had responded to Blue Raja's post that I disagreed with your reasoning. I downvoted this answer and not others because it still had nonnegative score, and I wanted to make clear to the community that I thought this answer was incorrect. – templatetypedef Feb 08 '12 at 20:57
  • @templatetypedef, "This isn't an efficient algorithm" again funny thing, I challenged BlueRaja for better solution two hours ago, and still he didn't offered better solution, Also I challenge you to find better solution than mine then say "This isn't an efficient algorithm" it's efficient now, because it's better than other algorithm provided here, If you can find better answer you well come. – Saeed Amiri Feb 08 '12 at 21:00
  • @SaeedAmiri- The OP's question is whether an efficient algorithm exists. This is an open problem. Producing an inefficient algorithm for the problem does not answer the question. It is better than no algorithm at all, and I agree with you on this point, but it's not an answer to the OP's question. – templatetypedef Feb 08 '12 at 21:02
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/7475/discussion-between-saeed-amiri-and-templatetypedef) – Saeed Amiri Feb 08 '12 at 21:08
  • 1
    @SaeedAmiri I upvoted your answer because I believe that people are abusing the down voting on this question. There is no perfect algorithm for this but your solution reduces the complexity over the simple brute force solution. – The Real Baumann Feb 08 '12 at 22:07
  • Saeed Amiri, let me just say I really appreciate your answer and effort, and I upvoted you for that. Thanks! – Fatso Feb 09 '12 at 07:41
  • @Korion, Thanks, I think downvotes here Is just from misunderstandings. – Saeed Amiri Feb 09 '12 at 08:37
  • @Korion, I want to say GCD, I wrote LCM, sorry for this mistake, now I see that. (my naming was right, `d` for GCD but first line was incorrect). – Saeed Amiri Feb 09 '12 at 09:03
  • @Real: I downvoted because his original algorithm was an order of magnitude slower than what he currently has written – BlueRaja - Danny Pflughoeft Feb 09 '12 at 22:46
  • @BlueRaja - Danny Pflughoeft, It was obvious I was talking about GCD not LCM, at least I select `d` for GCD not LCM and this was typo (how I can invite you for a challenge when my algorithm is slow?), but no matter, you can write this before downvoting not after 2 days. Also now you can remove your downvote. – Saeed Amiri Feb 10 '12 at 08:14
2

This is efficient as I can get it:

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
"""
Rusty Rob
  • 16,489
  • 8
  • 100
  • 116
0
  1. Find the GCD of x1 and x2.
  2. If GCD <= Y then return GCD
  3. Current best answer is GCD, with a best distance of GCD - y.
  4. Iterate through all numbers Y +/- [0 ... best distance]
  5. Return the first integer that is a multiple of both x1 and x2

To find the GCD

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

To find closest divisor to 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;
}

I believe the only additional optimization available would be to factor the gcd (perhaps using a sieve?) as @trinithis suggested.

The Real Baumann
  • 1,941
  • 1
  • 14
  • 20
  • 1
    -1 your algorithm is essentially the simple brute-force solution, but with extra steps. You are just checking all numbers `Y +- n` until you find one. – BlueRaja - Danny Pflughoeft Feb 08 '12 at 18:12
  • @BlueRaja-DannyPflughoeft if `Y` is significantly greater than `GCD( x1, x2 )` then it's a very handy optimization. – The Real Baumann Feb 08 '12 at 18:15
  • I'd sure like to see reasoning as to why I'm getting down voted for this answer. The solution works and there's not a more efficient simple solution. Expecting a silver bullet that doesn't exist doesn't seem like a good enough reason to reject my answer. – The Real Baumann Feb 08 '12 at 20:19
  • 1
    Just so you know, I didn't vote down (I actually up-voted)! There are always lots of down-voters on this website. I'm very grateful for you effort. – Fatso Feb 09 '12 at 09:22