13、Elixir 中的单元测试与结构化数据存储

Elixir 中的单元测试与结构化数据存储

1. 编写单元测试

在 Elixir 中,除了进行静态分析和为函数定义 @specs 外,提前对代码进行充分测试也能避免一些调试工作。Elixir 提供了一个名为 ExUnit 的单元测试模块,方便我们进行测试。

为了演示 ExUnit 的使用,我们使用 Mix 创建一个名为 drop 的新项目。在 lib/drop.ex 文件中,我们编写了一个存在错误的 Drop 模块,其中火星的重力常数被误输入为 3.41,而正确值应为 3.71:

defmodule Drop do
  def fall_velocity(planemo, distance) do
    gravity = case planemo do
      :earth -> 9.8
      :moon -> 1.6
      :mars -> 3.41
    end
    :math.sqrt(2 * gravity * distance)
  end
end

Mix 除了创建 lib 目录外,还会创建一个 test 目录。在该目录下,我们可以找到两个扩展名为 .exs 的文件: test_helper.exs drop_test.exs .exs 扩展名表示这些是脚本文件,无需编译。 test_helper.exs 文件会设置 ExUnit 自动运行,我们可以在 drop_test.exs 文件中使用 test 宏来定义测试:

defmodule DropTest do
  use ExUnit.Case, async: true
  test "Zero distance gives zero velocity" do
    assert Drop.fall_velocity(:earth,0) == 0
  end
  test "Mars calculation correct" do
    assert Drop.fall_velocity(:mars, 10) == :math.sqrt(2 * 3.71 * 10)
  end
end

use 行允许 Elixir 并行运行测试用例。一个测试以 test 宏和描述测试的字符串开头,测试内容包括执行一些代码并断言某个条件。如果执行代码的结果为 true ,则测试通过;如果结果为 false ,则测试失败。

要运行测试,在命令行中输入 mix test

$ mix test
Compiling 1 file (.ex)
Generated drop app
.
  1) test Mars calculation correct (DropTest)
     test/drop_test.exs:8
     Assertion with == failed
     code: Drop.fall_velocity(:mars, 10) == :math.sqrt(2 * 3.71 * 10)
     lhs:  8.258329128825032
     rhs:  8.613942186943213
     stacktrace:
       test/drop_test.exs:9: (test)
Finished in 0.06 seconds
2 tests, 1 failure
Randomized with seed 585665

. 开头的行表示每个测试的状态, . 表示测试成功。我们可以进入 Drop 模块,将火星的重力常数更改为正确值 3.71,然后再次运行测试,就会看到成功的测试结果:

$ mix test
Compiling 1 file (.ex)
..
Finished in 0.05 seconds
2 tests, 0 failures
Randomized with seed 811304

除了 assert/1 ,我们还可以使用 refute/1 ,它期望测试的条件为 false 时测试才会通过。 assert/1 refute/1 都会自动生成合适的消息,这两个函数还有双参数版本,允许我们指定断言或反驳失败时要产生的消息。

如果使用浮点运算,可能无法保证得到精确结果,这时可以使用 assert_in_delta/4 函数。它的四个参数分别是期望值、实际收到的值、允许的误差范围和消息。如果期望值和实际值在误差范围内,则测试通过;否则,测试失败, ExUnit 会打印我们指定的消息。以下是一个测试,检查从地球 1 米高处下落的速度是否接近每秒 4.4 米:

defmodule Drop2Test do
  use ExUnit.Case, async: true
  test "Earth calculation correct" do
    calculated = Drop.fall_velocity(:earth, 1)
    assert_in_delta calculated, 4.4, 0.05,
      "Result of #{calculated} is not within 0.05 of 4.4"
  end
end

如果想看到失败消息,可以添加一个要求计算更精确的新测试:

defmodule Drop3Test do
  use ExUnit.Case, async: true
  test "Earth calculation correct" do
    calculated = Drop.fall_velocity(:earth, 1)
    assert_in_delta calculated, 4.4, 0.0001,
      "Result of #{calculated} is not within 0.0001 of 4.4"
  end
end

运行结果如下:

$ mix test
..
  1) test Earth calculation correct (Drop3Test)
     test/drop3_test.exs:4
     Result of 4.427188724235731 is not within 0.0001 of 4.4
     stacktrace:
       test/drop3_test.exs:6: (test)
.
Finished in 0.08 seconds
4 tests, 1 failure
Randomized with seed 477713

我们还可以测试代码的某些部分是否能正确抛出异常。以下两个测试将检查不正确的行星名称和负距离是否会导致错误。在每个测试中,我们将需要测试的代码包装在一个匿名函数中:

defmodule Drop4Test do
  use ExUnit.Case, async: true
  test "Unknown planemo causes error" do
    assert_raise CaseClauseError, fn ->
      Drop.fall_velocity(:planetX, 10)
    end
  end
  test "Negative distance causes error" do
    assert_raise ArithmeticError, fn ->
      Drop.fall_velocity(:earth, -10)
    end
  end
end
2. 设置测试

我们还可以指定在每个测试前后以及所有测试开始前和结束后运行的代码。例如,在进行任何测试之前连接到服务器,测试结束后断开连接。

要指定在所有测试开始前运行的代码,可以使用 setup_all 回调。这个回调应该返回 :ok ,还可以选择返回一个关键字列表,该列表会被添加到测试上下文中,测试上下文中的 Elixir 映射可以在测试中访问:

setup_all do
  IO.puts "Beginning all tests"
  on_exit fn ->
    IO.puts "Exit from all tests"
  end 
  {:ok, [connection: :fake_PID]}
end

这段代码为测试上下文添加了一个 :connection 关键字, on_exit 指定了所有测试结束后要运行的代码。

要指定在每个单独测试前后运行的代码,可以使用 setup 。这段代码可以访问上下文:

setup context do
  IO.puts "About to start a test. Connection is #{Map.get(context, :connection)}"

  on_exit fn ->
    IO.puts "Individual test complete."
  end

  :ok
end

最后,我们可以在单个测试中访问上下文:

test "Zero distance gives zero velocity", context do
  IO.puts "In zero distance test. Connection is #{Map.get(context, :connection)}"
  assert Drop.fall_velocity(:earth,0) == 0
end

运行测试的结果如下,我们将 async 设置为 false ,以便按顺序查看所有输出。如果将 async 设置为 true ,测试将并行运行,输出顺序可能不太容易确定:

$ mix test
Beginning all tests
About to start a test. Connection is fake_PID
In zero distance test, connection is fake_PID
Individual test complete.
.About to start a test. Connection is fake_PID
Test two
Individual test complete.
.Exit from all tests
Finished in -0.08 seconds
2 tests, 0 failures
Randomized with seed 519579
3. 将测试嵌入文档

还有一种测试方法是将测试嵌入函数和模块的文档中,这称为 doctest 。测试脚本如下:

defmodule DropTest do
  use ExUnit.Case, async: false
  doctest Drop
end

doctest 后面是要测试的模块名称。 doctest 会在模块的文档中查找看起来像 IEx 命令和输出的行。这些行以 iex> iex(n)> 开头( n 是一个数字),下一行是预期输出。空行表示一个新测试的开始。以下是一个示例:

defmodule Drop do
  @doc  """
  Calculates speed of a falling object on a given planemo
  (planetary mass object)

    iex(1)> Drop.fall_velocity(:earth, 10)
    14.0

    iex(2)> Drop.fall_velocity(:mars, 20)
    12.181953866272849

    iex> Drop.fall_velocity(:jupiter, 10)
    ** (CaseClauseError) no case clause matching: :jupiter
  """
  def fall_velocity(planemo, distance) do
    gravity = case planemo do
      :earth -> 9.8
      :moon -> 1.6
      :mars -> 3.71
    end
    :math.sqrt(2 * gravity * distance)
  end
end

Elixir 的测试工具还允许我们测试是否收到消息、编写可在测试之间共享的函数等,更多详细信息可在 Elixir 文档中找到。

4. 记录:结构体出现之前的结构化数据

Elixir 的结构体允许我们使用名称而不是顺序(如元组)来关联数据,但结构体是基于映射的,而映射在 Erlang 和 Elixir 中是新特性。在映射出现之前,Erlang 为解决存储结构化数据的问题引入了记录的概念。与结构体一样,我们可以在记录中读写和模式匹配数据,而不必担心字段在元组中的位置或是否有人添加了新字段。

记录在 Erlang 中并不特别受欢迎,在 Elixir 中虽受支持但不被鼓励,因为记录定义的要求会带来一些麻烦。不过,记录在 Erlang API 中很常见,并且运行效率高,所以仍然值得了解。需要注意的是,记录底层仍然是元组,Elixir 偶尔会将其暴露出来,但不要直接使用元组表示,否则会增加使用元组的潜在问题。

5. 设置记录

使用记录需要通过特殊声明让 Elixir 知道它们。我们不使用 defmodule ,而是使用 defrecord 声明:

defrecord Planemo, name: :nil, gravity: 0, diameter: 0, distance_from_sun: 0

这定义了一个名为 Planemo 的记录类型,包含 name gravity diameter distance_from_sun 字段,并设置了默认值。以下声明创建了用于下落物体的不同塔的记录:

defrecord Tower, location: "", height: 20, planemo: :earth, name: ""

defmodule 声明不同,我们通常希望在多个模块之间共享记录声明,甚至在 shell 中使用。为了可靠地共享记录声明,可以将记录声明放在扩展名为 .ex 的文件中。可以根据需要将每个记录声明放在单独的文件中,也可以将它们放在同一个文件中。为了开始了解记录的行为,可以将两个声明放在同一个文件 records.ex 中:

defmodule Planemo do
  require Record
  Record.defrecord :planemo, [name: :nil, gravity: 0, diameter: 0,
  distance_from_sun: 0]
end
defmodule Tower do
  require Record
  Record.defrecord :tower, Tower,
    [location: "", height: 20, planemo: :earth, name: ""]
end

Record.defrecord 会构建一组宏来创建和访问记录。 Record.defrecord 后面的第一个项是记录名称,第二个项是可选的标签。如果不提供标签,Elixir 会使用记录名称。在这个例子中,我们为 Tower 记录提供了标签,但没有为 Planemo 记录提供标签。名称和可选标签后面是一个列表,列出了键名和默认值对。

Elixir 会自动在模块中构建函数,用于创建新记录、访问记录值和更新记录值。因为记录是模块,所以要使记录在程序中可用,只需确保它已编译并与其他模块在同一目录中。可以使用命令行中的 elixirc 程序编译 defrecord 声明,也可以在使用 iex -S mix 启动时让 Mix 进行编译。

shell 现在可以识别 Planemo Tower 记录,但要在程序或 shell 中使用它们,必须先引入它们。我们也可以直接在 shell 中输入 defrecord 声明来声明记录,但如果不只是简单尝试,将记录放在外部文件中会更方便。

6. 创建和读取记录

现在可以创建包含新记录的变量。通过使用记录名称函数来创建新记录:

iex(1)> require Tower
Tower
iex(2)> tower1 = Tower.tower()
{Tower, "", 20, :earth, ""}
iex(3)> tower2 = Tower.tower(location: "Grand Canyon")
{Tower, "Grand Canyon", 20, :earth, ""}
iex(4)> tower3 = Tower.tower(location: "NYC", height: 241,
...(4)> name: "Woolworth Building")
{Tower, "NYC", 241, :earth, "Woolworth Building"}
iex(5)> tower4 = Tower.tower location: "Rupes Altat 241", height: 500,
...(5)> planemo: :moon, name: "Piccolini View"
{Tower, "Rupes Altat 241", 500, :moon, "Piccolini View"}
iex(6)> tower5 = Tower.tower planemo: :mars, height: 500,
...(6)> name: "Daga Vallis", location: "Valles Marineris"
{Tower, "Valles Marineris", 500, :mars, "Daga Vallis"}

这些塔(至少是下落地点)展示了使用记录语法创建变量的多种方式以及与默认值的交互:
- 第 2 行使用默认值创建 tower1 ,之后可以添加实际值。
- 第 3 行创建一个有位置信息的塔,其他方面依赖默认值。
- 第 4 行覆盖了位置、高度和名称的默认值,但保留了 planemo 的默认值。
- 第 5 行用新值替换了所有默认值,并且和 Elixir 的常规用法一样,不需要将新参数放在括号内。
- 第 6 行替换了所有默认值,还表明列出名称/值对的顺序无关紧要,Elixir 会处理好。

有两种不同的方法可以读取记录条目。要提取单个值,可以使用点( . )语法,这在其他语言中可能很熟悉。例如,要找出 tower5 所在的行星,可以这样写:

iex(7)> Tower.tower(tower5, :planemo)
:mars
iex(8)> import Tower
nil
iex(9)> tower(tower5, :height)
500

第 8 行进行了导入操作,使第 9 行不再需要 Tower

如果想更改记录中的值,可以这样做。在以下示例中,右侧实际上返回了一个全新的记录,并将这个新记录重新绑定到 tower5 ,覆盖了它的旧值:

iex(10)> tower5
{Tower, "Valles Marineris", 500, :mars, "Daga Vallis"}
iex(11)> tower5 = tower(tower5, height: 512)
{Tower, "Valles Marineris", 512, :mars, "Daga Vallis"}
7. 在函数中使用记录

我们可以对作为参数提交的记录进行模式匹配。最简单的方法是只匹配记录的类型,如下所示:

defmodule RecordDrop do
  require Planemo
  require Tower
  def fall_velocity(t = Tower.tower()) do
    fall_velocity(Tower.tower(t, :planemo), Tower.tower(t, :height))
  end
  def fall_velocity(:earth, distance) when distance >= 0 do
    :math.sqrt(2 * 9.8 * distance)
  end
  def fall_velocity(:moon, distance) when distance >= 0 do
    :math.sqrt(2 * 1.6 * distance)
  end
  def fall_velocity(:mars, distance) when distance >= 0 do
    :math.sqrt(2 * 3.71 * distance)
  end
end

这段代码使用模式匹配,只匹配 Tower 记录,并将记录放入变量 t 中。然后,像之前的示例一样,将各个参数传递给 fall_velocity/2 进行计算,这次使用了记录语法。

iex(12)> r(RecordDrop)
warning: redefining module RecordDrop (current version loaded from
  _build/dev/lib/record_drop/ebin/Elixir.RecordDrop.beam)
  lib/record_drop.ex:1
{:reloaded, RecordDrop, [RecordDrop]}
iex(13)> RecordDrop.fall_velocity(tower5)
60.909769331364245
iex(14)> RecordDrop.fall_velocity(tower1)
19.79898987322333

示例中的 record_drop:fall_velocity/1 函数会提取 planemo 字段并将其绑定到变量 planemo ,提取高度字段并将其绑定到 distance ,然后返回从该高度下落的物体的速度。

我们还可以在模式匹配中从记录中提取特定字段:

defmodule RecordDrop do
  require Tower
  def fall_velocity(Tower.tower(planemo: planemo, height: distance)) do
    fall_velocity(planemo, distance)
  end
  def fall_velocity(:earth, distance) when distance >= 0 do
    :math.sqrt(2 * 9.8 * distance)
  end
  def fall_velocity(:moon, distance) when distance >= 0 do
    :math.sqrt(2 * 1.6 * distance)
  end
  def fall_velocity(:mars, distance) when distance >= 0 do
    :math.sqrt(2 * 3.71 * distance)
  end
end

可以将创建的记录输入到这个函数中,它会告诉我们从塔顶部到底部下落的速度。

最后,我们可以同时对字段和整个记录进行模式匹配:

defmodule RecordDrop do
  require Tower
  import Tower
  def fall_velocity(t = tower(planemo: planemo, height: distance)) do
    IO.puts("From #{tower(t, :name)}'s elevation of #{distance} meters on #{planemo},")
    IO.puts("the object will reach #{fall_velocity(planemo, distance)} m/s")
    IO.puts("before crashing in #{tower(t, :location)}")
  end
  def fall_velocity(:earth, distance) when distance >= 0 do
    :math.sqrt(2 * 9.8 * distance)
  end
  def fall_velocity(:moon, distance) when distance >= 0 do
    :math.sqrt(2 * 1.6 * distance)
  end
  def fall_velocity(:mars, distance) when distance >= 0 do
    :math.sqrt(2 * 3.71 * distance)
  end
end

在之前的示例中, planemo 字段被赋值给了一个同名的变量。如果将 Tower 记录传递给 RecordDrop.fall_velocity/1 ,它会匹配进行计算所需的各个字段,并将整个记录匹配到 t 中,以便生成更详细的报告:

iex(15)> RecordDrop.fall_velocity(tower5)
From Daga Vallis's elevation of 500 meters on mars,
the object will reach 60.90976933136424520399 m/s
before crashing in Valles Marineris
:ok
iex(16)> RecordDrop.fall_velocity(tower3)
From Woolworth Building's elevation of 241 meters on earth,
the object will reach 68.72845116834803036454 m/s
before crashing in NYC
:ok
8. 在 Erlang 术语存储中存储数据

Erlang 术语存储(ETS)是一个简单但强大的内存集合存储。它存储元组,由于记录底层也是元组,所以记录很适合存储在 ETS 中。ETS 及其基于磁盘的兄弟 DETS 为许多数据管理问题提供了(可能过于)简单的解决方案。ETS 不完全是一个数据库,但它做的工作类似,并且本身很有用,也是 Mnesia 数据库的底层基础。

ETS 表中的每个条目都是一个元组(或对应的记录),元组中的一部分被指定为键。根据处理键的方式,ETS 提供了几种不同的结构选择。ETS 可以存储四种类型的集合:
| 集合类型 | 描述 |
| ---- | ---- |
| 集合(:set) | 给定键只能有一个条目,这是默认类型。 |
| 有序集合(:ordered_set) | 与集合相同,但还会根据键维护遍历顺序,适合需要按字母或数字顺序保存的任何内容。 |
| 包(:bag) | 允许使用给定键存储多个条目,但如果有多个条目值完全相同,它们会合并为一个条目。 |
| 重复包(:duplicate_bag) | 不仅允许使用给定键存储多个条目,还允许存储多个值完全相同的条目。 |

默认情况下,ETS 表是集合类型,但在创建表时可以指定其他选项。这里的示例将使用集合,因为它们更容易理解,但相同的技术适用于所有四种表类型。

ETS 不要求所有条目看起来都相似,但在开始时,使用相同类型的记录或至少具有相同结构的元组会更简单。键可以使用任何类型的值,包括复杂的元组结构和列表,但开始时最好不要过于复杂。

以下部分的所有示例将使用前面定义的 planemo 记录类型和下表中的数据:
| Planemo | Gravity (m/s²) | Diameter (km) | Distance from Sun (10⁶ km) |
| ---- | ---- | ---- | ---- |
| mercury | 3.7 | 4878 | 57.9 |
| venus | 8.9 | 12104 | 108.2 |

Elixir 中的单元测试与结构化数据存储

9. 创建和操作 ETS 表

要使用 ETS 表,首先需要创建它。可以使用 :ets.new/2 函数来创建一个新的 ETS 表。以下是创建一个名为 planemo_table 的集合类型 ETS 表的示例:

iex(1)> planemo_table = :ets.new(:planemo_table, [:set])
#Reference<0.2665402240.2777180162.209368>

这里, :planemo_table 是表的名称, [:set] 指定了表的类型为集合。

接下来,可以向 ETS 表中插入数据。假设我们有之前提到的 planemo 记录,以下是插入水星和金星数据的示例:

iex(2)> require Planemo
Planemo
iex(3)> mercury = Planemo.planemo(name: :mercury, gravity: 3.7, diameter: 4878, distance_from_sun: 57.9)
{Planemo, :mercury, 3.7, 4878, 57.9}
iex(4)> venus = Planemo.planemo(name: :venus, gravity: 8.9, diameter: 12104, distance_from_sun: 108.2)
{Planemo, :venus, 8.9, 12104, 108.2}
iex(5)> :ets.insert(planemo_table, mercury)
true
iex(6)> :ets.insert(planemo_table, venus)
true

:ets.insert/2 函数用于将记录插入到 ETS 表中。

要从 ETS 表中查找数据,可以使用 :ets.lookup/2 函数。例如,查找水星的数据:

iex(7)> :ets.lookup(planemo_table, :mercury)
[{Planemo, :mercury, 3.7, 4878, 57.9}]

:ets.lookup/2 函数接受表名和键作为参数,返回匹配的记录列表。

如果要删除 ETS 表中的数据,可以使用 :ets.delete/2 函数。例如,删除水星的数据:

iex(8)> :ets.delete(planemo_table, :mercury)
true
iex(9)> :ets.lookup(planemo_table, :mercury)
[]

:ets.delete/2 函数接受表名和键作为参数,删除匹配的记录。

10. ETS 表的遍历

ETS 表提供了几种遍历数据的方法。对于有序集合,可以使用 :ets.first/1 :ets.next/2 函数按顺序遍历数据。以下是一个遍历 planemo_table 中所有记录的示例:

iex(10)> first_key = :ets.first(planemo_table)
:venus
iex(11)> loop = fn
...(11)>   key when key != :"$end_of_table" ->
...(11)>     record = :ets.lookup(planemo_table, key)
...(11)>     IO.inspect(record)
...(11)>     next_key = :ets.next(planemo_table, key)
...(11)>     loop.(next_key)
...(11)>   _ ->
...(11)>     :ok
...(11)> end
#Function<45.97283095/1 in :erl_eval.expr/5>
iex(12)> loop.(first_key)
[{Planemo, :venus, 8.9, 12104, 108.2}]
:ok

这里使用了递归函数 loop 来遍历表中的所有记录。

11. ETS 表的性能考虑

ETS 表是内存中的数据存储,因此在使用时需要考虑内存的使用情况。由于 ETS 表可以存储大量数据,当数据量非常大时,可能会导致内存占用过高。

另外,ETS 表的读写操作速度非常快,但对于大量的写入操作,可能会影响性能。在这种情况下,可以考虑批量插入数据,而不是一次插入一条记录。

12. 结合记录和 ETS 表的应用场景

结合记录和 ETS 表可以构建一些实用的应用。例如,我们可以创建一个简单的行星信息管理系统。以下是一个示例代码:

defmodule PlanetManager do
  def start do
    table = :ets.new(:planet_table, [:set])
    insert_planets(table)
    table
  end

  def insert_planets(table) do
    require Planemo
    planets = [
      Planemo.planemo(name: :mercury, gravity: 3.7, diameter: 4878, distance_from_sun: 57.9),
      Planemo.planemo(name: :venus, gravity: 8.9, diameter: 12104, distance_from_sun: 108.2)
    ]
    Enum.each(planets, fn planet -> :ets.insert(table, planet) end)
  end

  def get_planet(table, name) do
    :ets.lookup(table, name)
  end
end

可以这样使用这个模块:

iex(1)> table = PlanetManager.start()
#Reference<0.2665402240.2777180162.209368>
iex(2)> PlanetManager.get_planet(table, :venus)
[{Planemo, :venus, 8.9, 12104, 108.2}]

这个示例展示了如何结合记录和 ETS 表来管理行星信息。

13. 总结

在 Elixir 中,单元测试是确保代码质量的重要手段。通过 ExUnit 模块,可以方便地编写和运行各种类型的测试,包括基本的断言测试、异常测试等。同时,还可以设置测试的前后操作,使测试更加灵活。

记录是 Elixir 中一种结构化数据的表示方式,虽然现在结构体更为常用,但记录在 Erlang API 中仍然广泛存在,了解记录的使用有助于理解和处理相关代码。

Erlang 术语存储(ETS)是一个强大的内存集合存储,适合存储元组和记录。它提供了多种集合类型,并且操作简单高效,可以用于解决许多数据管理问题。

通过合理使用单元测试、记录和 ETS 表,可以提高 Elixir 代码的质量和性能,构建出更加健壮和高效的应用程序。

以下是一个简单的流程图,展示了使用 ETS 表的基本流程:

graph TD;
    A[创建 ETS 表] --> B[插入数据];
    B --> C[查找数据];
    C --> D[删除数据];
    D --> E[遍历数据];

总之,掌握这些技术对于 Elixir 开发者来说是非常有价值的,可以帮助他们更好地开发和维护 Elixir 应用。

基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样统计,通过模拟系统元件的故障修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高校研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更多不确定性因素进行深化研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值