簡體   English   中英

漢明數和雙精度

[英]Hamming numbers and double precision

我正在嘗試在 Haskell 中生成漢明數,試圖改進明顯的(請原諒函數的命名)

mergeUniq :: Ord a => [a] -> [a] -> [a]
mergeUniq (x:xs) (y:ys) = case x `compare` y of
                               EQ -> x : mergeUniq xs ys
                               LT -> x : mergeUniq xs (y:ys)
                               GT -> y : mergeUniq (x:xs) ys

powers :: [Integer]
powers = 1 : expand 2 `mergeUniq` expand 3 `mergeUniq` expand 5
  where
    expand factor = (factor *) <$> powers

我發現我能避免(更慢)的任意精度的Integer ,如果我所代表的數字作為三重的2-,3-和5-指數像data Power = Power { k2 :: !Int, k3 :: !Int, k5 :: !Int } ,其中數字被理解為2 k2 * 3 k3 * 5 k5 兩個Power的比較則變為

instance Ord Power where
  p1 `compare` p2 = toComp (p1 `divP` gcdP) `compare` toComp (p2 `divP` gcdP)
    where
    divP p1 p2 = Power { k2 = k2 p1 - k2 p2, k3 = k3 p1 - k3 p2, k5 = k5 p1 - k5 p2 }
    gcdP = Power { k2 = min (k2 p1) (k2 p2), k3 = min (k3 p1) (k3 p2), k5 = min (k5 p1) (k5 p2) }
    toComp Power { .. } = fromIntegral k2 * log 2 + fromIntegral k3 * log 3 + fromIntegral k5 * log 5

所以,很粗略地講,比較p₁ = 2 i₁ * 3 j₁ * 5 k₁p₂ = 2 i₂ * 3 j₂ * 5 k₂ ,我們比較的對數p₁p₂ ,這大概適合Double 但實際上我們做得更好:我們首先計算它們的 GCD(通過找到相應指數對的min p₁到目前為止只有Int算術!),將p₁p₂除以 GCD(通過從相應的指數中減去min p₂也只有Int算術),並比較結果的對數。

但是,考慮到我們經過Double ,最終會失去精度。 這是我的問題的基礎:

  1. Double的有限精度什么時候會咬我? 也就是說,如何估計2 i * 3 j * 5 k與具有“相似”指數的數字的比較結果會變得不可靠的i, j, k階?
  2. 我們除以 GCD(這可能大大降低了此任務的指數)這一事實如何修改前一個問題的答案?

我做了一個實驗,將這種方式產生的數字與通過任意精度算術產生的數字進行比較,並且所有漢明數直到 1'000'000'000th 完全匹配(這花了我大約 15 分鍾和 600 兆的 RAM驗證)。 但這顯然不是證據。

從經驗上看,它高於大約 10 萬億分之一的漢明數,或者更高。

使用你的 GCD 技巧在這里對我們沒有幫助,因為一些相鄰的漢明數之間必然沒有公因數。

更新:在 ideone和其他地方在線嘗試,我們得到

4T  5.81s 22.2MB  -- 16 digits used.... still good
                  --  (as evidenced by the `True` below), but really pushing it.
((True,44531.6794,7.275957614183426e-11),(16348,16503,873),"2.3509E+13405")
-- isTruly  max        min logval           nth-Hamming       approx.
--  Sorted   logval      difference          as i,j,k          value
--            in band      in band                             in decimal
10T   11.13s 26.4MB
((True,60439.6639,7.275957614183426e-11),(18187,23771,1971),"1.4182E+18194")
13T   14.44s 30.4MB    ...still good
((True,65963.6432,5.820766091346741e-11),(28648,21308,1526),"1.0845E+19857")

---- same code on tio:
10T   16.77s
35T   38.84s 
((True,91766.4800,5.820766091346741e-11),(13824,2133,32112),"2.9045E+27624")
70T   59.57s
((True,115619.1575,5.820766091346741e-11),(13125,13687,34799),"6.8310E+34804")

---- on home machine:
100T: 368.13s
((True,130216.1408,5.820766091346741e-11),(88324,876,17444),"9.2111E+39198")

140T: 466.69s
((True,145671.6480,5.820766091346741e-11),(9918,24002,42082),"3.4322E+43851")

170T: 383.26s         ---FAULTY---
((False,155411.2501,0.0),(77201,27980,14584),"2.80508E+46783")

我猜您可以使用自適應任意精度來計算日志。

如果您選擇 log base 2,則log2(2^i)是微不足道的。 這消除了 1 個因子,log2 的優點是比自然對數更容易計算( https://en.wikipedia.org/wiki/Binary_logarithm給出了一個算法,例如還有 Shanks ......)。

對於 log2(3) 和 log2(5),您將開發足夠的術語來區分兩個操作數。 我不知道它是否會導致比在大整數算法中直接對 3^j 和 5^k 取冪並計算高位更多的運算......但是這些可以預先制表到所需的位數。

暫無
暫無

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

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