Prelude Diatomic> firstDiatomic 400000
363490989
(0.03 secs, 26265328 bytes)
Prelude Diatomic> map firstDiatomic [400000 .. 400100]
[363490989,323659475,580472163,362981813,349334091,355685483,346478235,355707595
,291165867,346344083,347155797,316314293,576398643,315265835,313171245,355183267
,315444051,315970205,575509833,311741035,340569429,313223987,565355925,296441165
,361911645,312104147,557145429,317106853,323637939,324425077,610613547,311579309
,316037811,311744107,342436533,348992869,313382235,325406123,355818699,312128723
,347230875,324752171,313178421,312841811,313215645,321754459,576114987,325793195
,313148763,558545581,355294101,359224397,345462093,307583675,355677549,312120731
,341404245,316298389,581506779,345401947,312109779,316315061,315987123,313447771
,361540179,313878107,304788843,325765547,316036275,313731751,355635795,312035947
,346756533,313873883,349358379,357393763,559244877,313317739,325364139,312128107
,580201947,358182323,314944173,357403987,584291115,312158827,347448723,363246413
,315935571,349386085,315929427,312137323,357247725,313207657,320121429,356954923
,557139285,296392013,576042123,311726765,296408397]
(2.45 secs, 3201358192 bytes)
1/1
/ \
/ \
/ \
1/2 2/1
/ \ / \
1/3 3/2 2/3 3/1
1
/ \
/ \
/ \
2 3
/ \ / \
4 5 6 7
/ \
8 9 ...
p = a*q + b, 1 <= b < q
a
次,从b/q
节点到达p/q
节点。b/q
的父节点是b/(q-b)
。如果q = c*b + d, 1 <= d < b
以此类推。
我们可以使用p/q
的连分数(这里仅考虑简单连分数)展开来找到从根节点(1/1
)到p/q
节点的路径。假设p>q
,且
p/q = [a_0, a_1, ..., a_r,1]
以p/q
的连分数展开以1
结尾为条件。
r
是偶数,则先向右子节点走a_r
次,然后向左边走a_(r-1)
次,再向右子节点...最后向左子节点走a_1
次,最终向右子节点走a_0
次。r
是奇数,则先向左子节点走a_r
次,然后向右边走a_(r-1)
次...最后向左子节点走a_1
次,最终向右子节点走a_0
次。对于p < q
,我们必须最后向左边走,因此对于偶数r
开始向左边走,对于奇数r
开始向右边走。
我们因此找到了索引的二进制表示和通过从根到节点的路径所携带的分数的连分数展开之间的紧密联系。
设索引k
的运行长度编码为
[c_1, c_2, ..., c_j] (all c_i > 0)
k
以c_1
个1开头,后跟c_2
个零,然后是c_3
个1等,最后以c_j
结束。
k
是奇数,则为1-因此j
也是奇数;k
是偶数,则为0-因此j
也是偶数。然后,[c_j, c_(j-1), ..., c_2, c_1]
是a[k]/a[k+1]
的连分数展开式,其长度与k
具有相同的奇偶性(每个有理数恰好有两个连分数展开式,一个奇数长度,另一个偶数长度)。
RLE给出了从1/1
上方的0/1
节点到a[k]/a[k+1]
的路径。路径的长度为:
k
所需的位数,以及现在,要找到第一次出现n>0
的双调序列索引,我们首先观察到最小索引必须是奇数,因为对于偶数k
,a[k] = a[k/2]
。让最小索引为k=2*j+1
。然后
k
的RLE长度为奇数,k
的节点处的分数为a[2*j+1]/a[2*j+2] = (a[j]+a[j+1])/a[j+1]
,因此它是右子节点。因此,具有a[k]=n
的最小索引k
对应于所有最短路径的最左端,这些路径指向分子为n
的节点。
最短路径对应于n/m
的连分数展开式,其中0 < m <= n
与n
互质[分数必须被约简],并且其部分商之和最小。
我们需要期望什么样的长度?给定连分数p/q = [a_0, a_1, ..., a_r]
,其中a_0 > 0
且总和
s = a_0 + ... + a_r
F(5) = 5 < 6 <= F(6) = 8
6/m
,其中0 < m <= 6
。6/1 = [6] (alternatively [5,1])
6/5 = [1,4,1] (alternatively [1,5])
n/m = [a_0, a_1, ..., a_r]
a_0 >= 2
,(n-m)/m = [a_0 - 1, a_1, ..., a_r]
and since
n/(n-m) = 1 + m/(n-m) = 1 + 1/((n-m)/m)
n/(n-m)
的连分数展开式为:
n/(n-m) = [1, a_0 - 1, a_1, ..., a_r]
module Diatomic (diatomic, firstDiatomic, fuscs) where
import Data.List
strip :: Int -> Int -> Int
strip p = go
where
go n = case n `quotRem` p of
(q,r) | r == 0 -> go q
| otherwise -> n
primeFactors :: Int -> [Int]
primeFactors n
| n < 1 = error "primeFactors: non-positive argument"
| n == 1 = []
| n `rem` 2 == 0 = 2 : go (strip 2 (n `quot` 2)) 3
| otherwise = go n 3
where
go 1 _ = []
go m p
| m < p*p = [m]
| r == 0 = p : go (strip p q) (p+2)
| otherwise = go m (p+2)
where
(q,r) = m `quotRem` p
contFracLim :: Int -> Int -> Int -> Maybe [Int]
contFracLim = go []
where
go acc lim n d = case n `quotRem` d of
(a,b) | lim < a -> Nothing
| b == 0 -> Just (a:acc)
| otherwise -> go (a:acc) (lim - a) d b
fixUpCF :: [Int] -> [Int]
fixUpCF [a]
| a < 3 = [a]
| otherwise = [1,a-2,1]
fixUpCF xs
| even (length xs) = case xs of
(1:_) -> fixEnd xs
(a:bs) -> 1 : (a-1) : bs
| otherwise = case xs of
(1:_) -> xs
(a:bs) -> 1 : fixEnd ((a-1):bs)
fixEnd :: [Int] -> [Int]
fixEnd [a,1] = [a+1]
fixEnd [a] = [a-1,1]
fixEnd (a:bs) = a : fixEnd bs
fixEnd _ = error "Shouldn't have called fixEnd with an empty list"
cfCompare :: [Int] -> [Int] -> Ordering
cfCompare (a:bs) (c:ds) = case compare a c of
EQ -> cfCompare ds bs
cp -> cp
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
toNumber :: [Int] -> Integer
toNumber = foldl' ((+) . (*2)) 0 . concat . (flip (zipWith replicate) $ cycle [1,0])
fuscs :: Integer -> (Integer, Integer)
fuscs 0 = (0,1)
fuscs 1 = (1,1)
fuscs n = case n `quotRem` 2 of
(q,r) -> let (a,b) = fuscs q
in if r == 0
then (a,a+b)
else (a+b,b)
diatomic :: Integer -> Integer
diatomic = fst . fuscs
firstDiatomic :: Int -> Integer
firstDiatomic n
| n < 0 = error "Diatomic sequence has no negative terms"
| n < 2 = fromIntegral n
| n == 2 = 3
| otherwise = toNumber $ bestCF n
bestCF :: Int -> [Int]
bestCF n = check [] estimate start
where
pfs = primeFactors n
(step,ops) = case pfs of
(2:xs) -> (2,xs)
_ -> (1,pfs)
start0 = (n-1) `quot` 2
start | even n && even start0 = start0 - 1
| otherwise = start0
eligible k = all ((/= 0) . (k `rem`)) ops
estimate = length (takeWhile (<= fromIntegral n) fibs) + 2
check candidates lim k
| k < 1 || n `quot` k >= lim = if null candidates
then check [] (2*lim) start
else minimumBy cfCompare candidates
| eligible k = case contFracLim lim n k of
Nothing -> check candidates lim (k-step)
Just cf -> let s = sum cf
in if s < lim
then check [fixUpCF cf] s (k - step)
else check (fixUpCF cf : candidates) lim (k-step)
| otherwise = check candidates lim (k-step)
我建议你阅读这篇来自Dijkstra的信件,其中解释了通过以下方式计算此函数的另一种方法:
n, a, b := N, 1, 0;
do n ≠ 0 and even(n) → a, n:= a + b, n/2
odd(n) → b, n:= b + a, (n-1)/2
od {b = fusc(N)}
这个算法以a,b = 1,0开头,并且有效地使用了N的连续位(从最低有效位到最高有效位)来增加a和b,最终结果是b的值。
因此,可以通过找到使迭代产生该值b的最小n来计算特定b值的第一次出现的索引。
一种找到这个最小n的方法是使用A*搜索,其中成本是n的值。算法的效率取决于您选择的启发式算法。
对于启发式算法,我建议注意以下几点:
编辑
下面是一些示例Python代码,以说明A *方法。
from heapq import *
def gcd(a,b):
while a:
a,b=b%a,a
return b
def heuristic(node,goal):
"""Estimate least n required to make b==goal"""
n,a,b,k = node
if b==goal: return n
# Otherwise needs to have at least one more bit set
# Improve this heuristic to make the algorithm faster
return n+(1<<k)
def diatomic(goal):
"""Return index of first appearance of n in Stern's Diatomic sequence"""
start=0,1,0,0
f_score=[] # This is used as a heap
heappush(f_score, (0,start) )
while 1:
s,node = heappop(f_score)
n,a,b,k = node
if b==goal:
return n
for node in [ (n,a+b,b,k+1),(n+(1<<k),a,b+a,k+1) ]:
n2,a2,b2,k2 = node
if b2<=goal and (goal%gcd(a2,b2))==0:
heappush(f_score,(heuristic(node,goal),node))
print [diatomic(n) for n in xrange(1,10)]
od
在 od {b = fusc(N)}
中是什么意思? - גלעד ברקן