我正在尝试在Elixir中重新创建比特币的一些基础知识。我知道Elixir不是完成任务的理想语言,但我这样做是为了娱乐和学习目的。当我试图从一个秘密实现公钥派生时遇到了以下问题,其中包括在有限域上非常大的数量上的非常大的功率。我自己实现了这个:math.pow / 2已经在与相对较小的数字进行斗争。但我的实施花了很长时间,功能最终耗尽。
值并调用函数:
prime = 115792089237316195423570985008687907853269984665640564039457584007908834671663
val = 65341020041517633956166170261014086368942546761318486551877808671514674964848
Util.my_fpow(val, prime - 2, prime)
Util模块中的my_fpow / 3方法:
defmodule Util do
def my_fpow(n, k, prime), do: my_fpow(n, k, 1, prime)
defp my_fpow(_, 0, acc, _), do: acc
defp my_fpow(n, k, acc, prime) do
new_acc = n * acc
|> rem(prime)
my_fpow(n, k - 1, new_acc, prime)
end
end
首先,我想更深入地了解为什么这种方法对于这些大数字需要很长时间。另外我会感兴趣的是,还有其他更高效的实现可能仍然可以在Elixir中进行这样的计算(不是大规模的,只是因此计算实际上可以在超时之前完成)。
答案 0 :(得分:4)
您的功能将递归prime - 2
次,因为您一次减1。即使Elixir每秒执行10亿次呼叫(它在当前硬件上获胜),它也需要超过10 ^ 60年才能执行。
更快的解决方案involves squaring the number on each iteration,可以减少对log2(x)
的调用次数,这非常快。这是该算法的翻译:
defmodule A do
# Calculates (n ^ k) % m.
def powmod(n, k, m), do: powmod(n, k, m, 1)
def powmod(_, 0, _, r), do: r
def powmod(n, k, m, r) do
r = if rem(k, 2) == 1, do: rem(r * n, m), else: r
n = rem(n * n, m)
k = div(k, 2)
powmod(n, k, m, r)
end
end
prime = 115792089237316195423570985008687907853269984665640564039457584007908834671663
val = 65341020041517633956166170261014086368942546761318486551877808671514674964848
IO.inspect A.powmod(val, prime - 2, prime)
输出:
83174505189910067536517124096019359197644205712500122884473429251812128958118