17

I'm trying to find the largest prime factor of the number x, Python gives me the error that the range is too large. I've tried using x range but I get an OverflowError: Python int too large to convert to C long

x = 600851475143
maxPrime = 0


for i in range(x):
    isItPrime = True
    if (x%i == 0):
        for prime in range(2,i-1):
            if (i%prime == 0):
                isItPrime = False
        if (isItPrime == True):

            if (i > maxPrime):
                maxPrime = i;

print maxPrime
Rangi Lin
  • 9,303
  • 6
  • 45
  • 71
Alberto Does
  • 189
  • 1
  • 3
  • 14
  • You need to go about your algorithm in another manner. – Austin Henley Mar 22 '12 at 04:59
  • 1
    You could also use a `while i < 600851475143` and don't forget to increment `i` – wim Mar 22 '12 at 05:08
  • If you were not getting a range error, you would have gotten a `ZeroDivisionError: integer division or modulo by zero`. It looks like you are using your `range` to mod your value, and `range` starts from 0, so you must be doing `x % 0` in the first iteration. On linux, I get `MemoryError` with your code. – hughdbrown Mar 13 '13 at 17:46

6 Answers6

28

In old (2.x) versions of Python, xrange can only handle Python 2.x ints, which are bound by the native long integer size of your platform. Additionally, range allocates a list with all numbers beforehand on Python 2.x, and is therefore unsuitable for large arguments.

You can either switch to 3.x (recommended), or a platform where long int (in C) is 64 bit long, or use the following drop-in:

import itertools
range = lambda stop: iter(itertools.count().next, stop)

Equivalently, in a plain form:

def range(stop):
   i = 0
   while i < stop:
       yield i
       i += 1
phihag
  • 278,196
  • 72
  • 453
  • 469
  • I was looking for an iterator version of `range()` but couldn't find a pre-made one in the docs. Simple enough ;) – Blender Mar 22 '12 at 05:01
  • 3
    On Windows, even 64-bit Python 2.x will probably have this problem, see [What is the bit size of long on 64-bit Windows?](http://stackoverflow.com/questions/384502/what-is-the-bit-size-of-long-on-64-bit-windows) – agf Mar 22 '12 at 05:02
  • you just remade `itertools.count` as your answer, but using `itertools.count` would be much faster :) – Serdalis Mar 22 '12 at 05:07
  • 1
    @Serdalis Unless I'm mistaken, [`itertools.count`](http://docs.python.org/dev/library/itertools.html#itertools.count) has no `stop` parameter. This is a very simplistic implementation of Python 3.x's `range`. – phihag Mar 22 '12 at 05:09
  • @phihag, my apologies, I misread start as stop for some crazy reason, you are completely correct in your answer. – Serdalis Mar 22 '12 at 05:10
  • 1
    @Serdalis You could still use `count`: `iter(itertools.count().next, stop)`. – agf Mar 22 '12 at 05:19
  • @agf Are you sure about that? I don't think `itertools.count()` is callable, and from `help(iter)` (in 2.7): `iter(callable, sentinel) -> iterator` – phihag Mar 22 '12 at 05:26
  • @phihag Look closer, the callable is `itertools.count().next` -- the `next` method of an instance of `count`. – agf Mar 22 '12 at 05:29
  • Oops, sorry, missed that. Updated the answer. – phihag Mar 22 '12 at 05:35
  • @phihag if i have minimum range say range(3,longint). how can i achieve it – sundar nataraj Jun 11 '14 at 12:29
  • @sundarnatarajСундар Simply pass in the initial value as the first and only argument to `itertools.count` – phihag Jun 11 '14 at 16:36
  • 1
    If you want all of the arguments (e.g. step) ```import itertools; xrange = lambda start, stop, step: iter(itertools.count(start, step).next, stop)``` – Grazfather Apr 08 '15 at 07:15
6

This is what I would do:

def prime_factors(x):
    factors = []
    while x % 2 == 0:
        factors.append(2)
        x /= 2
    i = 3
    while i * i <= x:
        while x % i == 0:
            x /= i
            factors.append(i)
        i += 2
    if x > 1:
        factors.append(x)
    return factors

>>> prime_factors(600851475143)
[71, 839, 1471, 6857]

It's pretty fast and I think it's right. It's pretty simple to take the max of the factors found.


2017-11-08

Returning to this 5 years later, I would use yield and yield from plus faster counting over the prime range:

def prime_factors(x):
    def diver(x, i):
        j = 0
        while x % i == 0:
            x //= i
            j += 1
        return x, [i] * j
    for i in [2, 3]:
        x, vals = diver(x, i)
        yield from vals
    i = 5
    d = {5: 2, 1: 4}
    while i * i <= x:
        x, vals = diver(x, i)
        yield from vals
        i += d[i % 6]
    if x > 1:
        yield x

list(prime_factors(600851475143))

The dict {5: 2, 1: 4} uses the fact that you don't have to look at all odd numbers. Above 3, all numbers x % 6 == 3 are multiples of 3, so you need to look at only x % 6 == 1 and x % 6 == 5, and you can hop between these by alternately adding 2 and 4, starting from 5.

hughdbrown
  • 47,733
  • 20
  • 85
  • 108
  • EulerProject 12 has shed some lights.Thank you. – zionpi May 08 '13 at 05:06
  • can u explain how u did the code.i mean this is really fast how u reached to this solution – sundar nataraj Jun 11 '14 at 12:23
  • @sundar: (1) you don't have to check every number -- only those that are potentially prime. No even number after 2 is prime, so skip those. Then you can count up from 3 by 2's. (2) You only have to go to the square root of the number. (3) You only have to iterate until you exceed the original value divided by all the factors encountered. – hughdbrown Jun 17 '14 at 22:09
4

The accepted answer suggests a drop-in replacement for xrange, but only covers one case. Here is a more general drop-in replacement.

def custom_range(start=0,stop=None,step=1):
    '''xrange in python 2.7 fails on numbers larger than C longs.
    we write a custom version'''
    if stop is None:
        #handle single argument case. ugly...
        stop = start
        start = 0
    i = start
    while i < stop:
        yield i
        i += step

xrange=custom_range
bgschiller
  • 2,087
  • 1
  • 16
  • 30
1

I would definitely stick with xrange since creating a list between 0 and what looks like a number rivaled by infinity would be taxing for memory. xrange will generate only the numbers when asked. For the number too large problem, you might want to try a "long". This can be achieved by writing a L on the end of the number. I made my own version to test it out. I put in a small sleep as to not destroy my computer into virtually a while(1) loop. I was also impatient to see the program come to a complete end, so I put in print statements

from time import sleep

x = 600851475143L
maxPrime = 0

for i in xrange(1,x):
    isItPrime = True
    if (x%i) == 0:
        for prime in xrange(2,i-1):
            if (i%prime) == 0:
                isItPrime = False
                break
        if isItPrime:
            maxPrime = i
            print "Found a prime: "+str(i)
    sleep(0.0000001)


print maxPrime

Hope this helps!

EDIT: I also did a few more edits to yield this version. It is fairly efficient and I checked quite a few numbers this program provides (it seems to check out so far):

from time import sleep

x = 600851475143L

primes = []

for i in xrange(2,x):
    isItPrime = True
    for prime in primes:
        if (i%prime) == 0:
            isItPrime = False
            break
    if isItPrime:
        primes.append(i)
        print "Found a prime: "+str(i)
    sleep(0.0000001)


print primes[-1]
Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
jakebird451
  • 2,288
  • 4
  • 30
  • 45
  • 2
    This _doesn't work_ on Python 2.x 32-bit (or even 64-bit on Windows). It is _no different_ than the version in the question. The problem is with the C long, not the Python long. – agf Mar 22 '12 at 05:24
  • To quote @agf: `Read his question more closely`. xrange is limited by the platform's `long int` size. – phihag Mar 22 '12 at 05:25
0

Very uneficient code, that's not the best way of getting dividers, I've done it with a for and range, but I can't execute it because of the long variables, so I decided to implement it with a for using a while, and increment a counter by myself.

For 32 bit int like 13195:

# The prime factors of 13195 are 5, 7, 13 and 29.
# What is the largest prime factor of the number 600851475143 ?

i = 13195
for j in xrange(2, i, 1):
    if i%j == 0:
        i = i/j
        print j

Good way for longer numbers:

# The prime factors of 13195 are 5, 7, 13 and 29.
# What is the largest prime factor of the number 600851475143 ?

i = 600851475143
j = 2

while i >= j:
    if i%j == 0:
        i = i/j
        print j
    j = j+1

The last prime is the last printed value.

Roland Smith
  • 42,427
  • 3
  • 64
  • 94
0

Another implementation for python 2 range():

import itertools
import operator

def range(*args):
    """Replace range() builtin with an iterator version."""
    if len(args) == 0:
        raise TypeError('range() expected 1 arguments, got 0')
    start, stop, step = 0, args[0], 1
    if len(args) == 2: start, stop = args
    if len(args) == 3: start, stop, step = args
    if step == 0:
        raise ValueError('range() arg 3 must not be zero')
    iters = itertools.count(start, step)
    pred = operator.__ge__ if step > 0 else operator.__le__
    for n in iters:
        if pred(n, stop): break
        yield n
mja
  • 1,273
  • 1
  • 20
  • 22