簡體   English   中英

獲取比率為素數的數的除數

[英]Get divisors of a number whose ratio is a prime number

我需要得到比率為素數的任意數(最多 10e9)的所有除數。

考慮一些 integer n 的所有除數。 選擇其中一些除數,然后將它們排列成一個圓圈,使任何兩個相鄰數字的比率都是質數。 找到你可以選擇的 n 的最大除數,並找到合適的排列。

例如:

input: 10
output: 1 2 10 5

2 = 1 * 2 ; 10 = 2 * 5 5 = 1 * 5

output 陣列也必須是一個循環(圓)。 因此,第一個數字和最后一個數字的比率必須是質數。

我試圖找到給定數字的所有除數並生成它們的所有可能排列並檢查每個排列:

from itertools import permutations

n = int(input())
d = list(filter(lambda x: not n % x, range(1, n + 1)))
for i in range(1, len(d)):
    print(list(permutations(d, i)))
    # check the permutation

但這是一個糟糕的解決方案,因為它的工作時間很長。 誰能建議一種算法來更有效地解決我的問題?

我正在添加一個新答案,因為我想到了一個非常不同的過程來獲取除數列表。

我沒有關注因素,而是創建了一系列素數比率(乘法和除法)。 鑒於我們需要循環回初始值,每個乘法比率最終都必須遵循相同比率的相應除法。 所以我們只需要找到素數乘法和除法的正確順序(使用數字的素數因子)。

例如,6 的質因數是 2 和 3,因數是 1、2、3 和 6。因數之間的比率將是 2 或 3。從 1 開始,我們可以有一系列比率:x2、x3 , /2, /3 將產生因子序列:1、2、6、3、1。(最后一個是環回到 1)。 這平衡了每個 xPrime 與相應的 /Prime,確保循環完成。

如果我們將質因數 (5) 和 go 添加到 30,則質因數是 2、3 和 5,這些因數現在是 1、2、3、6、5、10、15 和 30。

當有兩個以上的素數因子時,比率序列可以遞歸地“堆疊”在每個素數的頂部(用作基線)。 這意味着一系列因素(例如 3,5 --> x3, x5, /3, /5)可以獨立存在並返回到原始值(1),但它也可以從不同的基線開始(例如2) 並且會回到它而不產生它從 1 基線中獲得的任何因素:

from 1:   (x3, x5, /3, /5) --> 3, 15, 5,  1
from 2:   (x3, x5, /3, /5) --> 6, 30, 10, 2
  • 2 的基本序列是x2, /2
  • 在 x2 基線上堆疊 3 和 5,我們將有:
    • x2, <stack 3 and 5>, /2
  • 3 和 5 的順序是x3, x5, /3, /5
  • 給予: x2, x3, x5, /3, /5, /2
  • 但是堆疊序列的最后一個分區 (/5) 會產生重復因子,因為序列會循環回基線。
  • 因此,我們將最后一個分區 (/5) 與下一個分區 (/2) 交換:
  • 給予: x2, x3, x5, /3, /2, /5
  • 導致因子: 2, 6, 30, 10, 5, 1

當有 4 個或更多素數因子時,這也適用於順序,通過循環回到每個素數並將剩余因子堆疊在其頂部(作為新的唯一基線)。

這類似於通過從每個值(在本例中為質數)開始並從剩余值添加組合模式來遞歸構建組合的方式

這仍然需要一個素數分解 function:

def primeFactors(N):
    d = 2
    while d*d<=N:
        while N%d==0:
            yield d
            N //= d
        d += 1
    if N>1: yield N

比率序列 function 是遞歸的,使用正數表示乘法,使用負數表示除法(例如 x2, x3, /2, /x --> [2, 3, -2, -3])

def getRatios(factors,seen={1},base=1):
    seen     = set(seen)           # want a copy
    rFactors = list(factors)       # remaining prime factors
    uFactors = set(rFactors)       # unique prime factors
    skipSet  = {f for f in uFactors if base*f in seen} # excluded ratios
    ratios   = []                  # resulting sequence of ratios
    while uFactors:                # new baseline for each prime
        if not ratios:             # first multiplication            
            f = min(uFactors-skipSet,default=0) # non-conflicting prime
            if not f: break
            ratios = [f,-f]  # positive = multiply, negative = divide
            base  *= f       # new baseline
            seen.add(base)   # and seen factor 
        else:                                  # extend recursively
            fr = getRatios(rFactors,seen,base) # pattern over baseline
            if not fr: break                   # if any
            fr.insert(-1,ratios.pop(-1))       # invert last divisions
            prods = [base]                     # check resulting factors
            for r in fr[:-1]:
                prods.append(prods[-1]*r if r>0 else prods[-1]//-r)
            if seen.isdisjoint(prods[1:]):     # no conflicts
                base = prods[-1]               # work from new base (prime) 
                seen.update(prods)             # track factors used
                ratios.extend(fr)              # extend ratio sequence
                f = abs(ratios[-1])            # last used prime factor
            else:
                skipSet.add(ratios[0])         # can't start with that prime
                ratios = []                    # restart process
                continue
        if f in rFactors:                      # remove last used prime
            del rFactors[rFactors.index(f)]    # allow repeated primes
        uFactors.discard(f)                    # but only once ber level 
    return ratios

最終結果是通過從基線 1 開始依次應用比率獲得的(不包括最后一個除法,因為我們只需要看到 1 一次:

def factorCircle2(N):
    ratios = getRatios(primeFactors(N))  # get ratio sequence
    result = [1]                         # startfactors at 1
    for r in ratios[:-1]:                # apply ratios to get factors
        result.append(result[-1]*r if r>0 else result[-1]//-r)
    return result

Output:

print(factorCircle2(10))    # [1, 2, 10, 5]                # 4 of 4 factors
print(factorCircle2(30))    # [1, 2, 6, 30, 10, 5, 15, 3]  # 8 of 8
print(factorCircle2(512))   # [1, 2]                       # 2 of 10 
print(factorCircle2(660))   
# [1, 2, 4, 12, 60, 660, 132, 44, 220, 20, 10, 30, 330, 110, 22, 
   66, 6, 3, 15, 165, 33, 11, 55, 5]                       # 24 of 24
print(factorCircle2(60060)) 
# [1, 2, 4, 12, 60, 420, 4620, 60060, 5460, 780, 8580, 660, 132, 924, 
   12012, 1716, 156, 1092, 84, 28, 140, 1540, 20020, 1820, 364, 4004,
   308, 44, 220, 2860, 572, 52, 260, 20, 10, 30, 210, 2310, 30030, 
   2730, 390, 4290, 330, 110, 770, 10010, 1430, 130, 910, 70, 14, 42, 
   462, 6006, 546, 182, 2002, 154, 22, 66, 858, 286, 26, 78, 6, 3, 15,
   105, 1155, 15015, 1365, 195, 2145, 165, 33, 231, 3003, 429, 39, 273, 
   21, 7, 35, 385, 5005, 455, 91, 1001, 77, 11, 55, 715, 143, 13, 65, 
   5]   # 96 of 96 factors

請注意,我還沒有進行徹底的測試,並且仍然存在需要解決的邊緣情況。 然而,性能比基於圖形的解決方案要好得多。 我將嘗試修復邊緣情況(圍繞重復的主要因素),但與此同時,這可能是找出一個好的解決方案的起點。

您可以通過將其轉換為在圖中查找最長路徑來解決此問題。

該圖將由對應於因素的節點和將節點與因素之間的素數比率聯系起來的邊組成。

您首先需要一個 function ,它可以為您提供一個數字的所有素數(包括重復素數):

def primeFactors(N):
    d = 2
    while d*d<=N:
        while N%d==0:
            yield d
            N //= d
        d += 1
    if N>1: yield N

然后,您可以將所有這些素數因子組合成一個因子字典,其中的值是一組其他因子,這些因子與每個因子 {factor:{factor with prime ratio}..} 成素數比。 這將是您的圖表。

def factorGraph(N):
    primes  = list(primeFactors(N))
    factors = {1:set()}
    for p in primes:
        for f in list(factors):
            factors.setdefault(p*f,set()).add(f)
            factors.setdefault(f,set()).add(p*f)
            if f in primes:
                factors.setdefault(p*f,set()).add(p) 
    return factors

然后使用遞歸 function 找到從起始因子到給定目標節點的最長循環路徑(從目標節點本身的相關節點開始)。 function 不能多次使用相同的因子,因此它會下推一組已訪問節點(可見)以將它們從更深的搜索中排除。

def longestCircle(graph,target,fromFactor=0,seen=set()):
    longest = []
    remaining = graph[fromFactor or target] - seen  # eligible next factors
    for factor in remaining:                        # Try each factor
        path = [factor]                             # form a path
        if factor != target:                        # must reach target
            path += longestCircle(graph,target,factor,seen|{factor})
        if path[-1]==target and len(path)>=len(longest): 
            longest = path                          # keep longest path
    return longest

最后,最長的因子循環路徑將是元素最多的那個。

def factorCircle(N):
    graph = factorGraph(N)
    return max((longestCircle(graph,first) for first in graph),key=len)

請注意,這是從每個可能的起始因素檢查,以防更長的路徑跳過其中一個因素,但很可能 1 總是在循環中,所以這可能是多余的(我檢查了從 2 到 100,000 的數字,總是發現 1 到處於循環中)。 返回longestCircle(graph,1) 可能就足夠了,但我沒有時間證明它是(或不是)。

output:

print(factorCircle(10))  # [5, 10, 2, 1]               # 4 of 4 factors
print(factorCircle(30))  # [2, 6, 3, 15, 5, 1]         # 6 of 8
print(factorCircle(512)) # [2, 1]                      # 2 of 10
print(factorCircle(660)) # [2, 6, 3, 15, 5, 55, 11, 1] # 8 of 24
print(factorCircle(60060))
# [2, 6, 3, 15, 5, 35, 7, 77, 11, 143, 13, 1]          # 12 of 96

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM