MM们
找到数字所有因素的最快方法约束——不要使用除数学之外的任何外部库测试了4种方法审判部门(提问者@HasnainAli 发布的代码)又名审判Eratosthenes Sieve(来自@MonsieurGalois 帖子)又名 Sieve素因数分解的灵感来自aka FactorizePrimes based on Wheel Factorization 灵感来自Wheel Factorization aka Wheel结果结果是相对于试分的,即(试分时间)÷(其他方法时间)使用 timeit 的 @Davakar使用Benchit 的基准测试N trial sieve prime_fac wheel_fac 1 1.0 1.070048 1.129752 1.1046192 1.0 1.438679 3.201589 1.1192844 1.0 1.492564 2.749838 1.1761498 1.0 1.595604 3.164555 1.29055416 1.0 1.707575 2.917286 1.17285132 1.0 2.051244 3.070331 1.26299864 1.0 1.982820 2.701439 1.073524128 1.0 2.188541 2.776955 1.098292256 1.0 2.086762 2.442863 0.945812512 1.0 2.365761 2.446502 0.9149361024 1.0 2.516539 2.076006 0.7770482048 1.0 2.518634 1.878156 0.6909004096 1.0 2.546800 1.585665 0.5733528192 1.0 2.623528 1.351017 0.48452116384 1.0 2.642640 1.117743 0.39543732768 1.0 2.796339 0.920231 0.32726465536 1.0 2.757787 0.725866 0.258145131072 1.0 2.790135 0.529174 0.189576262144 1.0 2.676348 0.374986 0.148726524288 1.0 2.877928 0.269510 0.1072371048576 1.0 2.522501 0.189929 0.0802332097152 1.0 3.142147 0.125797 0.0531574194304 1.0 2.673095 0.105293 0.0457988388608 1.0 2.675686 0.075033 0.03010516777216 1.0 2.508037 0.057209 0.02276033554432 1.0 2.491193 0.038634 0.01544067108864 1.0 2.485025 0.029142 0.011826134217728 1.0 2.493403 0.021297 0.008597268435456 1.0 2.492891 0.015538 0.006098536870912 1.0 2.448088 0.011308 0.0045391073741824 1.0 1.512157 0.005103 0.002075结论:筛分法总是比试分法慢(即比率列> 1)试用师最快达到 n ~256轮分解法整体最快(即481X试分法为n = 2**30即1/0.002075~481)代码方式一:原帖import mathdef trial(n): " Factors by trial division " factors = set() for i in range(2, int(math.sqrt(n) + 1)): if n % i == 0: factors.update([i, n // i]) return factors方法二——筛法(@MonsieurGalois post)def factors_sieve(number): " Using primes in trial division " # Find primes up to sqrt(n) n=int(number**.5)+1 era =[1] * n primes=[] for p in range(2, n): if era[p]: primes.append(p) for i in range(p*p, n, p): era[i] = False # Trial division using primes divisors=[] x=number for i in primes: while x%i==0: x//=i divisors.append(i) if x!=1: divisors.append(x) return divisors方法三——基于质因数分解求除数灵感来自def generateDivisors(curIndex, curDivisor, arr): " Yields the next factor based upon prime exponent " if (curIndex == len(arr)): yield curDivisor return for i in range(arr[curIndex][0] + 1): yield from generateDivisors(curIndex + 1, curDivisor, arr) curDivisor *= arr[curIndex][1]def prime_factorization(n): " Generator for factors of n " # To store the prime factors along # with their highest power arr = [] # Finding prime factorization of n i = 2 while(i * i <= n): if (n % i == 0): count = 0 while (n % i == 0): n //= i count += 1 # For every prime factor we are storing # count of it's occurenceand itself. arr.append([count, i]) i += 2 if i % 2 else 1 # If n is prime if (n > 1): arr.append([1, n]) curIndex = 0 curDivisor = 1 # Generate all the divisors yield from generateDivisors(curIndex, curDivisor, arr) 方法四——轮式分解def wheel_factorization(n): " Factors of n based upon getting primes for trial division based upon wheel factorization " # Init to 1 and number result = {1, n} # set up prime generator primes = prime_generator() # Get next prime i = next(primes) while(i * i <= n): if (n % i == 0): result.add(i) while (n % i == 0): n //= i result.add(n) i = next(primes) # use next prime return resultdef prime_generator(): " Generator for primes using trial division and wheel method " yield 2; yield 3; yield 5; yield 7; def next_seq(r): " next in the equence of primes " f = next(r) yield f r = (n for n in r if n % f) # Trial division yield from next_seq(r) def wheel(): " cycles through numbers in wheel whl " whl = [2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10] while whl: for element in whl: yield element def wheel_accumulate(n, gen): " accumulate wheel numbers " yield n total = n for element in gen: total += element yield total for p in next_seq(wheel_accumulate(11, wheel())): yield p测试代码from timeit import timeitcnt = 100000 # base number of repeats for timeitprint('{0: >12} {1: >9} {2: >9} {3: >9} {4: >9}'.format('N', 'Trial', 'Primes', 'Division', 'Wheel'))for k in range(1, 31): N = 2**k count = cnt // k # adjust repeats based upon size of k x = timeit(lambda:trial(N), number=count) y = timeit(lambda:sieve(N), number=count) z = timeit(lambda:list(prime_factorization(N)), number=count) k = timeit(lambda:list(wheel_factorization(N)), number=count) print(f"{N:12d} {1:9d} {x/y:9.5f} {x/z:9.5f} {x/k:9.5f}")