简体   繁体   中英

Product of prime factors of a number, less than that number

First of all, I apologise for the title, I did not know how to put my problem in words. Well, here it is:

For an integer a greater than 1, let F be a sorted list of prime factors of a . I need to find all tuples c (filled with whole numbers), such that length of each tuple is equal to the size of F and (F[0] ** c[0]) * (F[1] ** c[1]) * (...) < a . I should add that I write in Python.

Example:

a = 60
F = [2,3,5]

# expected result:

C = {(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 2, 0),
(0, 2, 1), (0, 3, 0), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1),
(1, 2, 0), (1, 3, 0), (2, 0, 0), (2, 0, 1), (2, 1, 0), (2, 2, 0), (3, 0, 0),
(3, 0, 1), (3, 1, 0), (4, 0, 0), (4, 1, 0), (5, 0, 0)}

I generated this result using itertools.product() , specifically:

m = math.floor(round(math.log(a, min(F)), 12))
for i in itertools.product(range(m + 1), repeat=len(F)):
    if math.prod([F[j] ** i[j] for j in range(len(F))]) < a: print(i)

I think it works but it's inefficient. For example number 5 appears only in one tuple, but was checked many more times? Is there any way to make it faster? I would use multiple while loops (with break statements) but since I don't know what is the length of F , I don't think that is possible.

You base all your range limits on just min(F) . Let's customize each to the log(a, factor) to reduce the cases:

from math import ceil, log, prod
from itertools import product

a = 60
F = [2, 3, 5]

ranges = [range(0, ceil(log(a, factor))) for factor in F]

C = []

for powers in product(*ranges):
    if prod(F[i] ** power for i, power in enumerate(powers)) < a:
        C.append(powers)

print(C)

By my measure, your code generates 216 test cases to come up with 25 results, but the above code only generates 1/3 of those test cases.

You could iterate over all the "valid" tuples with a generator, like so:

def exponent_tuples(prime_factors, limit):
    def next_tuple(t):
        n = math.prod(f ** tt for f, tt in zip(prime_factors, t))
        for idx, (f, tt) in enumerate(zip(prime_factors, t)):
            n *= f
            if n < limit:
                return (0,) * idx + (tt + 1,) + t[idx + 1 :]
            n //= f**(tt+1)
        return None

    t = (0,) * len(prime_factors)
    while t is not None:
        yield t
        t = next_tuple(t)


for t in exponent_tuples([2, 3, 5], 60):
    print(t)

The idea here is to basically increment the tuple entries like digits of a number and have the respective digit roll over to zero and carry the 1 whenever you reach the defined limit.

I'm pretty sure this does exactly what you want, except for maybe the order in which it yields the tuples (can be adjusted by modifying the next_tuple function)

EDIT: Simplified the code a bit

The almost cooked proposition would go like this (shell execution)

>>> max_exponents(42,[2,3,7])
[5, 3, 1]
>>> #pick 2
>>> max_exponents(42//2**2,[3,7])
[2, 1]
>>> #pick 1
>>> max_exponents(42//(2**2*3**1),[7])
[0]

I'm almost done. This will adapt to any number of factors !

Somehow your proposition reduces to this (more readable form?)

import math as m 
import pprint

a = 60
prime_factors = [2,3,5]

exponents =list(map(lambda x:m.floor(m.log(a,x)),prime_factors))
rez = []
for i in range(exponents[0]+1):
    for j in range(exponents[1]+1):
        for k in range(exponents[2]+1):
            if 2**i*3**j*5**k <= a:
                rez.append((i,j,k))
pprint.pprint(rez)

and you would like to know wether there's a way to make if faster (with less tests). So we're no more on the implementation side, but more on the conception (algorithm) side?

For example, once the first exponent c[0] has been chosen, the next ones should be selected amongst the one fitting in a//(2**c[a]) as the other answerer proposed i guess

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM