在学习考试时,我刚刚在练习中找到了以下任务:
编写一个函数,将整数对数给予基数2(向上舍入),同时仅使用乘法和加法。
我立即尝试了,但无法解决任何问题。我认为这将是一项简单的任务,但我只能在使用整数除法时找到解决方案(例如在Haskell中):
log2 :: Int -> Int
log2 1 = 0
log2 2 = 1
log2 x = 1 + log2 (x `div` 2)
这项任务是否可以仅使用乘法?在左侧使用乘法(模式)总是会导致编译器错误。在右侧使用它,如何将解决方案追溯到较低的数字?
答案 0 :(得分:6)
在右侧使用它,如何将解决方案追溯到较低的数字?
递归。由于计算楼层更容易,我们使用
这一事实ceiling (log_2 n) == floor (log_2 (2*n-1))
很容易看出来。然后,为了找到基数b
的对数,我们计算基数b²
的对数并调整:
log2 :: Int -> Int
log2 1 = 0
log2 2 = 1
log2 n
| n < 1 = error "Argument of logarithm must be positive"
| otherwise = fst $ doLog 2 1
where
m = 2*n-1
doLog base acc
| base*acc > m = (0, acc)
| otherwise = case doLog (base*base) acc of
(e, a) | base*a > m -> (2*e, a)
| otherwise -> (2*e+1,a*base)
需要更多步骤的更简单的算法是简单地迭代,在每一步中乘以2并计数,直到达到或超过参数值:
log2 :: Int -> Int
log2 n
| n < 1 = error "agument of logarithm must be positive"
| otherwise = go 0 1
where
go exponent prod
| prod < n = go (exponent + 1) (2*prod)
| otherwise = exponent
答案 1 :(得分:2)
怎么样:
log2 n = length (takeWhile (<n) (iterate (*2) 1))
我假设您可以使用Prelude中的函数(如error
,fst
和比较运算符)。如果在考试中不允许这样做,理论上你可以使用length
,takeWhile
和iterate
的定义,并最终得到一些相对接近的东西(在精神上,可能不在信中!)丹尼尔的答案。
答案 2 :(得分:0)
也许您可以使用系列扩展来近似日志功能。特别是Taylor’s ones。