我目前正在尝试在Haskell中实施Atkin筛分法
在Wikipedia article on the Sieve of Atkin的第3步中,我需要找到多个方程的整数解。
但是我对这些方程中的第一个方程的解(4x²+y²= n,x> 0,y> 0 n是正整数列表中的一个条目),对任何n的查询都会产生无限循环。
这是我到目前为止针对此问题的代码:
eq1 :: Integer -> Integer
eq1 n = eq1_ n []
eq1_ :: Integer -> [(Integer, Integer)] -> Integer
eq1_ n list | (x > 0) && (y > 0) && (n == 4*(x^2) + (y^2)) && (notElem ((x,y)) list) = eq1_ n ([(x, y)] ++ list)
| otherwise = toInteger (length list)
where
x = floor (sqrt (fromIntegral ((n - y^2) `div` 4)))
y = floor (sqrt (fromIntegral (n - 4*(x^2))))
WinGHCi可以很好地加载它,但是当我查询例如eq1 0
仅停留在无限循环中,必须在产生答案之前中断。我怀疑它会在x
和y
的两个分配之间循环。
如何防止这种情况?这有可能吗?
编辑:意识到无限循环必须在哪里。
答案 0 :(得分:1)
我将首先将您的代码重新格式化以使其更具可读性。换行符很有帮助!同样,操作顺序可以减少括号的权重。旁注:
f x | e1 && e2 && e3 = e4
也可以写
f x | e1
, e2
, e3
= e4
在眼睛上可能更容易。
eq1 :: Integer -> Integer
eq1 n = eq1_ n []
eq1_ :: Integer -> [(Integer, Integer)] -> Integer
eq1_ n list
| x > 0 &&
y > 0 &&
n == 4*x^2 + y^2 &&
notElem (x,y) list
= eq1_ n ([(x, y)] ++ list)
| otherwise
= toInteger (length list)
where
isqrt = floor . sqrt . fromIntegral
x = isqrt $ (n - y^2) `div` 4
y = isqrt $ n - 4*(x^2)
现在,我可以立即看到逻辑很奇怪。给定n
,您就可以计算出x
和y
。然后,您可以停止或递归调用该函数。但是,在递归调用上,您一定会停止!因此,即使您是对的,您也肯定会遇到语义问题,总是返回0或1。
但是,正如您所看到的,这不是唯一的问题。您还根据x
定义了y
,并根据y
定义了x
。现在,在某些重要情况下,这种相互递归很有用。但是,当相互递归的值是“原子”之类的整数时,您肯定会遇到无限循环。 Haskell不会为您解决方程式;那是你的工作!
这是我的建议:
从暴力列表理解解决方案开始:
sols n
= [(x,y)
|x <- takeWhile (\p -> 4 * p^2 < n) [1..]
,y <- takeWhile (\q -> f x y <= n) [1..]
,f x y = n]
where
f x y = 4*x^2+y^2
接下来,您可以使用近似整数平方根来缩小y
的搜索空间:
sols n
= [(x,y)
|x <- takeWhile (\p -> 4 * p^2 < n) [1..]
,y <- takeWhile
(\q -> f x y <= n)
[floor(sqrt(fromIntegral(n-4*x^2)))..]
,f x y = n]
where
f x y = 4*x^2+y^2