patternpythonMinor
Computing a list of divisors of a number
Viewed 0 times
listnumberdivisorscomputing
Problem
I developed the following Python functions when solving a few Project Euler problems, and since I'm not that familiar with Python, I'm curious at how I could improve them.
The code consists of a data holder class, and a few functions.
Option class:
This object stores data for a combination option. It has two data members:
Get Prime Factorization:
This function calculates a number's prime factorization. It returns the factorization as a list of Options where the option's
Get Combinations:
To calculate the combinations, I use
The
```
def getCombinations(options, combine, base):
combos = [base]
chosen = [0] * len(options)
while getCo
The code consists of a data holder class, and a few functions.
Option class:
This object stores data for a combination option. It has two data members:
value: the value of the option
count: how many times this option can be chosen
class Option:
def __init__(self, value, count):
self.value = value
self.count = countGet Prime Factorization:
This function calculates a number's prime factorization. It returns the factorization as a list of Options where the option's
value is the prime and it's count is the power.def getPrimeFactorization(n):
f = []
nth = 0
while n > 1:
prime = nthPrime(nth)
if n % prime == 0:
f.append(Option(prime, 1))
n = n // prime
while n % prime == 0:
f[-1].count += 1
n = n // prime
nth += 1
return fGet Combinations:
To calculate the combinations, I use
getCombinationsIncrement to iterate through each possible combination. The index is the index of the current option, options is the list of Options, and chosen is the number of times the Option will be chosen.def getCombinationsIncrement(index, options, chosen):
if index >= len(options):
return False
else:
chosen[index] += 1
if chosen[index] > options[index].count:
chosen[index] = 0
return getCombinationsIncrement(index + 1, options, chosen)
else:
return TrueThe
options parameter is a list of options, combine is the function used to combine options, base is the base amount to combine with other values (it's similar to reduce if your familiar with JavaScript).```
def getCombinations(options, combine, base):
combos = [base]
chosen = [0] * len(options)
while getCo
Solution
You code is hard to understand. Very hard.
In Python we prefer iteration over recursion, not only does recursion limit you to a maximum depth of 1000 function calls, it also has severe performance impacts.
You should:
Merging the above together for just
After this you want to move more code into combinations, so that it's self contained in
This should be able to get you:
Finally we want to change
-
You want to create
Alternately you could try using
This can get you:
In Python we prefer iteration over recursion, not only does recursion limit you to a maximum depth of 1000 function calls, it also has severe performance impacts.
You should:
- Use
snake_casein Python notcamelCase. The only time you useCamelCaseis when creating a class.
- Start using generators. In most cases you can just replace
list.appendwith ayield.
- Change
nthPrimeto be a generator, sayprimes. As I'm not here to re-writenthPrimeyou can just useitertools.countto get an infinite generator.
- Don't duplicate logic before and during a loop. In
prime_factorization, you wrote the floor division,n //= prime, twice.
- It's simpler to use a for loop and break from it, than
whilelooping with a condition and manually taking the next item.
- You can use
collections.namedtupleto remove the need to create your own class.
Merging the above together for just
prime_factorization can get you:from collections import namedtuple
from itertools import count
Option = namedtuple('Option', 'value count')
def primes():
for i in count():
yield nthPrime(i)
def prime_factorization(n):
for prime in primes():
count = 0
while n % prime == 0:
count += 1
n //= prime
if count:
yield Option(prime, count)
if n <= 1:
breakAfter this you want to move more code into combinations, so that it's self contained in
getCombinationsIncrement, and to rename it combinations.- Change
combinationsto insteadyieldatupleofchosen.
- Create
chosenwithincombinations.
- Pass a single one dimensional list to
combinationsso that it's more usable later.
- When you finish running through all of
optionswithoutyielding, you want to stop. To do this you can use thefor ... elsesyntax and justbreak.
This should be able to get you:
def combinations(options):
chosen = [0] * len(options)
while True:
for i, option in enumerate(options):
chosen[i] += 1
if chosen[i] > option:
chosen[i] = 0
break
yield tuple(chosen)
else:
breakFinally we want to change
combinations_reduce to use combinations.- First you should change
optionsfrom a 2d lists to two 1d lists. You should want to getvaluesandcountsinto their own lists. And so you can usezipwith the argument unpacking operator,*, to expand the list to \$n\$ 2 long lists.
-
You want to create
combos off the bat, and can use enumerate to get the current index.Alternately you could try using
yield instead, and off-load the sorting to the user.- You can use
reduceto replace your innermost loop.
- You can change the input to use default variables of
operators.muland1. This simplifies the usage, as you don't need to specify as much.
This can get you:
def combinations_reduced(options, combine=mul, base=1):
values, counts = zip(*options)
combos = [base] * len(counts)
for i, chosen in enumerate(combinations(counts), 1):
for j, value in enumerate(values):
combos[i] = reduce(combine, [combos[i]] + [value] * chosen[j])
combos.sort()
return combos
def get_divisors(n):
return combinations_reduced(prime_factorization(n))Code Snippets
from collections import namedtuple
from itertools import count
Option = namedtuple('Option', 'value count')
def primes():
for i in count():
yield nthPrime(i)
def prime_factorization(n):
for prime in primes():
count = 0
while n % prime == 0:
count += 1
n //= prime
if count:
yield Option(prime, count)
if n <= 1:
breakdef combinations(options):
chosen = [0] * len(options)
while True:
for i, option in enumerate(options):
chosen[i] += 1
if chosen[i] > option:
chosen[i] = 0
break
yield tuple(chosen)
else:
breakdef combinations_reduced(options, combine=mul, base=1):
values, counts = zip(*options)
combos = [base] * len(counts)
for i, chosen in enumerate(combinations(counts), 1):
for j, value in enumerate(values):
combos[i] = reduce(combine, [combos[i]] + [value] * chosen[j])
combos.sort()
return combos
def get_divisors(n):
return combinations_reduced(prime_factorization(n))Context
StackExchange Code Review Q#150123, answer score: 2
Revisions (0)
No revisions yet.