有没有办法提高在 Elixir 中生成具有非重复随机数的列表的速度?
Is there a way to improve speed of generating a list with non-duplicate random numbers in Elixir?
我正在寻找提高算法速度的可能性:
{k, _} = Integer.parse IO.gets("Amount of generated numbers? ")
{n, _} = Integer.parse IO.gets("What is the highest number which can be generated? ")
{:ok, time1} = DateTime.now("Etc/UTC")
numbers = Enum.to_list(1..n)
answer = Enum.reduce(1..k, [], fn _t, a -> [Enum.random(numbers -- a)] ++ a end)
{:ok, time2} = DateTime.now("Etc/UTC")
IO.puts("microsecond diff:")
IO.puts(DateTime.diff(time2, time1, :microsecond))
IO.inspect(answer, charlists: :as_lists)
我可以通过将随机数添加到数组 'a' 而不是追加来改进它,
此外,具有单独的变量 'numbers' 也提高了速度(这很明显,但最初我将 'Enum.to_list' 放入函数中)。
尽管如此,Elixir 算法比 Python 算法慢约 100 倍,后者做同样的事情(我测试了 k=999 和 n=10000):
import random
from datetime import datetime
k = int(input("Amount of generated numbers? "))
n = int(input("What is the highest number which can be generated? "))
time1 = datetime.now()
numbers = []
for i in range(1, n + 1):
numbers.append(i)
result = []
for i in range(1, k + 1):
d_my = random.random()
r = int(d_my * n)
result.append(numbers[r])
numbers[r] = numbers[n - 1]
n = n - 1
time2 = datetime.now()
print(time2 - time1)
# for r in result:
# print(r, end=" ")
我不认为 Elixir 算法应该和 Python 算法一样快,但是任何关于我如何改进第一个算法的想法都会受到赞赏。
我认为减慢您的 Elixir 示例的最大原因是您进行了多少枚举。在每次迭代中,你都在做一个昂贵的差异,然后是 Enum.random/1
,它再次迭代列表。
编辑:我将为他们提供更简单的示例和基准
我认为可以采用两种主要方法:
- 重复生成一个随机数,省略任何重复。
- 从一组剩余选项中选择随机数。
当您的 max
(我称您的 "highest number" 输入)远大于您的 count
(我称您的 "amount" 输入)。随着 count
接近 max
,选项 2 将赶上然后超过选项 1 的速度。
选项 1
这种方法保留了一组已经选择的号码,并在生成新号码时对其进行检查。决定性能的最大因素是碰撞。当 count
与 max
相比较小时,冲突的可能性较小,被丢弃的数字也较少。随着 count
越来越接近 max
,碰撞的可能性更大,并且大量时间被浪费在生成将被丢弃的数字上。
我原来的回答是这种方法的一个更复杂的例子。我们在下面的基准测试中对这种方法的拥护者可以相当简单。
defmodule RandUniq.Stream do
@behaviour RandUniq
@impl RandUniq
def take(count, max) do
fn -> :random.uniform(max) end
|> Stream.repeatedly()
|> Stream.uniq()
|> Enum.take(count)
end
end
选项 2
此方法的常见实现基于 max
创建一个新的随机数据结构,然后可以简单地从中获取 count
。这会产生大量的前期成本,因为每个元素都是随机放置的,与 max
相比,这对于较小的 count
来说是一种浪费。然而,大 count
和小 count
的工作基本相同,所以这就是这种方法的亮点。
我们将根据 Enum.take_random/2
和 Enum.shuffle/1
.
为这种方法包括几个拥护者
defmodule RandUniq.TakeRandom do
@behaviour RandUniq
@impl RandUniq
def take(count, max) do
Enum.take_random(1..max, count)
end
end
defmodule RandUniq.Shuffle do
@behaviour RandUniq
@impl RandUniq
def take(count, max) do
1..max
|> Enum.shuffle()
|> Enum.take(count)
end
end
控制
对于基准,我们也包括原始文件。
defmodule RandUniq.Control do
@behaviour RandUniq
@impl RandUniq
def take(count, max) do
numbers = Enum.to_list(1..max)
Enum.reduce(1..count, [], fn _t, a ->
[Enum.random(numbers -- a)] ++ a
end)
end
end
基准
这是它在我的机器上的表现(count / max
形式的输入):
##### With input 1000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 1404.78 0.71 ms ±17.04% 0.67 ms 1.07 ms
Elixir.RandUniq.TakeRandom 247.34 4.04 ms ±4.77% 3.99 ms 4.65 ms
Elixir.RandUniq.Shuffle 167.64 5.96 ms ±13.41% 5.83 ms 8.49 ms
Elixir.RandUniq.Control 0.33 3013.59 ms ±0.63% 3013.59 ms 3026.97 ms
Comparison:
Elixir.RandUniq.Stream 1404.78
Elixir.RandUniq.TakeRandom 247.34 - 5.68x slower +3.33 ms
Elixir.RandUniq.Shuffle 167.64 - 8.38x slower +5.25 ms
Elixir.RandUniq.Control 0.33 - 4233.43x slower +3012.87 ms
##### With input 2000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 718.81 1.39 ms ±9.76% 1.35 ms 1.90 ms
Elixir.RandUniq.TakeRandom 184.45 5.42 ms ±8.14% 5.33 ms 6.73 ms
Elixir.RandUniq.Shuffle 162.37 6.16 ms ±11.64% 6.03 ms 8.67 ms
Elixir.RandUniq.Control 0.170 5897.41 ms ±0.00% 5897.41 ms 5897.41 ms
Comparison:
Elixir.RandUniq.Stream 718.81
Elixir.RandUniq.TakeRandom 184.45 - 3.90x slower +4.03 ms
Elixir.RandUniq.Shuffle 162.37 - 4.43x slower +4.77 ms
Elixir.RandUniq.Control 0.170 - 4239.14x slower +5896.02 ms
##### With input 3000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 448.74 2.23 ms ±8.41% 2.20 ms 2.80 ms
Elixir.RandUniq.Shuffle 166.81 5.99 ms ±11.74% 5.86 ms 8.44 ms
Elixir.RandUniq.TakeRandom 162.07 6.17 ms ±5.27% 6.12 ms 7.18 ms
Elixir.RandUniq.Control 0.112 8951.54 ms ±0.00% 8951.54 ms 8951.54 ms
Comparison:
Elixir.RandUniq.Stream 448.74
Elixir.RandUniq.Shuffle 166.81 - 2.69x slower +3.77 ms
Elixir.RandUniq.TakeRandom 162.07 - 2.77x slower +3.94 ms
Elixir.RandUniq.Control 0.112 - 4016.91x slower +8949.31 ms
##### With input 4000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 293.83 3.40 ms ±9.04% 3.35 ms 4.39 ms
Elixir.RandUniq.Shuffle 173.81 5.75 ms ±10.26% 5.64 ms 7.54 ms
Elixir.RandUniq.TakeRandom 138.75 7.21 ms ±8.61% 7.11 ms 9.26 ms
Elixir.RandUniq.Control 0.0865 11566.12 ms ±0.00% 11566.12 ms 11566.12 ms
Comparison:
Elixir.RandUniq.Stream 293.83
Elixir.RandUniq.Shuffle 173.81 - 1.69x slower +2.35 ms
Elixir.RandUniq.TakeRandom 138.75 - 2.12x slower +3.80 ms
Elixir.RandUniq.Control 0.0865 - 3398.48x slower +11562.71 ms
##### With input 5000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 216.28 4.62 ms ±7.90% 4.60 ms 5.66 ms
Elixir.RandUniq.Shuffle 168.73 5.93 ms ±10.47% 5.83 ms 7.93 ms
Elixir.RandUniq.TakeRandom 126.83 7.88 ms ±6.92% 7.81 ms 9.17 ms
Elixir.RandUniq.Control 0.0687 14556.00 ms ±0.00% 14556.00 ms 14556.00 ms
Comparison:
Elixir.RandUniq.Stream 216.28
Elixir.RandUniq.Shuffle 168.73 - 1.28x slower +1.30 ms
Elixir.RandUniq.TakeRandom 126.83 - 1.71x slower +3.26 ms
Elixir.RandUniq.Control 0.0687 - 3148.10x slower +14551.37 ms
##### With input 6000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 176.27 5.67 ms ±6.76% 5.61 ms 6.80 ms
Elixir.RandUniq.Shuffle 172.27 5.80 ms ±9.31% 5.73 ms 7.34 ms
Elixir.RandUniq.TakeRandom 115.76 8.64 ms ±11.56% 8.66 ms 10.86 ms
Elixir.RandUniq.Control 0.0635 15740.88 ms ±0.00% 15740.88 ms 15740.88 ms
Comparison:
Elixir.RandUniq.Stream 176.27
Elixir.RandUniq.Shuffle 172.27 - 1.02x slower +0.132 ms
Elixir.RandUniq.TakeRandom 115.76 - 1.52x slower +2.97 ms
Elixir.RandUniq.Control 0.0635 - 2774.60x slower +15735.20 ms
##### With input 7000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Shuffle 171.13 5.84 ms ±10.26% 5.70 ms 7.77 ms
Elixir.RandUniq.Stream 140.55 7.11 ms ±9.16% 7.10 ms 8.84 ms
Elixir.RandUniq.TakeRandom 103.55 9.66 ms ±9.93% 9.92 ms 11.36 ms
Elixir.RandUniq.Control 0.0561 17837.14 ms ±0.00% 17837.14 ms 17837.14 ms
Comparison:
Elixir.RandUniq.Shuffle 171.13
Elixir.RandUniq.Stream 140.55 - 1.22x slower +1.27 ms
Elixir.RandUniq.TakeRandom 103.55 - 1.65x slower +3.81 ms
Elixir.RandUniq.Control 0.0561 - 3052.52x slower +17831.30 ms
##### With input 8000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Shuffle 172.44 5.80 ms ±9.72% 5.68 ms 7.46 ms
Elixir.RandUniq.Stream 109.34 9.15 ms ±10.45% 8.99 ms 11.37 ms
Elixir.RandUniq.TakeRandom 90.51 11.05 ms ±9.95% 11.11 ms 13.33 ms
Elixir.RandUniq.Control 0.0507 19712.01 ms ±0.00% 19712.01 ms 19712.01 ms
Comparison:
Elixir.RandUniq.Shuffle 172.44
Elixir.RandUniq.Stream 109.34 - 1.58x slower +3.35 ms
Elixir.RandUniq.TakeRandom 90.51 - 1.91x slower +5.25 ms
Elixir.RandUniq.Control 0.0507 - 3399.20x slower +19706.22 ms
##### With input 9000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Shuffle 163.33 6.12 ms ±10.87% 6.01 ms 8.20 ms
Elixir.RandUniq.TakeRandom 87.59 11.42 ms ±12.70% 11.59 ms 14.92 ms
Elixir.RandUniq.Stream 77.42 12.92 ms ±7.04% 12.91 ms 15.04 ms
Elixir.RandUniq.Control 0.0458 21839.19 ms ±0.00% 21839.19 ms 21839.19 ms
Comparison:
Elixir.RandUniq.Shuffle 163.33
Elixir.RandUniq.TakeRandom 87.59 - 1.86x slower +5.29 ms
Elixir.RandUniq.Stream 77.42 - 2.11x slower +6.79 ms
Elixir.RandUniq.Control 0.0458 - 3567.03x slower +21833.07 ms
##### With input 10000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Shuffle 152.63 6.55 ms ±15.19% 6.35 ms 11.46 ms
Elixir.RandUniq.TakeRandom 99.66 10.03 ms ±13.20% 9.72 ms 13.49 ms
Elixir.RandUniq.Stream 26.56 37.65 ms ±11.88% 36.73 ms 55.84 ms
Elixir.RandUniq.Control 0.0420 23822.66 ms ±0.00% 23822.66 ms 23822.66 ms
Comparison:
Elixir.RandUniq.Shuffle 152.63
Elixir.RandUniq.TakeRandom 99.66 - 1.53x slower +3.48 ms
Elixir.RandUniq.Stream 26.56 - 5.75x slower +31.10 ms
Elixir.RandUniq.Control 0.0420 - 3635.98x slower +23816.10 ms
似乎 RandUniq.Stream
,我们的选项 1 冠军,保持最快,直到 count
达到 max
的大约 60-70%(至少在这个规模上)。然后 RandUniq.Shuffle
,基于@zwippie 的回答,明显领先。
怎么样:
(1..n) |> Enum.shuffle |> Enum.take(k)
似乎执行得相当快。
更新:
我已经对这个解决方案进行了基准测试,并将其与 Enum.take_random
性能进行了比较。后者快一倍:
Name ips average deviation median 99th %
take_random 507.13 1.97 ms ±4.22% 1.96 ms 2.37 ms
shuffle_take 251.84 3.97 ms ±7.76% 3.92 ms 4.83 ms
Comparison:
take_random 507.13
shuffle_take 251.84 - 2.01x slower +2.00 ms
我正在寻找提高算法速度的可能性:
{k, _} = Integer.parse IO.gets("Amount of generated numbers? ")
{n, _} = Integer.parse IO.gets("What is the highest number which can be generated? ")
{:ok, time1} = DateTime.now("Etc/UTC")
numbers = Enum.to_list(1..n)
answer = Enum.reduce(1..k, [], fn _t, a -> [Enum.random(numbers -- a)] ++ a end)
{:ok, time2} = DateTime.now("Etc/UTC")
IO.puts("microsecond diff:")
IO.puts(DateTime.diff(time2, time1, :microsecond))
IO.inspect(answer, charlists: :as_lists)
我可以通过将随机数添加到数组 'a' 而不是追加来改进它, 此外,具有单独的变量 'numbers' 也提高了速度(这很明显,但最初我将 'Enum.to_list' 放入函数中)。 尽管如此,Elixir 算法比 Python 算法慢约 100 倍,后者做同样的事情(我测试了 k=999 和 n=10000):
import random
from datetime import datetime
k = int(input("Amount of generated numbers? "))
n = int(input("What is the highest number which can be generated? "))
time1 = datetime.now()
numbers = []
for i in range(1, n + 1):
numbers.append(i)
result = []
for i in range(1, k + 1):
d_my = random.random()
r = int(d_my * n)
result.append(numbers[r])
numbers[r] = numbers[n - 1]
n = n - 1
time2 = datetime.now()
print(time2 - time1)
# for r in result:
# print(r, end=" ")
我不认为 Elixir 算法应该和 Python 算法一样快,但是任何关于我如何改进第一个算法的想法都会受到赞赏。
我认为减慢您的 Elixir 示例的最大原因是您进行了多少枚举。在每次迭代中,你都在做一个昂贵的差异,然后是 Enum.random/1
,它再次迭代列表。
编辑:我将为他们提供更简单的示例和基准
我认为可以采用两种主要方法:
- 重复生成一个随机数,省略任何重复。
- 从一组剩余选项中选择随机数。
当您的 max
(我称您的 "highest number" 输入)远大于您的 count
(我称您的 "amount" 输入)。随着 count
接近 max
,选项 2 将赶上然后超过选项 1 的速度。
选项 1
这种方法保留了一组已经选择的号码,并在生成新号码时对其进行检查。决定性能的最大因素是碰撞。当 count
与 max
相比较小时,冲突的可能性较小,被丢弃的数字也较少。随着 count
越来越接近 max
,碰撞的可能性更大,并且大量时间被浪费在生成将被丢弃的数字上。
我原来的回答是这种方法的一个更复杂的例子。我们在下面的基准测试中对这种方法的拥护者可以相当简单。
defmodule RandUniq.Stream do
@behaviour RandUniq
@impl RandUniq
def take(count, max) do
fn -> :random.uniform(max) end
|> Stream.repeatedly()
|> Stream.uniq()
|> Enum.take(count)
end
end
选项 2
此方法的常见实现基于 max
创建一个新的随机数据结构,然后可以简单地从中获取 count
。这会产生大量的前期成本,因为每个元素都是随机放置的,与 max
相比,这对于较小的 count
来说是一种浪费。然而,大 count
和小 count
的工作基本相同,所以这就是这种方法的亮点。
我们将根据 Enum.take_random/2
和 Enum.shuffle/1
.
defmodule RandUniq.TakeRandom do
@behaviour RandUniq
@impl RandUniq
def take(count, max) do
Enum.take_random(1..max, count)
end
end
defmodule RandUniq.Shuffle do
@behaviour RandUniq
@impl RandUniq
def take(count, max) do
1..max
|> Enum.shuffle()
|> Enum.take(count)
end
end
控制
对于基准,我们也包括原始文件。
defmodule RandUniq.Control do
@behaviour RandUniq
@impl RandUniq
def take(count, max) do
numbers = Enum.to_list(1..max)
Enum.reduce(1..count, [], fn _t, a ->
[Enum.random(numbers -- a)] ++ a
end)
end
end
基准
这是它在我的机器上的表现(count / max
形式的输入):
##### With input 1000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 1404.78 0.71 ms ±17.04% 0.67 ms 1.07 ms
Elixir.RandUniq.TakeRandom 247.34 4.04 ms ±4.77% 3.99 ms 4.65 ms
Elixir.RandUniq.Shuffle 167.64 5.96 ms ±13.41% 5.83 ms 8.49 ms
Elixir.RandUniq.Control 0.33 3013.59 ms ±0.63% 3013.59 ms 3026.97 ms
Comparison:
Elixir.RandUniq.Stream 1404.78
Elixir.RandUniq.TakeRandom 247.34 - 5.68x slower +3.33 ms
Elixir.RandUniq.Shuffle 167.64 - 8.38x slower +5.25 ms
Elixir.RandUniq.Control 0.33 - 4233.43x slower +3012.87 ms
##### With input 2000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 718.81 1.39 ms ±9.76% 1.35 ms 1.90 ms
Elixir.RandUniq.TakeRandom 184.45 5.42 ms ±8.14% 5.33 ms 6.73 ms
Elixir.RandUniq.Shuffle 162.37 6.16 ms ±11.64% 6.03 ms 8.67 ms
Elixir.RandUniq.Control 0.170 5897.41 ms ±0.00% 5897.41 ms 5897.41 ms
Comparison:
Elixir.RandUniq.Stream 718.81
Elixir.RandUniq.TakeRandom 184.45 - 3.90x slower +4.03 ms
Elixir.RandUniq.Shuffle 162.37 - 4.43x slower +4.77 ms
Elixir.RandUniq.Control 0.170 - 4239.14x slower +5896.02 ms
##### With input 3000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 448.74 2.23 ms ±8.41% 2.20 ms 2.80 ms
Elixir.RandUniq.Shuffle 166.81 5.99 ms ±11.74% 5.86 ms 8.44 ms
Elixir.RandUniq.TakeRandom 162.07 6.17 ms ±5.27% 6.12 ms 7.18 ms
Elixir.RandUniq.Control 0.112 8951.54 ms ±0.00% 8951.54 ms 8951.54 ms
Comparison:
Elixir.RandUniq.Stream 448.74
Elixir.RandUniq.Shuffle 166.81 - 2.69x slower +3.77 ms
Elixir.RandUniq.TakeRandom 162.07 - 2.77x slower +3.94 ms
Elixir.RandUniq.Control 0.112 - 4016.91x slower +8949.31 ms
##### With input 4000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 293.83 3.40 ms ±9.04% 3.35 ms 4.39 ms
Elixir.RandUniq.Shuffle 173.81 5.75 ms ±10.26% 5.64 ms 7.54 ms
Elixir.RandUniq.TakeRandom 138.75 7.21 ms ±8.61% 7.11 ms 9.26 ms
Elixir.RandUniq.Control 0.0865 11566.12 ms ±0.00% 11566.12 ms 11566.12 ms
Comparison:
Elixir.RandUniq.Stream 293.83
Elixir.RandUniq.Shuffle 173.81 - 1.69x slower +2.35 ms
Elixir.RandUniq.TakeRandom 138.75 - 2.12x slower +3.80 ms
Elixir.RandUniq.Control 0.0865 - 3398.48x slower +11562.71 ms
##### With input 5000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 216.28 4.62 ms ±7.90% 4.60 ms 5.66 ms
Elixir.RandUniq.Shuffle 168.73 5.93 ms ±10.47% 5.83 ms 7.93 ms
Elixir.RandUniq.TakeRandom 126.83 7.88 ms ±6.92% 7.81 ms 9.17 ms
Elixir.RandUniq.Control 0.0687 14556.00 ms ±0.00% 14556.00 ms 14556.00 ms
Comparison:
Elixir.RandUniq.Stream 216.28
Elixir.RandUniq.Shuffle 168.73 - 1.28x slower +1.30 ms
Elixir.RandUniq.TakeRandom 126.83 - 1.71x slower +3.26 ms
Elixir.RandUniq.Control 0.0687 - 3148.10x slower +14551.37 ms
##### With input 6000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Stream 176.27 5.67 ms ±6.76% 5.61 ms 6.80 ms
Elixir.RandUniq.Shuffle 172.27 5.80 ms ±9.31% 5.73 ms 7.34 ms
Elixir.RandUniq.TakeRandom 115.76 8.64 ms ±11.56% 8.66 ms 10.86 ms
Elixir.RandUniq.Control 0.0635 15740.88 ms ±0.00% 15740.88 ms 15740.88 ms
Comparison:
Elixir.RandUniq.Stream 176.27
Elixir.RandUniq.Shuffle 172.27 - 1.02x slower +0.132 ms
Elixir.RandUniq.TakeRandom 115.76 - 1.52x slower +2.97 ms
Elixir.RandUniq.Control 0.0635 - 2774.60x slower +15735.20 ms
##### With input 7000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Shuffle 171.13 5.84 ms ±10.26% 5.70 ms 7.77 ms
Elixir.RandUniq.Stream 140.55 7.11 ms ±9.16% 7.10 ms 8.84 ms
Elixir.RandUniq.TakeRandom 103.55 9.66 ms ±9.93% 9.92 ms 11.36 ms
Elixir.RandUniq.Control 0.0561 17837.14 ms ±0.00% 17837.14 ms 17837.14 ms
Comparison:
Elixir.RandUniq.Shuffle 171.13
Elixir.RandUniq.Stream 140.55 - 1.22x slower +1.27 ms
Elixir.RandUniq.TakeRandom 103.55 - 1.65x slower +3.81 ms
Elixir.RandUniq.Control 0.0561 - 3052.52x slower +17831.30 ms
##### With input 8000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Shuffle 172.44 5.80 ms ±9.72% 5.68 ms 7.46 ms
Elixir.RandUniq.Stream 109.34 9.15 ms ±10.45% 8.99 ms 11.37 ms
Elixir.RandUniq.TakeRandom 90.51 11.05 ms ±9.95% 11.11 ms 13.33 ms
Elixir.RandUniq.Control 0.0507 19712.01 ms ±0.00% 19712.01 ms 19712.01 ms
Comparison:
Elixir.RandUniq.Shuffle 172.44
Elixir.RandUniq.Stream 109.34 - 1.58x slower +3.35 ms
Elixir.RandUniq.TakeRandom 90.51 - 1.91x slower +5.25 ms
Elixir.RandUniq.Control 0.0507 - 3399.20x slower +19706.22 ms
##### With input 9000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Shuffle 163.33 6.12 ms ±10.87% 6.01 ms 8.20 ms
Elixir.RandUniq.TakeRandom 87.59 11.42 ms ±12.70% 11.59 ms 14.92 ms
Elixir.RandUniq.Stream 77.42 12.92 ms ±7.04% 12.91 ms 15.04 ms
Elixir.RandUniq.Control 0.0458 21839.19 ms ±0.00% 21839.19 ms 21839.19 ms
Comparison:
Elixir.RandUniq.Shuffle 163.33
Elixir.RandUniq.TakeRandom 87.59 - 1.86x slower +5.29 ms
Elixir.RandUniq.Stream 77.42 - 2.11x slower +6.79 ms
Elixir.RandUniq.Control 0.0458 - 3567.03x slower +21833.07 ms
##### With input 10000 / 10000 #####
Name ips average deviation median 99th %
Elixir.RandUniq.Shuffle 152.63 6.55 ms ±15.19% 6.35 ms 11.46 ms
Elixir.RandUniq.TakeRandom 99.66 10.03 ms ±13.20% 9.72 ms 13.49 ms
Elixir.RandUniq.Stream 26.56 37.65 ms ±11.88% 36.73 ms 55.84 ms
Elixir.RandUniq.Control 0.0420 23822.66 ms ±0.00% 23822.66 ms 23822.66 ms
Comparison:
Elixir.RandUniq.Shuffle 152.63
Elixir.RandUniq.TakeRandom 99.66 - 1.53x slower +3.48 ms
Elixir.RandUniq.Stream 26.56 - 5.75x slower +31.10 ms
Elixir.RandUniq.Control 0.0420 - 3635.98x slower +23816.10 ms
似乎 RandUniq.Stream
,我们的选项 1 冠军,保持最快,直到 count
达到 max
的大约 60-70%(至少在这个规模上)。然后 RandUniq.Shuffle
,基于@zwippie 的回答,明显领先。
怎么样:
(1..n) |> Enum.shuffle |> Enum.take(k)
似乎执行得相当快。
更新:
我已经对这个解决方案进行了基准测试,并将其与 Enum.take_random
性能进行了比较。后者快一倍:
Name ips average deviation median 99th %
take_random 507.13 1.97 ms ±4.22% 1.96 ms 2.37 ms
shuffle_take 251.84 3.97 ms ±7.76% 3.92 ms 4.83 ms
Comparison:
take_random 507.13
shuffle_take 251.84 - 2.01x slower +2.00 ms