Форум программистов, компьютерный форум, киберфорум
Visual Basic
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.93/57: Рейтинг темы: голосов - 57, средняя оценка - 4.93
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16

Сортировка словаря методом реконструкции

13.04.2014, 01:43. Показов 12031. Ответов 47

Студворк — интернет-сервис помощи студентам
Посмотрите, пожалуйста, может существует более оптимальный алгоритм.

Здесь я создаю 2 массива: с ключами и со значениями.
В алгоритме сортировки вставками я переставляю одновременно оба массива.
Затем создаю виртуальный словарь: последовательно читаю, в каком порядке должны идти ключи
и заполняю его "ключ + значение исходного словаря".

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
'Сортировка словаря методом вставок -> исходный словарь реконструируется
Sub SpecialSortDict(inDict)
    Dim arrPos: arrPos = inDict.keys                    'Инициализация массива позиций ключей словаря
    Dim arrTemp: arrTemp = inDict.Items                 'Виртуализация значений словаря
 
    Dim i, j, xItem
    For i = 1 To UBound(arrTemp)                        'Сортировка методом вставок
        For j = i To 1 Step -1
            If arrTemp(j) < arrTemp(j - 1) Then
                xItem = arrTemp(j)                      'Обмен значений
                arrTemp(j) = arrTemp(j - 1)
                arrTemp(j - 1) = xItem
                xItem = arrPos(j)                       'Обмен ключей
                arrPos(j) = arrPos(j - 1)
                arrPos(j - 1) = xItem
            Else
                Exit For
            End If
        Next
    Next
 
    Dim virtDict: Set virtDict = CreateObject("Scripting.Dictionary")
    For i = 0 To UBound(arrPos)                         'Расставляем значения в виртуальный словарь согласно массива ключей
        virtDict.Add arrPos(i), inDict(arrPos(i))
    Next
 
    Set inDict = virtDict
End Sub
Пример вызова:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Option Explicit
 
Sub main()
    Dim oScr: Set oScr = CreateObject("Scripting.Dictionary")
    
    oScr.Add "c", 8
    oScr.Add "aa", 1
    oScr.Add "bb", 3
    oScr.Add "a", 2
    oScr.Add "i", 4
    
    SpecialSortDict oScr
    
    Dim key
    For Each key In oScr.keys
        Debug.Print key & " - " & oScr(key)
    Next
    
End Sub
Переменные изначально нетипизированны. Код ориентирован на VBScript.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
13.04.2014, 01:43
Ответы с готовыми решениями:

Составить алгоритм и программу определения самых старых зданий, подлежащих реконструкции
Имеется список 60-ти зданий города, подлежащих реконструкции. Сведения о каждом здании содержат название микрорайона, улицу, номер дома и...

Сортировка словаря
Функция которая возвращает словарь. Но бывает что возвращает его не в том порядке в котором задавался. def multiply(vector, num): ...

Сортировка словаря
Всем привет. Есть словарь: babynames = { 'София, Софья': { 2012: ', 2010: ', 2005: ',

47
 Аватар для Антихакер32
1201 / 473 / 46
Регистрация: 06.01.2014
Сообщений: 1,797
Записей в блоге: 19
14.04.2014, 11:35
Студворк — интернет-сервис помощи студентам
Упс... забыл сменить тип...
счаз всё должно заработать ...
Кликните здесь для просмотра всего текста

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
Option Explicit
'
'Сортировка словаря !
'© FelixMacintosh (Антихакер32™)
'
Const Elements = 100
Dim Dic1 As Object
Dim Dic2 As Object
Dim WithEvents Picture1 As PictureBox
Dim WithEvents Picture2 As PictureBox
Dim WithEvents Command1 As CommandButton
 
Private Sub Command1_Click()
    Const r = vbNullChar & "[Разделяющее значение]" & vbNullChar
    Dim Keys, j$(), f&, v As Variant, p(1) As PictureBox
    CreateDictionary
    Keys = Dic1.Keys
    For f = 0 To Dic1.Count - 1
        Keys(f) = Keys(f) & r & Dic1.Item(Keys(f))
    Next
    Call SortStr(Keys, Dic1.CompareMode, 0, UBound(Keys))
    Dic2.RemoveAll
    For f = 0 To Dic1.Count - 1
        j = Split(Keys(f), r)
        Dic2.Add j(0), j(1)
    Next
    '---------------------------------------------------------------------------------------
    Set p(0) = Picture1: Set p(1) = Picture2
    For f = 0 To 1: p(f).Cls: Next
    For f = 0 To 1: p(f).BackColor = "&H80000009": Next
    p(0).Print vbTab; "Исходный список Dic1"
    p(1).Print vbTab; "Отсортированный список Dic2"
    For f = 0 To 1: p(f).Print String(1000, "."): Next
    For Each v In Dic1
        Picture1.Print vbTab; v; vbTab; , "="; vbTab; Dic1(v)
    Next
    For Each v In Dic2 'Абсолютное соответствие !
        Picture2.Print vbTab; v; vbTab; , "="; vbTab; Dic2(v); vbTab; "Dic1(" & v & ") = " & Dic1(v)
    Next
End Sub
 
Private Sub Form_Initialize()
    Set Dic1 = CreateObject("Scripting.Dictionary")
    Dic1.CompareMode = 1
    Set Dic2 = CreateObject("Scripting.Dictionary")
    Dic2.CompareMode = 1
End Sub
 
Private Sub Form_Load()
    Set Picture1 = Controls.Add("VB.PictureBox", "Picture1")
    Set Picture2 = Controls.Add("VB.PictureBox", "Picture2")
    Set Command1 = Controls.Add("VB.CommandButton", "Command1")
    Picture1.Visible = True: Picture2.Visible = True
    Command1.Caption = "Запустить !"
    Command1.Visible = True
End Sub
 
Private Sub Form_Resize()
    Const c1 = 5, c2 = 20
    Dim twx&, w1&
    twx = c1 * Screen.TwipsPerPixelX
    Me.WindowState = 2
    w1 = Me.ScaleWidth / 2
    Picture1.Move 0, 0, w1, Me.ScaleHeight
    Picture2.Move w1 + twx, 0, w1 - twx, Me.ScaleHeight
    Command1.Move w1 - c2 * twx, 0, c2 * twx, c1 * twx
    Command1.ZOrder 0
End Sub
 
Private Sub CreateDictionary()
 
    Dim s$, j$(), f&, f1&, i&
    Randomize Timer
    s = vbNullString
    For f = 1 To Elements
        For f1 = 1 To 1 + Fix(Rnd * 10)
            s = s & Chr(65 + Fix(Rnd * 26))
            i = i + 1: Mid(s, i, 1) = IIf(Fix(Rnd * 2), UCase(Mid(s, i, 1)), LCase(Mid(s, i, 1)))
        Next
        s = s & " ": i = i + 1
    Next
    j = Split(s): ReDim Preserve j(UBound(j) - 1)
    
    Dic1.RemoveAll
    For f = 0 To UBound(j)
        While Dic1.Exists(j(f))
            s = "": i = 0
            For f1 = 1 To 1 + Fix(Rnd * 10)
                s = s & Chr(65 + Fix(Rnd * 26))
                i = i + 1: Mid(s, i, 1) = IIf(Fix(Rnd * 2), UCase(Mid(s, i, 1)), LCase(Mid(s, i, 1)))
            Next
            j(f) = s
        Wend
        Dic1.Add j(f), f
    Next
End Sub
 
Private Sub SortStr(List As Variant, Compare&, Min&, Max&)
    'Рекурсивный алгоритм быстрой сортировки
    Dim i1&, i2&, n&, s$
    If Not IsArray(List) Then Err.Raise 13
    If Max - Min < 1 Then Exit Sub 'Пройден весь список
    n = Fix((Max - Min + 1) / 5 + Min) 'Выбрать разделяющее значение.
    s = List(n)
    List(n) = List(Min) 'Переместить его вперед.
    i1 = Min: i2 = Max
 
    Do
 
        While StrComp(List(i2), s, Compare) >= 0 'Просмотр сверху вниз от i2 до значения >= s
            i2 = i2 - 1
            If i2 <= i1 Then List(i1) = s: Exit Do
        Wend
        List(i1) = List(i2) 'Поменять местами значения i1 и i2.
        '-----------
        i1 = i1 + 1 'Просмотр снизу вверх от i1 до значения < s.
 
        While StrComp(List(i1), s, Compare) < 0
            i1 = i1 + 1
            If i1 >= i2 Then i1 = i2: List(i2) = s: Exit Do
        Wend
        List(i2) = List(i1) 'Поменять местами значения i1 и i2.
    Loop
    '---------------------------------Сортировать два подсписка.
    On Error GoTo 1
 
    While List(i1) = List(i1 - 1) 'Сокращение диапазона поиска для Max
        i1 = i1 - 1
    Wend
    SortStr List, Compare, Min, i1
    
1
    On Error GoTo 2
 
    While List(i1) = List(i1 + 1) 'Сокращение диапазона поиска для Min
        i1 = i1 + 1
    Wend
    SortStr List, Compare, i1 + 1, Max
2
End Sub


теперь это можно запустить где угодно и на пустой форме
...
тоесть в 23 строчке вполне можно изменить код на этот для всех вариантов !

Visual Basic
1
2
3
4
    For f = 0 To Dic1.Count - 1
        j = Split(Keys(f), r)
        Dic2.Add j(0), Dic1(j(0))
    Next
0
 Аватар для Антихакер32
1201 / 473 / 46
Регистрация: 06.01.2014
Сообщений: 1,797
Записей в блоге: 19
14.04.2014, 12:02
И тишина ...
Вот архив !
кому лень проект создавать
извлекать не обязательно только 2 раза нажать на форму Sort !
Вложения
Тип файла: zip Sort.zip (2.0 Кб, 19 просмотров)
1
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
14.04.2014, 18:10
Лучший ответ Сообщение было отмечено The trick как решение

Решение

Не по теме:

Наконец-то выспался :D


Вот еще быстрее раз в 7-8.
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
' Быстрая сортировка на месте
Private Sub qSort(Dic As Dictionary, ByVal SortItem As Boolean)
    Dim pt As Long, SortData() As SortItem, I As Long, f As Long
    Dim ppCurrent() As Long, pCurrent() As DictionaryItem, prvpCurrent As Long, prvppCurrent As Long
    Dim Tim As Single
    
    Tim = Timer
    
    If Dic.Count <= 1 Then Exit Sub
    ReDim SortData(Dic.Count - 1)
    ' Кто говорил что в VB нельзя работать с указателями? ;)
    ReDim ppCurrent(0): ReDim pCurrent(0)
    GetMem4 ByVal (Not Not ppCurrent) + &HC, prvppCurrent: GetMem4 (Not Not pCurrent) + &HC, ByVal (Not Not ppCurrent) + &HC
    prvpCurrent = ppCurrent(0)
    GetMem4 ByVal ObjPtr(Dic) + &H1C, f
    pt = f
    If SortItem Then
        Do While pt
            ppCurrent(0) = pt
            SortData(I).Field = pCurrent(0).Item
            SortData(I).Pointer = pt
            pt = pCurrent(0).pNext
            I = I + 1
        Loop
    Else
        Do While pt
            ppCurrent(0) = pt
            SortData(I).Field = pCurrent(0).Key
            SortData(I).Pointer = pt
            pt = pCurrent(0).pNext
            I = I + 1
        Loop
    End If
    ' Сортируем
    qSort_ SortData(), 0, I - 1
    GetMem4 SortData(0).Pointer, ByVal ObjPtr(Dic) + &H1C
    ppCurrent(0) = SortData(0).Pointer
    For I = 1 To UBound(SortData)
        pCurrent(0).pNext = SortData(I).Pointer
        ppCurrent(0) = SortData(I).Pointer
    Next
    pCurrent(0).pNext = 0
    GetMem4 SortData(I - 1).Pointer, ByVal ObjPtr(Dic) + &H20
    ' Восстановление
    ppCurrent(0) = prvpCurrent: GetMem4 prvppCurrent, ByVal (Not Not ppCurrent) + &HC
    
bug: On Error GoTo bug
    MsgBox Format$(Timer - Tim, "###0.0000")
 
End Sub
Private Sub qSort_(D() As SortItem, ByVal L As Long, ByVal H As Long)
    Dim I As Long, J As Long, M As SortItem, wsp As SortItem
    I = L: J = H: M = D((I + J) \ 2)
    Do Until I > J: Do While D(I).Field < M.Field: I = I + 1: Loop: Do While D(J).Field > M.Field: J = J - 1: Loop
        If (I <= J) Then wsp = D(I): D(I) = D(J): D(J) = wsp: I = I + 1: J = J - 1
    Loop
    If L < J Then qSort_ D(), L, J
    If I < H Then qSort_ D(), I, H
End Sub
Кстати в словаре еще кроме Key и Value можно еще хранить 4 байта .
Вложения
Тип файла: rar DicViewer.rar (15.5 Кб, 40 просмотров)
5
 Аватар для Антихакер32
1201 / 473 / 46
Регистрация: 06.01.2014
Сообщений: 1,797
Записей в блоге: 19
14.04.2014, 18:24
(Not Not ppCurrent) ...

тяжело для понимания
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
14.04.2014, 18:25
Цитата Сообщение от FelixMacintosh Посмотреть сообщение
Not Not ppCurrent
Получить адрес SafeArray
0
 Аватар для Антихакер32
1201 / 473 / 46
Регистрация: 06.01.2014
Сообщений: 1,797
Записей в блоге: 19
14.04.2014, 18:38
Что-ж молодец ...
проси медаль

Добавлено через 5 минут
Цитата Сообщение от The trick Посмотреть сообщение
Кстати в словаре еще кроме Key и Value можно еще хранить 4 байта .
и где их можно хранить ?
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
14.04.2014, 18:47
Dragokas работает ли данный код у тебя?

Добавлено через 5 минут
Цитата Сообщение от FelixMacintosh Посмотреть сообщение
и где их можно хранить ?
Я описывал структуру элемента списка, там есть поле Reserved, в нем и хранить. Я хотел изначально сделать в этом поле реверс списка, т.е. сделать из односвязного списка двусвязный. Но почитав несколько отзывов решил что qSort будет быстрее сортировки двусвязного списка.
Вот в цикле
Visual Basic
1
2
3
4
5
6
7
Do
            ppCurrent(0) = pt
            SortData(I).Field = pCurrent(0).Item
            SortData(I).Pointer = pt
            pt = pCurrent(0).pNext
            I = I + 1
        Loop
Если поставишь на паузу (только не тормози проект пока не выйдешь из функции!) внутри цикла, то pCurrent будет поочередно принимать значения всех элементов в списке, там в структуре и ключ, и указатель и значение и вот это свободное поле. Можешь ему присваивать значения а потом считывать, получится как бы один ключ - два значения.
Not Not ppCurrent можно заменить ArrPtr(ppCurrent)
2
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
15.04.2014, 12:36  [ТС]
The trick, работает, но отрицательные странновато сортирует.
Миниатюры
Сортировка словаря методом реконструкции  
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
15.04.2014, 12:39
Цитата Сообщение от Dragokas Посмотреть сообщение
The trick, работает, но отрицательные странновато сортирует.
Потому что они текстовые, т.к. вводятся в текстбокс и сравниваются как текстовые. Преобразований я не делал. Но функция сортирует любой тип правильно. Можешь программно добавить числовые значения, они отсортируются как надо.
0
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
15.04.2014, 12:43  [ТС]
Я тоже так подумал. Просто под рукой нет компилятора.
Спасибо.
0
Модератор
10046 / 3892 / 883
Регистрация: 22.02.2013
Сообщений: 5,844
Записей в блоге: 79
16.04.2014, 01:11
Кстати, кому интересно, эти элементы являются объектами поддерживающими интерфейс IDictionary (первый элемент структуры указывал на интерфейс IDictionary я его расшифровал), описание не нашел пока, но чувствую там много полезных фич еще будет на уровне элементов.
1
1 / 1 / 0
Регистрация: 26.03.2016
Сообщений: 42
01.10.2017, 16:43
А как пользоваться? при попытке открыть DicViewer.exe звук ошибки, окна ошибки нет. При попытке добавить DicViewer.exe, как надстройку ничего не происходит...
0
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
01.10.2017, 16:47  [ТС]
Baiker34, выполните перекомпиляцию проекта.
0
39 / 39 / 8
Регистрация: 15.08.2014
Сообщений: 634
02.10.2017, 21:59
может не в тему..
Но когда то очень здорово помогли...
Тогда задача правда была несколько инная...удаление дублей из списка более 100 000 тыс элементов...
Работает очень быстро

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Option Explicit
 
Private Sub Command1_Click()
     Dim a() As String, i As Long, k As Long, n As Long
     n = 100000
     ReDim a(1 To n)
     For i = 1 To n
         a(i) = "aa"
     Next i
     a(25000) = "bb"
     a(50000) = "cc"
     a(75000) = "dd"
     
     Call RemoveDupesFromArray(a, k)
     ReDim Preserve a(1 To k)
     MsgBox Join(a)
     
End Sub
 
Public Sub RemoveDupesFromArray(ByRef a, ByRef k As Long)
    Dim Dict, i As Long
    Set Dict = CreateObject("Scripting.Dictionary")
    With Dict
         For i = LBound(a) To UBound(a)
            If Not .Exists(a(i)) Then
                .Add a(i), i
                k = k + 1
                a(k) = a(i)
            End If
         Next
    End With
End Sub
0
1379 / 834 / 89
Регистрация: 08.02.2017
Сообщений: 3,478
Записей в блоге: 1
25.11.2023, 16:27
Процедура DictPtrStrKeys (vba/vb6-совместимая) предназначена для получения массива указателей строковых кючей. Можно использовать, если заранее изветно, что все ключи строковые. Также можно получить массив строковых итемов ели заменить строку pKeys(i) = DicItem.pKey.Ptr на pKeys(i) = DicItem.pItem.Ptr
Кликните здесь для просмотра всего текста
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Option Explicit
'в VB6 разкомментировать эти строки:
'Public Enum LongPtr
'[_]
'End Enum
Public Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
 
Private Type pVariant
    cur As Currency
    Ptr As LongPtr
    lp0 As LongPtr
End Type
Private Type tpDicItemPtr
    pNext As LongPtr
    pKey As pVariant
    pItem As pVariant
End Type
 
#If Win64 Then
    Private Const ptrSz = 8
    Private Const dict1PtrOffset = 48
#Else
    Private Const ptrSz = 4
    Private Const dict1PtrOffset = 28
#End If
 
Sub ПримерИспользования()
    Dim dict As New Dictionary, pSA As LongPtr
    Dim pKeys() As LongPtr, Keys() As String, Ptr0 As LongPtr
    
    dict.Add "key1", "item1"
    dict.Add "key2", "item2"
    dict.Add "key3", "item3"
    
    DictPtrStrKeys dict, pKeys
    
    'CopyMemory ByVal VarPtr(Ptr0) + ptrSz, ByVal VarPtr(Ptr0) + ptrSz * 2, ptrSz 'если не нужен Join
    CopyMemory pSA, ByVal VarPtr(Ptr0) + ptrSz * 2, ptrSz
    CopyMemory ByVal VarPtr(Ptr0) + ptrSz, pSA, ptrSz
    CopyMemory ByVal pSA + &H2, 384, &H2          'без этой строки не работает Join
    
    MsgBox Join$(Keys(), vbCr)
    
    CopyMemory ByVal pSA + &H2, 128, &H2          'без этой строки не освобождаются указатели в массиве
    CopyMemory ByVal VarPtr(Ptr0) + ptrSz, Ptr0, ptrSz
End Sub
 
Private Sub DictPtrStrKeys(Dic As Dictionary, pKeys() As LongPtr)
    Dim DicItem As tpDicItemPtr
    DictPtrStrKeys_ Dic, pKeys, DicItem
End Sub
Private Sub DictPtrStrKeys_(Dic As Dictionary, pKeys() As LongPtr, DicItem As tpDicItemPtr, _
                    Optional Ptr As LongPtr, Optional pPtr As LongPtr, Optional ByVal Ptr0 As LongPtr)
    Dim i As Long, Cnt As Long, pTmp As LongPtr
    
    Cnt = Dic.Count
    If Cnt = 0 Then Exit Sub
    Ptr0 = VarPtr(Ptr0)
    CopyMemory ByVal Ptr0 - ptrSz, Ptr0 - ptrSz * 2, ptrSz
    pPtr = ObjPtr(Dic) + dict1PtrOffset: pTmp = Ptr
    pPtr = Ptr0 - ptrSz * 3: Ptr = pTmp + ptrSz
    ReDim pKeys(Cnt - 1)
    For i = 0 To Cnt - 1
        pKeys(i) = DicItem.pKey.Ptr
        Ptr = DicItem.pNext + ptrSz
    Next
End Sub


Добавлено через 8 минут
Методом дидукции и подбора нашел смещение первого элемента в vba x64 у коллекций и словарей, но вот с поиском элемента по хэшу по сложнее там еще lcid, pHTbl, Div. Не подскажете, как и в чем или как определить эти смещения?
0
1379 / 834 / 89
Регистрация: 08.02.2017
Сообщений: 3,478
Записей в блоге: 1
26.11.2023, 13:55
Нашел все смещения, "метод дидукции" продолжает рабоать. )

Добавлено через 1 час 5 минут
Насчет смещения следующего элемента в хэш-таблице (у меня константа dictNxtpHItem) не очень уверен, х.з. как проверить, остальное все "попадает" )
Кликните здесь для просмотра всего текста
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
Option Explicit
Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
Private Declare PtrSafe Function VarBstrCmp Lib "oleaut32" (ByVal bstrLeft As LongPtr, ByVal bstrRight As LongPtr, ByVal lcid As Long, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Sub VariantCopy Lib "oleaut32.dll" (pvargDest As Any, pvargSrc As Any)
 
Private Type tpLong
    l As Long
End Type
Private Type tpCurrency
    c As Currency
End Type
#If Win64 Then
    Private Const ptrSz = 8
    Private Const varSz = 24
    Private Const dictPtr1Offset = 48
    Private Const dictpHTblOffset = 64
    Private Const dictDivOffset = 72
    Private Const dictlcidOffset = 80
    Private Const dictSKeyOffset = 24
    Private Const dictItemOffset = 40
    Private Const dictNxtpHItem = 72
#Else
    Private Const ptrSz = 4
    Private Const varSz = 16
    Private Const dictPtr1Offset = 28
    Private Const dictpHTblOffset = 36
    Private Const dictDivOffset = 40
    Private Const dictlcidOffset = 48
    Private Const dictSKeyOffset = 16
    Private Const dictItemOffset = 24
    Private Const dictNxtpHItem = 40
#End If
 
Sub Пример()
    Dim dict As New Dictionary
    
    dict.Add "key1", "item1"
    dict.Add "key2", "item2"
    dict.Add "key3", "item3"
    
    Debug.Print GetItemVBA(dict, "key2")
End Sub
' Получить элемент по ключу. Реплика функции The trick-а https://www.cyberforum.ru/visual-basic/thread1146688.html#post6040814
Private Function GetItemVBA(Dic As Dictionary, Key As String) As Variant
    Dim Hash As Long, pHTbl As LongPtr, pHItem As LongPtr, lcid As Long, iKey As LongPtr, cmp As Long    
    CopyMemory lcid, ByVal ObjPtr(Dic) + dictlcidOffset, ptrSz      ' Получаем lcid
    Hash = HashValVBA(Dic, Key)                                     ' Вычисляем хэш
    CopyMemory pHTbl, ByVal ObjPtr(Dic) + dictpHTblOffset, ptrSz    ' Получаем указатель на хэш-таблицу
    CopyMemory pHItem, ByVal pHTbl + Hash * ptrSz, ptrSz            ' Получаем указатель элемента в хэш-таблице
    Do While pHItem                                                 ' Если есть такой элемент
        CopyMemory iKey, ByVal pHItem + dictSKeyOffset, ptrSz       ' Сравниваем значение ключа в таблице с заданым ключем
        Select Case VarBstrCmp(StrPtr(Key), iKey, lcid, Dic.CompareMode)
        Case 1: VariantCopy GetItemVBA, ByVal pHItem + dictItemOffset: Exit Function
        Case Else
            CopyMemory pHItem, ByVal pHItem + dictNxtpHItem, ptrSz  ' Получаем указатель на следующую запись в таблице
        End Select
    Loop
End Function
 
Private Function HashValVBA(Dic As Dictionary, ByVal s As String) As Long
    Dim i As Long, ch As Currency, Div As Long
    Dim res As tpLong, cres As tpCurrency
    If Dic.CompareMode = TextCompare Then LSet s = StrConv(s, vbLowerCase)
    ' Извращения с Currency, т.к. в VB нет UINT32 и циклической арифметики
    For i = 1 To Len(s)                                             
        ch = AscW(Mid$(s, i, 1)) / 10000
        cres.c = CCur(res.l) / 10000 * 17 + ch
        LSet res = cres
    Next
    cres.c = 0: LSet cres = res 
    ch = cres.c * 10000
    CopyMemory Div, ByVal ObjPtr(Dic) + dictDivOffset, 4 ' Константа
    HashValVBA = (ch - (Int(ch / Div) * Div))
End Function
1
932 / 365 / 43
Регистрация: 10.05.2021
Сообщений: 1,564
Записей в блоге: 10
27.11.2023, 10:30
Dragokas, здравствуйте
Попробуйте рекурсивный QuickSort
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Sub PRDX_SortRecur_WithInd(aV(), aI() As Long, LBnd&, UBnd&)
Dim i&, j&, n&, x, y
 
i = LBnd: j = UBnd: x = aV((LBnd + UBnd) \ 2)
 
Do
    While aV(i) < x: i = i + 1: Wend
    While x < aV(j): j = j - 1: Wend
    If i <= j Then
        y = aV(i): aV(i) = aV(j): aV(j) = y
        n = aI(i): aI(i) = aI(j): aI(j) = n
        i = i + 1: j = j - 1
    End If
Loop Until i > j
 
If LBnd < j Then PRDX_SortRecur_WithInd aV, aI, LBnd, j
If i < UBnd Then PRDX_SortRecur_WithInd aV, aI, i, UBnd
End Sub
Сортируем массив ключей и массив их индексов одновременно, потом сортируем значения по индексам.
Если нужно отсортировать только ключи, то проще
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Sub PRDX_SortRecur_a1D(a1D(), LBnd&, UBnd&)
Dim i&, j&, x, y
 
i = LBnd: j = UBnd: x = a1D((LBnd + UBnd) \ 2)
 
Do
    While a1D(i) < x: i = i + 1: Wend
    While x < a1D(j): j = j - 1: Wend
    If i <= j Then y = a1D(i): a1D(i) = a1D(j): a1D(j) = y: i = i + 1: j = j - 1
Loop Until i > j
 
If LBnd < j Then PRDX_SortRecur_a1D a1D, LBnd, j
If i < UBnd Then PRDX_SortRecur_a1D a1D, i, UBnd
End Sub
1
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
27.11.2023, 17:35  [ТС]
Jack Famous, через 10 лет оно мне уже не особо нужно, но спасибо. Именно так и делаю в другой утилите.
Алгоритм не принципиально Quick Sort, или метод вставок (эффективность больше зависит от набора данных). Изначальный вопрос был вообще в принципе о методике сортировки именно словаря, а не массивов. Да и в 1-м посте был опубликован именно метод вставок из-за более простого для пояснения кода.
0
932 / 365 / 43
Регистрация: 10.05.2021
Сообщений: 1,564
Записей в блоге: 10
28.11.2023, 09:25
Цитата Сообщение от Dragokas Посмотреть сообщение
через 10 лет оно мне уже не особо нужно
простите — это всё testuser2 виноват
Цитата Сообщение от Dragokas Посмотреть сообщение
Изначальный вопрос был вообще в принципе о методике сортировки именно словаря, а не массивов
ну, как бы, тут смотря, что нужно — исследовать алгоритм/возможность или получить результат. Мой вариант — для второго подхода. Зачем сортировать ключи словаря вместе со значениями, если не использовать потом их в виде массивов — мне непонятно. А, если нужно как раз это, то почему бы не отойти от словаря и сразу перейти к массивам (благо, их получение происходит очень быстро).
Цитата Сообщение от Dragokas Посмотреть сообщение
Алгоритм не принципиально Quick Sort, или метод вставок
квик в данной реализации на моих тестах показывает стабильный отрыв от других аналогов на VBA. Возможно, так будет не у всех…
0
1379 / 834 / 89
Регистрация: 08.02.2017
Сообщений: 3,478
Записей в блоге: 1
28.11.2023, 13:41
Цитата Сообщение от Jack Famous Посмотреть сообщение
квик в данной реализации на моих тестах показывает стабильный отрыв от других аналогов на VBA.
Для строковых данных, есть задел на убыстрение. Допустим StrComp пишут быстрей обычного сравнения.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
28.11.2023, 13:41
Помогаю со студенческими работами здесь

Сортировка словаря
##Дан словарь. Помогите остортировать его по значению Номер Dictionary = {'№':,'1':,'2':,'3':} print...

Сортировка словаря
database = { &quot;Группа1&quot;:, , ], &quot;Группа2&quot;:, ] } def cout2(groupname): for key in...

сортировка словаря
при запуске программы создается пустой словарь в процессе работы добавляются элементы. причем первое значение всегда число. при...

Сортировка словаря по ключу
Как отсортировать Dictionary &lt;int, Vector3&gt; по ключу, по возрастанию? Метода Sort() для словаря так сходу не нашёл. Надо компаратор...

Сортировка ключей словаря
Здравствуйте:) Существует словарь следующего вида: {'35': 1, '45': 2, '56': 3, '76': 4, '24': 5} Нужно отсортировать ключи данного...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru