1c1,2
< # This one only took 8 seconds on my machine. Wow?
---
>
> # This one took hours to compute, overnight.
28c29
< powers = [0, 0]
---
> powers = [0] * (num + 1)
32c33
< powers[factor-1] += 1
---
> powers[factor] += 1
35d35
< powers.append(0)
55c55
< n += 1
\ No newline at end of file
---
> n += 1
--
Powered by Gentoo GNU/Linux
http://linuxcrazy.com
For example, for num=28, your version creates a list 29 elements long,
but the other version creates one only 7 elements long. Also, 'the time
needed to append an item to the list is "amortized constant"' (quoted
from http://effbot.org/zone/python-list.htm).
This means that your little speed test isn't representation of what's
actually happening.
Here's a guess:
As the number you're testing gets larger, version A is creating very
big list. I'm not sure exactly how much overhead each list entry has
in python, but I guess it's at least 8 bytes: a 32-bit reference for
each list entry, and 32 bits to hold the int value (assuming a 32-bit
version of python). The solution you're looking for is a large 8 digit
number; let's say 80,000,000, for the sake of easy calculation. That
means, as you get close to the solution, you'll be trying to allocate
almost 640 Mb of memory for every number you're checking. That's going
to make the garbage collector work extremely hard. Also, depending on
how much memory your computer has free, you'll probably start hitting
virtual memory too, which will slow you down even further. Finally,
the reduce step has to process all 80,000,000 elements which is
clearly going to take a while.
Version b creates a list which is only as long as the largest prime
factor, so at worst the list size will be approx. sqrt(80,000,000),
which is approx. 8900 elements or approx. 72 Kb or memory - a much
more manageable size.
Hope that helps,
Vil.
It would probably save some time to not bother storing the zeroes in the
list at all. And it should help if you were to step through a list of
primes, rather than trying every possible int. Or at least constrain
yourself to odd numbers (after the initial case of 2).
DaveA
>DA> It would probably save some time to not bother storing the zeroes in the
>DA> list at all. And it should help if you were to step through a list of
>DA> primes, rather than trying every possible int. Or at least constrain
>DA> yourself to odd numbers (after the initial case of 2).
The first and the last save a constant factor (slightly less than 2):
def factorise(num):
"""Returns a list of prime factor powers. For example:
factorise(6) will return
[2, 2] (the powers are returned one higher than the actual value)
as in, 2^1 * 3^1 = 6."""
powers = []
factor = 2
while num > 1:
power = 0
while num % factor == 0:
power += 1
num /= factor
if power > 0:
powers.append(power+1)
factor += 1
return powers
...
return reduce(mul, powers)
or to skip the odd factors:
def factorise(num):
"""Returns a list of prime factor powers. For example:
factorise(6) will return
[2, 2] (the powers are returned one higher than the actual value)
as in, 2^1 * 3^1 = 6."""
powers = []
factor = 2
while num > 1:
power = 0
while num % factor == 0:
power += 1
num /= factor
if power > 0:
powers.append(power+1)
factor = 3 if factor == 2 else factor + 2
return powers
This can be slightly optimised by taking factor 2 out of the loop.
def factorise(num):
"""Returns a list of prime factor powers. For example:
factorise(6) will return
[2, 2] (the powers are returned one higher than the actual value)
as in, 2^1 * 3^1 = 6."""
powers = []
power = 0
while num % 2 == 0:
power += 1
num /= 2
if power > 0:
powers.append(power+1)
factor = 3
while num > 1:
power = 0
while num % factor == 0:
power += 1
num /= factor
if power > 0:
powers.append(power+1)
factor += 2
return powers
To restrict the search to primes you would have to use a
sieve of Eratosthenes or something similar.
My first attempt (with a sieve from
http://code.activestate.com/recipes/117119/) only gave a speed decrease!!
But this had the sieve recreated for every triangle number. A global
sieve that is reused at each triangle number is better. But the speed
increase relative to the odd factors only is not dramatical.
# Based upon http://code.activestate.com/recipes/117119/
D = {9: 6} # contains composite numbers
Dlist = [2, 3] # list of already generated primes
def sieve():
'''generator that yields all prime numbers'''
global D
global Dlist
for p in Dlist:
yield p
q = Dlist[-1]+2
while True:
if q in D:
p = D[q]
x = q + p
while x in D: x += p
D[x] = p
else:
Dlist.append(q)
yield q
D[q*q] = 2*q
q += 2
def factorise(num):
"""Returns a list of prime factor powers. For example:
factorise(6) will return
[2, 2] (the powers are returned one higher than the actual value)
as in, 2^1 * 3^1 = 6."""
powers = []
power = 0
for factor in sieve():
power = 0
while num % factor == 0:
power += 1
num /= factor
if power > 0:
# if you really want the factors then append((factor, power))
powers.append(power+1)
if num == 1:
break
return powers
--
Piet van Oostrum <pi...@cs.uu.nl>
URL: http://pietvanoostrum.com [PGP 8DAE142BE17999C4]
Private email: pi...@vanoostrum.org
D = {9: 6} # contains composite numbers
Dlist = [2, 3] # list of already generated primes
def sieve():
'''generator that yields all prime numbers'''
global D
global Dlist
for q in Dlist:
yield q
while True:
q += 2
p = D.pop(q, 0)
if p:
x = q + p
while x in D: x += p
D[x] = p
else:
Dlist.append(q)
D[q*q] = 2*q
yield q
for pair in Elist:
yield pair
q = pair[0] + 2
> while True:
> if q in D:
> p = D[q]
> x = q + p
> while x in D: x += p
> D[x] = p
> else:
XXX Dlist.append(q)
XXX yield q
XXX D[q*q] = 2*q
square = q * q
pair = q, square
Elist.append(pair)
yield pair
D[square] = 2 * q
> q += 2
>
> def factorise(num):
> """Returns a list of prime factor powers. For example:
> factorise(6) will return
> [2, 2] (the powers are returned one higher than the actual value)
> as in, 2^1 * 3^1 = 6."""
> powers = []
> power = 0
XXX for factor in sieve():
for factor, limit in sieve2():
> power = 0
> while num % factor == 0:
> power += 1
> num /= factor
XXX if power > 0:
if power: # good enough here, and faster
> # if you really want the factors then append((factor, power))
> powers.append(power+1)
XXX if num == 1:
XXX break
XXX return powers
if num < limit:
if num > 1:
# if you really want the factors then append((num, 1))
powers.append(2)
return powers
OK, that's a straightforward speedup, _but_:
factorize(6) == [2, 2] == factorize(10) == factorize(15)
So I am not sure exactly what you are calculating.
--Scott David Daniels
Scott....@Acm.Org
>SDD> # No need for global declarations, we alter, not replace
Yes, I know, but I find it neater to do the global declarations, if only
for documentation. And they don't affect the run time, only the compile
time.
>DA> MRAB wrote:
>>> <div class="moz-text-flowed" style="font-family: -moz-fixed">Dave Angel
>>> wrote:
>>> [snip]
>>>> It would probably save some time to not bother storing the zeroes in the
>>>> list at all. And it should help if you were to step through a list of
>>>> primes, rather than trying every possible int. Or at least constrain
>>>> yourself to odd numbers (after the initial case of 2).
>>>>
>>> Or stop looking for more factors when you've passed the square root of
>>> num. I don't know what effect there'll be on the time if you recalculate
>>> the square root when num changes (expensive calculation vs smaller
>>> search space).
>>>
>>> </div>
>>>
>DA> But if I remember the code, it stopped when the quotient is one, which is
>DA> usually sooner than the square root. And no need to precalculate the
>DA> square root.
That's right. I thought of doing the sqrt trick by testing for num <
factor, i.e."
if num < factor:
break
but found out that it is useless, as for each factor found, num is
divided by that factor as many times as possible. Therefore when the
largest prime factor of num has been found, num ends up being 1 and the
loop stops.