schmem语言实现快速幂
使用 Scheme 语言重写的 C++ 快速幂(模幂运算)代码。
//相关C++代码
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1 % p;
while (b)
{
if (b & 1) res = res * a % p;
a = a * (LL)a % p;
b >>= 1;
}
return res;
}
我们将 C++ 中的 while 循环迭代实现,转换为 Scheme 中等价且高效的尾递归实现。
Scheme 代码
这个版本使用一个命名的 let 来创建一个尾递归循环,这与 C++ 中的 while 循环在逻辑上是完全对应的。
(define (qmi a b p)
;; 使用一个命名的 let 'loop' 来进行尾递归,模拟 C++ 的 while 循环
;; a: 当前的底数 (C++ 中的 a)
;; b: 当前的指数 (C++ 中的 b)
;; res: 累积的结果 (C++ 中的 res)
(let loop ((a (remainder a p)) ; 初始 a 对 p 取模
(b b)
(res 1))
;; 当指数 b 为 0 时,循环结束,返回结果
(if (= b 0)
res
;; 否则,继续下一次递归
(loop
;; 更新 a: a = a * a % p
(remainder (* a a) p)
;; 更新 b: b = b / 2 (整数除法)
(quotient b 2)
;; 更新 res: 如果 b 是奇数, res = res * a % p
(if (odd? b)
(remainder (* res a) p)
res)))))
代码解释
-
函数定义:
define (qmi a b p)定义了一个名为qmi的函数,接受三个参数:底数a、指数b和模数p。 -
尾递归循环:
let loop (...)在 Scheme 中创建了一个局部作用域的循环。通过在循环内部调用loop本身,我们实现了递归。因为这个调用是函数的最后一个操作,所以它是“尾递归”,Scheme 解释器会将其优化为迭代,避免了堆栈溢出的风险,效率与 C++ 的while循环相当。loop有三个状态变量,完全对应 C++ 版本中的变量:a: 对应 C++ 中的a,在每次迭代中都会被平方。初始值是(remainder a p)。b: 对应 C++ 中的b,在每次迭代中都会被右移一位(即整除2)。res: 对应 C++ 中的res,用于累积结果。初始值为 1。
-
循环终止条件:
(if (= b 0) res ...)检查指数b是否为 0。如果是,说明计算完成,返回累积的结果res。这相当于 C++ 中的while (b)的循环终止条件。
-
循环体逻辑:
loop的三个新参数对应着 C++ 循环体中变量的更新。- 更新
a:(remainder (* a a) p)对应 C++ 的a = a * (LL)a % p;。 - 更新
b:(quotient b 2)对应 C++ 的b >>= 1;(quotient是整数除法)。 - 更新
res:(if (odd? b) (remainder (* res a) p) res)对应 C++ 的if (b & 1) res = res * a % p;。(odd? b)用于检查b的最低位是否为1。
与 C++ 代码的对比
C++ 版本 (while 循环) |
Scheme 版本 (尾递归 loop) |
说明 |
|---|---|---|
LL res = 1 % p; |
(res 1) |
初始化结果 res 为 1。 |
while (b) |
(if (= b 0) res ...) |
循环条件:当 b 不为 0 时继续。 |
if (b & 1) |
(if (odd? b) ...) |
检查 b 是否为奇数。 |
res = res * a % p; |
(remainder (* res a) p) |
如果 b 是奇数,则更新 res。 |
a = a * (LL)a % p; |
(remainder (* a a) p) |
a 自我平方并取模,为下一次迭代做准备。 |
b >>= 1; |
(quotient b 2) |
b 右移一位(整除2)。 |
类型处理 (LL) |
自动大数处理 | C++ 需要 long long 来防止 a*a 溢出,而 Scheme 自动支持大整数运算,无需特殊处理。 |
如何使用
你可以像这样调用这个函数:
;; 计算 3^5 mod 7
(qmi 3 5 7)
;; => 5
;; 计算 2^100 mod 31
(qmi 2 100 31)
;; => 1

浙公网安备 33010602011771号