Какая разница между `ImmutableSortedSet` и fsharp` Set`?

BCL представила группу Неизменяемых Коллекций

Мне интересно, какая разница между ImmutableSortedSet и родным FSharp Set? Похоже, что подписи производительности обоих схожи. Также я видел где-то, что SortedSet реализован как Red Black Tree, поэтому, думаю, ImmutableSortedSet делает то же самое.

Какова внутренняя реализация fsharp map? Is is Red Black Tree, как заявлено здесь, или дерево AVL как узнал здесь?

Кроме того, почему в документах MSDN не указано, какая фактическая структура данных для коллекции библиотеки? Я знаю, что это детали реализации и вот-вот изменится. Я хочу сказать, что если они не хотят связывать тип данных библиотеки с определенным типом хорошо известной структуры данных, они должны, по крайней мере, предлагать совокупность всех подпрограмм производительности с точки зрения сложности?

Ответ 1

Мне интересно, какая разница между ImmutableSortedSet и собственным набором FSharp?

Они, как правило, очень похожи. Основное отличие состоит в том, что F # Set поддерживает быстрые теоретические операции (объединение, пересечение и разность).

Вот простая программа F #, которая измеряет производительность некоторых общих операций:

open System.Collections.Immutable

while true do
  do
    let timer = System.Diagnostics.Stopwatch.StartNew()
    let cmp = LanguagePrimitives.FastGenericComparer<int>
    let mutable s1 = ImmutableSortedSet.Create<int>(cmp)
    let mutable s2 = ImmutableSortedSet.Create<int>(cmp)
    for i in 1..1000000 do
      s1 <- s1.Add i
    for i in 1000000..2000000 do
      s2 <- s2.Add i
    printfn "BCL ImmutableSortedSet: add in %fs" timer.Elapsed.TotalSeconds
    timer.Restart()
    for _ in 1..10 do
      for i in 1..1000000 do
        ignore(s1.Contains i)
    printfn "BCL ImmutableSortedSet: contains in %fs" timer.Elapsed.TotalSeconds
    timer.Restart()
    let s = s1.Union s2
    printfn "BCL ImmutableSortedSet: union in %fs" timer.Elapsed.TotalSeconds

  do
    let timer = System.Diagnostics.Stopwatch.StartNew()
    let mutable s1 = Set.empty
    let mutable s2 = Set.empty
    for i in 1..1000000 do
      s1 <- s1.Add i
    for i in 1000000..2000000 do
      s2 <- s2.Add i
    printfn "F# Set: %fs" timer.Elapsed.TotalSeconds
    timer.Restart()
    for _ in 1..10 do
      for i in 1..1000000 do
        ignore(s1.Contains i)
    printfn "F# Set: contains in %fs" timer.Elapsed.TotalSeconds
    timer.Restart()
    let s = Set.union s1 s2
    printfn "F# Set: union in %fs" timer.Elapsed.TotalSeconds

На моей машине я получаю:

         BCL ImmutableSortedSet  F# Set
add                2.6s          3.0s
contains           2.1s          1.9s
union              1.1s          0.00004s

Таким образом, F # Set немного медленнее, чтобы построить и немного быстрее искать, но на порядок быстрее для операции теоретического объединения множеств.

Какова внутренняя реализация карты fsharp? Является ли это красным черным деревом, как указано здесь, или деревом AVL, как выяснено здесь?

Как указано в ваших ссылках, F # использует деревья AVL.

Это действительно актуально в контексте приведенных выше показателей эффективности. Деревья AVL содержат максимальную высоту поддерева в каждой ветки и, следовательно, позволяют перебалансировать поддеревья без изучения всего поддерева. Напротив, красно-черные деревья содержат один бит данных в каждой ветки, поэтому для поддеревьев поддеревьев требуется, чтобы все деревья пересекались, что было асимптотически медленнее. В непрофессиональных терминах объединение двух одинаковых неперекрывающихся множеств влечет за собой не что иное, как создание новой ветки, содержащей два существующих дерева. Обратите внимание, что Union в API BCL не может даже выразить это: он обрабатывает абстрактный IEnumerable, а не конкретный набор.

Кроме того, почему в документах MSDN не указано, какая фактическая структура данных для коллекции библиотеки? Я знаю, что это детали реализации и вот-вот изменится. Я хочу сказать, что если они не хотят связывать тип данных библиотеки с определенным типом хорошо известной структуры данных, они должны, по крайней мере, предлагать совокупность всех подпрограмм производительности с точки зрения сложности?

Я согласен с тем, что сложности в документах будут хорошими.

Ответ 2

Типы F # Set и Map реализуются с помощью деревьев AVL.

Я не знаю о документации MSDN, вам нужно спросить команду F # об этом:)

В любом случае, деревья Red-Black и деревья AVL имеют одинаковую вычислительную сложность для их основных операций. На практике у них разные характеристики производительности, которые могут привести вас к выбору того или другого для вашего конкретного приложения. У красно-черных деревьев есть более быстрая вставка/удаление, потому что им не нужно делать столько перебалансировки дерева, а поиск быстрее в деревьях AVL, благодаря дополнительной балансировке, выполняемой для вставки/удаления. Я думаю, именно поэтому деревья AVL были выбраны для карт F # Map и Set - Map/Set обычно создается один раз (то есть, не модифицируется), а затем повторно запрашивается.

https://en.wikipedia.org/wiki/Red%E2%80%93black_tree

https://en.wikipedia.org/wiki/AVL_tree