为什么 Elixir 中的大量电源超时?
Why is large number power timing out in Elixir?
我正在尝试在 Elixir 中重新创建比特币的一些基础知识。我知道 Elixir 不是完成这项任务的理想语言,但我这样做是出于娱乐和学习的目的。当我尝试从一个秘密实现 public 密钥派生时,我 运行 遇到了以下问题,其中包括对有限域上非常大的数字的非常大的幂。鉴于 :math.pow/2 已经在与相对较小的数字作斗争,我自己实现了这一点。但是我的实现花费了非常长的时间,并且函数最终超时了。
值和调用函数:
prime = 115792089237316195423570985008687907853269984665640564039457584007908834671663
val = 65341020041517633956166170261014086368942546761318486551877808671514674964848
Util.my_fpow(val, prime - 2, prime)
my_fpow/3Util模块中的方法:
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 中进行此类计算(不是大规模的,只是这样计算可以在超时之前实际完成),我也会感兴趣。
您的函数将递归 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
我正在尝试在 Elixir 中重新创建比特币的一些基础知识。我知道 Elixir 不是完成这项任务的理想语言,但我这样做是出于娱乐和学习的目的。当我尝试从一个秘密实现 public 密钥派生时,我 运行 遇到了以下问题,其中包括对有限域上非常大的数字的非常大的幂。鉴于 :math.pow/2 已经在与相对较小的数字作斗争,我自己实现了这一点。但是我的实现花费了非常长的时间,并且函数最终超时了。
值和调用函数:
prime = 115792089237316195423570985008687907853269984665640564039457584007908834671663
val = 65341020041517633956166170261014086368942546761318486551877808671514674964848
Util.my_fpow(val, prime - 2, prime)
my_fpow/3Util模块中的方法:
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 中进行此类计算(不是大规模的,只是这样计算可以在超时之前实际完成),我也会感兴趣。
您的函数将递归 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