tags:

views:

319

answers:

10

I am currently using the following code but its very slow for large numbers



        static int divisor(int number)
        {
            int i;
            for (i = number / 2; i >= 1; i--)
            {
                if (number % i == 0)
                {
                    break;
                }
            }
            return i;
        }
+13  A: 

First thought -yYou can find the smallest divisor d (not equal to 1 of course), then N/d will be the largest divisor you're looking for.

For example if N is divisible by 3 then you'll need 2 iterations to find the answer - in your case it would be about N/6 iterations.

Edit: To further improve your algorithm you can iterate through odd numbers only (after checking if you number is even) or, even better, if you have the list of primes pre-calculated then you can iterate through them only because smallest divisor is obviously is a prime number.

Vladimir
This has the added advantage of checking the "most likely" divisors first. (i.e. 2 is "more likely" to be a divisor of an arbitrary number than say 2189)
Kragen
@Vladimir: this has a hidden claim that small divisors are more dense (on average, for ALL numbers), i.e. it is faster to find a divisor starting from 2 upwards than starting from N/2 downwards. While this seems intuitively reasonable, do you have some mathematics to support it?
davka
And actually, this can even be further improved if there is a function that pre-calculates a reasonable number of primes (or just make a table of primes), and finding which of those divides out the number instead of iterating through through all the odd numbers.
supercheetah
@supercheetah, yes I've told about that as well in edit
Vladimir
@davka, Roughly it can be proved so: let p is a minimal divisor of N, then number of iterations searching from 1 is about p/2 (when we check only odd numbers), searching from N/2 number is N/2-N/p. The case when 1st method is worse mean that p/2 > N/2-N/p which equivalent to p*p/(p-2) > n - and that can be possible only if n=p, that is when n is a prime number
Vladimir
@Vladimir: that's does it, thanks. one tiny remark - you need also to divide the second option in 2 i.e. 1/2*(N/2-N/p), but the result is the same basically
davka
@Vladimir +1, your answer is perfect and it also proves that common sense is little rare to find :)
Akash Kava
The easiest way to think of the fact that small divisors are more dense is that if two numbers (A,B) multiply to give the target number (N) then if we assume A>B then we know that the smallest possible A and the largest possible B is sqrt(N). Since for each A there is a B that is unique (ignoring A=B=sqrt(N)) so we have the same number of possibilities for A and B. Clearly though the range 1 to sqrt(N) is smaller than sqrt(N) to N (by a factor of sqrt(N)-1) so you are better off searching in the lower range than the upper.
Chris
+1  A: 

In order to limit your search space, you should start at 2 and work up to the square root of the number. There are far more numbers (in a finite search space) divisible by 2 than by 27 so you're more likely to get a low divisor than a high one, statistically speaking.

You'll find a big difference when using the square root, rather than half the value, when you processing (for example) 1,000,000. The difference is between a search space of 500,000 for your method and 1,000 for the square root method is considerable.

Another advantage is to halve the search space right at the front by discounting multiples of two. Then, when you have your lowest divisor, the highest one is simply the number divided by that.

Pseudocode:

if n % 2 == 0:              # Halve search space straight up.
    print n / 2
else:
    i = 3                   # Start at 3.
    while i * i <= n:       # Or use i <= sqrt(n), provided sqrt is calc'ed once
        if n % i  == 0:
            print n / i     # If multiple, get opposite number, print and stop
            break
        i = i + 2           # Only need to process odd numbers
paxdiablo
Suppose the number was 10^9, wouldn't my method find its largest divisor faster i.e 5*(10^8) ?
Ronzii
@Ronzi. The 10^9 number would be caught immediately by this code since it's divisible by two. I would return 10^9/2 (5x10^8) in the second statement. That's why you start low, not at n/2. The likelihood of finding factors low is much better.
paxdiablo
Good Advice. I was able to find it really quickly!
Ronzii
+3  A: 

Don't know if it's the optimal solution but you'd probably be better starting at 2 then going upwards such as:

  static int divisor(int number)
    {
        int i;
        for (i = 2; i <sqrt(number); i++)
        {
            if (number % i == 0)
            {
                break;
            }
        }
        return number/i;
    }

EDIT

to get it to work with primes as well:

 static int divisor(int number)
    {
        int i;
        for (i = 2; i <=sqrt(number); i++)
        {
            if (number % i == 0)
            {
                return number/i;
            }
        }
        return 1;
    }
Bunnit
@Bunnit: Above code will not produce correct results
bjskishore123
Don't be obtuse, @bjsk, _specify_ under what conditions it doesn't work.
paxdiablo
@bjskishore123:Yeah the return value is wrong if the number is a prime but this could easily be fixed by checking til sqrt(number)+1 then checking for that value when returning, or returning where the break is and outside the for(if it is a prime). I just wrote it as a quick example.
Bunnit
@paxdiablo: For prime numbers it doesn't work.Lets say we take number 31Above loop runs from 2 to 5returns 31/5 = 6, which is wrong.
bjskishore123
@Bunnit: could you let us know what change you made to make it work for prime numbers ?
bjskishore123
Instead this could be used to find the lowest divisor (reducing the traversal of loop to sqrt(n)). And the number itself could be divided by the lowest divisor.
Ronzii
+1  A: 

Optimization: An odd number can't have even number as largest divisor. Use this filter on number early. So if odd number is given.

  • First do division with 2.

  • Then decrement i by 2 everytime in loop

This is will improve speed for odd numbers.

bjskishore123
Also even numbers' largest real divisor is the half of themselves.
Calmarius
+3  A: 

A huge optimization (not sure if it's completely optimal - you'd have to ask a mathematician for that) is to search upwards using only prime numbers. As Vladimir and Bunnit said, it is better to search upwards, because you'll find it to be much faster. Then, return the inverse (number / i). However, if you've already tried 2 and come up dry, there is no point in trying 4 or 6. Similarly, if you've tried 3, there's no point in trying 6 or 9.

So, if time of running is a big concern, you could have a list of the first 100 primes hard coded in your program. Test each of them. If you don't find an answer by then, then you could just increment by 2 (skipping even numbers).

Smashery
A: 

You've basically hit the "factorization of large numbers" problem, which is the basis of today's Public Key encryption and is known (or hoped) to be a computationally hard problem. I indeed hope that you will not find a much better solution, otherwise the whole security infrastructure of the world will collapse... :)

davka
Yes, the OP is dealing with the slowness of factoring large numbers, but *not* on the scale anywhere near equivalent to factoring the numbers used in public key cryptographic systems such as RSA and DSA.
mctylr
+2  A: 

One of the industry standard methods for finding factors of large numbers is the Quadratic Sieve algorithm.

Have a read of this:

http://en.wikipedia.org/wiki/Quadratic_sieve

P.S. you should also consider how big your numbers are. Different factorisation algorithms tend to perform well for a certain size, and not for others. As noted in the QS wiki article, this method is generally the fastest for numbers less than about 100 decimal digits.

Given that his input is specified as `int`, that is entirely overkill, and not optimal for such small numbers.
mctylr
good point, I had missed that he's using ints
A: 

Have a look here :- http://programmingpraxis.com/contents/themes/#Prime%20Numbers

Programming Praxis regularly sets programming challenges for people to solve for a bit of fun on a Friday lunchtime. This particular problem crops up a lot and there are several well known algorithms for solving it illustrated with code.

Andrew
A: 

Some additional optimizations:

1.  If even then divisable by 2.
2.  If the sum of the digits is divisable by 3, then number is divisble by 3 (ie 78 = 7 + 8 = 15 = 1 + 5 = 6 which is divisable by 3)
3.  If it ends in 0 or 5 it is divisable by 5

Gordon.

Schmoo
A: 

The best algorithm will depend on how huge your numbers will really be.

This problem is basically as hard as factoring integers - if have some factoring algorithm, it will be rather easy to use it to construct the greatest non-trivial divisor. Again, which of all the known factoring algorithms you employ for a number should depend on its 'hugeness', as for different scales these fancy algorithms will likely have different performances.

You will probably find several (maybe different) answers to your question in good books on cryptography, algorithmics and computational number theory.

Again, depending on your scale, it might even be an option to simple precompute a large list a prime numbers, save it, and then given an input number search within this list for the smallest divisor - which will immediately have the greatest divisor pop up, too.