为什么 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