簡體   English   中英

查找一組素數大於x的素數的算法

[英]algorithm to find products of a set of primes, in order, greater than x

考慮有限集{2,3,5,...,n}。 我對素數感興趣,但這個問題可能適用於任何一組數字。 我想找到這些數字升序的所有可能乘積,尤其是大於或等於某個數字x的乘積。 有誰知道一個不錯的算法嗎?

編輯以澄清:

輸入集中的每個因子可以使用多次。 如果輸入為{2,3,5,7},則輸出為{2,3,4,5,6,7,8,9,10,12,14,15,16,18,...} 。 一旦產生大於或等於某個數x的結果,該算法便會停止。

(編輯:使它按升序生產所有產品;讓用戶根據需要過濾它們。這是一個廣義的漢明數問題)

genHamming     :: Integral a => [a] -> [a]
genHamming  zs = hmng where
         hmng = 1 : foldr (||) []  [map (z*) hmng | z <- zs]
         []     ||  ys             =  ys
         xs     ||  []             =  xs
         (x:xs) || (y:ys)  | x==y  =  x : (xs || ys)
                           | x<y   =  x : (xs || (y:ys))
                           | y<x   =  y : (ys || (x:xs))

用法示例

 Prelude Hamming> take 10 $ dropWhile (< 1000) $ genHamming [2,3,5]
 [1000,1024,1080,1125,1152,1200,1215,1250,1280,1296]
 Prelude Hamming>

該答案所示的Haskell代碼,

hamm :: [Integer] -> [Integer]
hamm []     = []   
hamm (p:ps) = xs        -- e.g. hamm [2,3,5] 
        where xs = merge (hamm ps)               --   H({p} ∪ ps) = S,
                         (p : map (p*) xs)       -- S ⊇ {p} ∪ H(ps) ∪ { p*x | x ∊ S }

merge a@(x:xs) b@(y:ys) | x < y     = x : merge xs b 
                        | otherwise = y : merge a ys 
merge [] b = b
merge a [] = a

在這里merge不會嘗試消除倍數,因為不會有倍數-但前提是您在輸入中使用素數

~> take 20 $ hamm [2,3,5,7]
[2,3,4,5,6,7,8,9,10,12,14,15,16,18,20,21,24,25,27,28]

如果不是,則需要使用union

union a@(x:xs) b@(y:ys) | x < y     = x : union xs  b
                        | x > y     = y : union a  ys
                        | otherwise = x : union xs ys
union [] b = b
union a [] = a

從上方開始有效地給定值可能是一個有趣的挑戰。 該答案底部的直接生成切片的代碼可以作為起點。

通常,很容易跳過有序序列,直到傳遞值為止。 在Haskell中,這是通過內置dropWhile (< n)

~> take 10 $ dropWhile (< 100) $ hamm [2,3,5,7]
[100,105,108,112,120,125,126,128,135,140]

您可能還希望在輸出中包括2 ^ 0 * 3 ^ 0 * 5 ^ 0 * 7 ^ 0 = 1。

執行此操作的方法是使用優先級隊列。 如果k在序列中,則2 k ,3 k ,5 k和7 k也是 從1開始輸出,然后將2、3、5和7添加到優先級隊列。 從隊列頂部彈出2,然后將2 * 2 = 4、2 * 3 = 6、2 * 5 = 10和2 * 7 = 14添加到隊列中; 該點處的隊列將包含3、4、5、6、7、10和14。從隊列頂部彈出3,然后添加3 * 2 = 6、3 * 3 = 9、3 * 5 = 15和3 * 7 = 21進入隊列。 等等。

您會發現許多元素是重復的。 例如,在上例中,我們兩次向優先級隊列中添加了6。 您可以添加重復項,並在每次彈出隊列時檢查該元素是否與序列中的前一個成員相同,也可以在隊列中保留單獨的項目列表,並避免首先添加重復項。

我在博客中討論了僅包含不同元素的優先級隊列。

每個大於1的整數都是“素數集”的乘積,因為它是素數因子的乘積。 從所需的最小數字開始並刪除所有不在初始集合中有素數的數字可能會更容易。 繼續執行該過程,直到結果集足夠大為止。 實際上,您正在做一個改進的Eratosthenes篩,刪除了不在初始集中的所有素數的倍數。

因為我們的應用程序是用python編寫的,所以我想分享以下實現:

def powers(x):
    y = x
    while True:
        yield y
        y *= x


def products(factors):
    y0 = factors[0]
    if len(factors) == 1:
        yield from powers(y0)
    else:
        yield y0
        g1 = products(factors)
        y1 = y0 * next(g1)
        g2 = products(factors[1:])
        y2 = next(g2)
        while True:
            if y1 < y2:
                yield y1
                y1 = y0 * next(g1)
            else:
                yield y2
                y2 = next(g2)


if __name__ == "__main__":
    import itertools
    for n in itertools.islice(products([2, 3, 5, 7]), 10**6):
        print(n)

毫無疑問,可以對生成器使用遞歸進行改進,但是實際上對於我們的應用來說,性能已經足夠好了。 除此之外,如Will Ness的答案所述,我仍然對如何有效地以給定的最小值開始感興趣。 感謝所有的貢獻者。

我想到了兩種算法。 首先,您可以計算數字之間的所有可能乘積,然后對它們進行排序。 盡管這似乎是幼稚的方法,但您可以通過“記住”最后一個乘積,除以一個數字,然后在其位置乘以另一個數字來加快此過程。 如果使用正確的排列順序(查看格雷碼 ),可以最大程度地減少總乘法運算,那么這將大大減少所需的運算次數。

另一方面,您可以做的是計算所有原始數字的集合,(兩個)原始數字對的乘積集合,3的乘積集合...依此類推。 然后,您可以對每個單獨的集合進行排序(這不難確保無論如何都將它們幾乎排序),然后將集合進行合並排序到一個排序的產品列表中。 這將需要更多的操作,但最終會導致列表幾乎排序,從而可能花費更少的時間來構建整體。

另一種算法是獲取所有感興趣的素數的乘積,並將其稱為P。構造所有原始素數平方的另一個列表。 現在,循環遍歷所有數字,直到P,然后測試它們是否可以被質數平方數組中的任何值整除。 如果是的話,就扔他們。 如果不是,則將它們添加到輸出數組。 您可以僅通過測試sqrt(i)的除數來優化此性能,其中i是for循環中的迭代。 但是,這仍然可能比上述方法慢。

由於每個素數都可以出現多次,因此序列是無限的。 因此,我們無法生成所有產品然后對其進行排序。 我們必須迭代生成序列。

如果a是該序列的成員,則{2*a, 3*a, 5*a ... n*a}也將是該序列的成員,稍后再介紹。

因此,我想到的算法是為下一個序列成員提供一個(排序的,無重復的)緩沖區。 我們提取並呈現出最小值,並將其所有倍數插入緩沖區。

由於很難預測起始數字x的緩沖區內容,因此該算法應從頭開始,並忽略結果,直到達到x閾值為止。

暫無
暫無

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

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