Добрый день! Год назад в этой теме http://www.excelworld.ru/forum/2-34294-1 уважаемая Pelena решила мою задачу. Мне нужно было построить ряд целых чисел, которые отсутствую в промежутке от 1 до номера квартиры. В примере: в массиве M2:N4952 -адреса с квартирами, которые есть в базе. В массиве Q2:R48 адреса домов с максимальным номером квартиры в этом доме. Функция работает прекрасно, но с большими диапазонами ооочень медленно. На обработку только 200 (из 15000) адресов (при условии поиска 15-ти тысяч адресов в 700-стах тысяч квартир) у нее уходит минут 25-30. Вот уже третий день я абсолютно безуспешно пытаюсь ускорить работу функции с помощью словарей. Но дальше определения диапазонов и формирования словаря я нисколько не продвинулся, при этом, я совершенно никак не могу сообразить как прописать условия поиска. Знаю, что можно прописать функцию в самом коде со словарем, но будет ли это быстрее работать? Или этот алгоритм поиска можно прописать в самом коде без использования функции?
Добрый день! Год назад в этой теме http://www.excelworld.ru/forum/2-34294-1 уважаемая Pelena решила мою задачу. Мне нужно было построить ряд целых чисел, которые отсутствую в промежутке от 1 до номера квартиры. В примере: в массиве M2:N4952 -адреса с квартирами, которые есть в базе. В массиве Q2:R48 адреса домов с максимальным номером квартиры в этом доме. Функция работает прекрасно, но с большими диапазонами ооочень медленно. На обработку только 200 (из 15000) адресов (при условии поиска 15-ти тысяч адресов в 700-стах тысяч квартир) у нее уходит минут 25-30. Вот уже третий день я абсолютно безуспешно пытаюсь ускорить работу функции с помощью словарей. Но дальше определения диапазонов и формирования словаря я нисколько не продвинулся, при этом, я совершенно никак не могу сообразить как прописать условия поиска. Знаю, что можно прописать функцию в самом коде со словарем, но будет ли это быстрее работать? Или этот алгоритм поиска можно прописать в самом коде без использования функции?AVI
Dim MyArray As Range, NewMyArray, a, Dic As Object
On Error Resume Next Set MyArray = Application.InputBox("Только одна колонка с адресами." & vbLf & _ "Квартиры должны быть в соседней колонке справа от выделенного.", _ "Выберите диапазон с адресами", , , , , , 8) '[m2:m5000] ' массив с адресами If Err.Number > 0 Then Exit Sub
Dim RunTimes As Date: RunTimes = Time Dim Start!: Start = Timer
'' для раннего связывания требуется подключение '' Tools - References... "Microsoft Scripting Runtime" ' Set Dic = New Dictionary
Set Dic = CreateObject("Scripting.Dictionary") Dic.CompareMode = vbTextCompare 'Что бы сделать ключить не чуствительными к регистру.
For Each a In MyArray If Dic.Exists(CStr(a)) Then Dic.Item(CStr(a)) = Dic.Item(CStr(a)) & "|" & a.Offset(, 1).Value Else Dic.Add CStr(a), a.Offset(, 1).Value End If Next a
For Each a In Dic.keys Dic.Item(a) = SearchNumKv(SortedRezult(Split(Dic.Item(a), "|"))) If Len(Dic.Item(a)) = 0 Then Dic.Remove a 'что бы выводить в результат только строки со значениями. Можно закомментировать. Next
With Workbooks.Add(xlWBATWorksheet).Worksheets(1) .Range(.Cells(1, 1), .Cells(Dic.Count, 2)) = NewMyArray .Columns(1).AutoFit End With Debug.Print "Затрачено: " & Format(Timer - Start, "#0.0") & " сек." 'MsgBox "Затрачено: " & Format(Time - RunTimes, "hh:mm:ss") End Sub
Private Function SortedRezult(Massiv As Variant) Dim n&, i& Dim Tmp1 As Variant Dim Tmp2 As Variant On Error Resume Next For i = LBound(Massiv) To UBound(Massiv) Step 1 Tmp1 = Val(Massiv(i)) For n = i To UBound(Massiv) If Val(Massiv(n)) < Tmp1 Then Tmp1 = Massiv(n): Tmp2 = Massiv(i) Massiv(i) = Tmp1: Massiv(n) = Tmp2 End If Next n Next i SortedRezult = Massiv End Function
Private Function SearchNumKv(Massiv As Variant) Dim i&, j&, sVal$ For i = 1 To Val(Massiv(UBound(Massiv))) For j = LBound(Massiv) To UBound(Massiv) If i = Val(Massiv(j)) Then Exit For Next If j > UBound(Massiv) Then sVal = sVal & ", " & i Next SearchNumKv = Mid(sVal, 3) End Function
[/vba]
AVI, здравствуйте, вариант со словарем во вложении(Module2). Сам листинг под спойлером. Ваш список из 5 тыс. записей обрабатывается за 0,8 сек.
Dim MyArray As Range, NewMyArray, a, Dic As Object
On Error Resume Next Set MyArray = Application.InputBox("Только одна колонка с адресами." & vbLf & _ "Квартиры должны быть в соседней колонке справа от выделенного.", _ "Выберите диапазон с адресами", , , , , , 8) '[m2:m5000] ' массив с адресами If Err.Number > 0 Then Exit Sub
Dim RunTimes As Date: RunTimes = Time Dim Start!: Start = Timer
'' для раннего связывания требуется подключение '' Tools - References... "Microsoft Scripting Runtime" ' Set Dic = New Dictionary
Set Dic = CreateObject("Scripting.Dictionary") Dic.CompareMode = vbTextCompare 'Что бы сделать ключить не чуствительными к регистру.
For Each a In MyArray If Dic.Exists(CStr(a)) Then Dic.Item(CStr(a)) = Dic.Item(CStr(a)) & "|" & a.Offset(, 1).Value Else Dic.Add CStr(a), a.Offset(, 1).Value End If Next a
For Each a In Dic.keys Dic.Item(a) = SearchNumKv(SortedRezult(Split(Dic.Item(a), "|"))) If Len(Dic.Item(a)) = 0 Then Dic.Remove a 'что бы выводить в результат только строки со значениями. Можно закомментировать. Next
With Workbooks.Add(xlWBATWorksheet).Worksheets(1) .Range(.Cells(1, 1), .Cells(Dic.Count, 2)) = NewMyArray .Columns(1).AutoFit End With Debug.Print "Затрачено: " & Format(Timer - Start, "#0.0") & " сек." 'MsgBox "Затрачено: " & Format(Time - RunTimes, "hh:mm:ss") End Sub
Private Function SortedRezult(Massiv As Variant) Dim n&, i& Dim Tmp1 As Variant Dim Tmp2 As Variant On Error Resume Next For i = LBound(Massiv) To UBound(Massiv) Step 1 Tmp1 = Val(Massiv(i)) For n = i To UBound(Massiv) If Val(Massiv(n)) < Tmp1 Then Tmp1 = Massiv(n): Tmp2 = Massiv(i) Massiv(i) = Tmp1: Massiv(n) = Tmp2 End If Next n Next i SortedRezult = Massiv End Function
Private Function SearchNumKv(Massiv As Variant) Dim i&, j&, sVal$ For i = 1 To Val(Massiv(UBound(Massiv))) For j = LBound(Massiv) To UBound(Massiv) If i = Val(Massiv(j)) Then Exit For Next If j > UBound(Massiv) Then sVal = sVal & ", " & i Next SearchNumKv = Mid(sVal, 3) End Function
Можно сводной таблицей сделать.(в 2016+) Данных немного поудалял, но всеравно в 100кб не влез - поэтому два архива Алгоритм явно можно сократить - но лениво. .
Можно сводной таблицей сделать.(в 2016+) Данных немного поудалял, но всеравно в 100кб не влез - поэтому два архива Алгоритм явно можно сократить - но лениво. .SLAVICK
boa, что-то я не понял как работает. Я выбрал диапазон - нажал ОК и просто открылся пустой лист. SLAVICK, Сводную сдуру погнал по всем оригинальному файлу:"идет извлечение фалов" уже 35 минут... и вылезло сообщение, что заканчивается свободная память на ssd-шнике... свободной было 45 гб... sboy, с Power Query у меня еще хуже, чем с макросами. Я просто не понимаю как это работает.
[offtop]Главный вопрос: куда делось место и как его вернуть после сводной)))))))))))))))))))))))[/offtop]
boa, что-то я не понял как работает. Я выбрал диапазон - нажал ОК и просто открылся пустой лист. SLAVICK, Сводную сдуру погнал по всем оригинальному файлу:"идет извлечение фалов" уже 35 минут... и вылезло сообщение, что заканчивается свободная память на ssd-шнике... свободной было 45 гб... sboy, с Power Query у меня еще хуже, чем с макросами. Я просто не понимаю как это работает.
[offtop]Главный вопрос: куда делось место и как его вернуть после сводной)))))))))))))))))))))))[/offtop]AVI
Сообщение отредактировал AVI - Среда, 10.10.2018, 13:44
идет извлечение фалов" уже 35 минут... и вылезло сообщение
ого а строк сколько? Вы же к-во квартир не увеличивали? Имею ввиду таблицу с номерами от 1-400 -- тут нужно сделать до макс. к-ва квартир, но не пихать всю таблицу со всеми квартирами.
да так же как и с моим примером в 2016+ офисе - можно так и так. По хорошему если данных много есть смысл подумать над запуском через SQL, ну или заточенным макросом, возможно макрос boa, и справится - в алгоритм не вникал.
идет извлечение фалов" уже 35 минут... и вылезло сообщение
ого а строк сколько? Вы же к-во квартир не увеличивали? Имею ввиду таблицу с номерами от 1-400 -- тут нужно сделать до макс. к-ва квартир, но не пихать всю таблицу со всеми квартирами.
да так же как и с моим примером в 2016+ офисе - можно так и так. По хорошему если данных много есть смысл подумать над запуском через SQL, ну или заточенным макросом, возможно макрос boa, и справится - в алгоритм не вникал.
Да уж - выходит таблица на 840млн записей - поэтому и зависает. Тут или делать кусками(за 10 раз должно потянуть), или макрос. Похоже Алгоритм boa, тоже не оптимальный - я бы сделал по другому.
Да уж - выходит таблица на 840млн записей - поэтому и зависает. Тут или делать кусками(за 10 раз должно потянуть), или макрос. Похоже Алгоритм boa, тоже не оптимальный - я бы сделал по другому.SLAVICK
Option Compare Text Sub d_D() Dim dic As Object, dic2 As Object, arr, arrT1, arrT2, s$ Dim stKV%, endKV%, i&, ii&, tI&
SORT_
Set dic = CreateObject("Scripting.Dictionary") Set dic2 = CreateObject("Scripting.Dictionary")
arr = Selection i = 2 dic(arr(1, 2)) = arr(1, 2)
Do While i < UBound(arr) s = "" endKV = 0 If i > 2 Then dic.RemoveAll
ii = IIf(i > 2, i - 1, 1) dic(arr(ii, 2)) = arr(ii, 2) Do While arr(i, 1) = arr(i - 1, 1) And i <= UBound(arr) dic(arr(i, 2)) = arr(i, 2) endKV = IIf(endKV > Val(arr(i, 2)), endKV, Val(arr(i, 2))) i = i + 1 If i > UBound(arr) Then Exit Do If i \ 100 = i / 100 Then DoEvents: Application.StatusBar = i Loop arrT2 = dic.KEYS For tI = 1 To endKV ' ЕСЛИ нужно смотреть НЕ с 1-й квартиры, а минимальной - то For tI = arrT2(0) To endKV If Not dic.EXISTS(tI) Then s = s & ", " & tI Next s = Mid(s, 3, 9 ^ 9) dic2(arr(ii, 1)) = s
i = i + 1 Loop arr = dic2.KEYS arrT2 = dic2.ITEMS ReDim arrT1(1 To UBound(arr) + 1, 1 To 2) For i = 1 To UBound(arrT1) arrT1(i, 1) = arr(i - 1) arrT1(i, 2) = arrT2(i - 1) Next Sheets.Add [a1].Resize(UBound(arrT1), 2) = arrT1 Application.StatusBar = False End Sub
Sub SORT_() ActiveSheet.SORT.SortFields.Clear ActiveSheet.SORT.SortFields.Add2 _ Key:=Selection.Columns(1), SortOn:=xlSortOnValues, Order:=xlAscending, _ DataOption:=xlSortNormal ActiveSheet.SORT.SortFields.Add2 _ Key:=Selection.Columns(1), SortOn:=xlSortOnValues, Order:=xlAscending, _ DataOption:=xlSortNormal With ActiveSheet.SORT .SetRange Selection .Header = xlYes .MatchCase = False .Orientation = xlTopToBottom .SortMethod = xlPinYin .Apply End With End Sub
Option Compare Text Sub d_D() Dim dic As Object, dic2 As Object, arr, arrT1, arrT2, s$ Dim stKV%, endKV%, i&, ii&, tI&
SORT_
Set dic = CreateObject("Scripting.Dictionary") Set dic2 = CreateObject("Scripting.Dictionary")
arr = Selection i = 2 dic(arr(1, 2)) = arr(1, 2)
Do While i < UBound(arr) s = "" endKV = 0 If i > 2 Then dic.RemoveAll
ii = IIf(i > 2, i - 1, 1) dic(arr(ii, 2)) = arr(ii, 2) Do While arr(i, 1) = arr(i - 1, 1) And i <= UBound(arr) dic(arr(i, 2)) = arr(i, 2) endKV = IIf(endKV > Val(arr(i, 2)), endKV, Val(arr(i, 2))) i = i + 1 If i > UBound(arr) Then Exit Do If i \ 100 = i / 100 Then DoEvents: Application.StatusBar = i Loop arrT2 = dic.KEYS For tI = 1 To endKV ' ЕСЛИ нужно смотреть НЕ с 1-й квартиры, а минимальной - то For tI = arrT2(0) To endKV If Not dic.EXISTS(tI) Then s = s & ", " & tI Next s = Mid(s, 3, 9 ^ 9) dic2(arr(ii, 1)) = s
i = i + 1 Loop arr = dic2.KEYS arrT2 = dic2.ITEMS ReDim arrT1(1 To UBound(arr) + 1, 1 To 2) For i = 1 To UBound(arrT1) arrT1(i, 1) = arr(i - 1) arrT1(i, 2) = arrT2(i - 1) Next Sheets.Add [a1].Resize(UBound(arrT1), 2) = arrT1 Application.StatusBar = False End Sub
Sub SORT_() ActiveSheet.SORT.SortFields.Clear ActiveSheet.SORT.SortFields.Add2 _ Key:=Selection.Columns(1), SortOn:=xlSortOnValues, Order:=xlAscending, _ DataOption:=xlSortNormal ActiveSheet.SORT.SortFields.Add2 _ Key:=Selection.Columns(1), SortOn:=xlSortOnValues, Order:=xlAscending, _ DataOption:=xlSortNormal With ActiveSheet.SORT .SetRange Selection .Header = xlYes .MatchCase = False .Orientation = xlTopToBottom .SortMethod = xlPinYin .Apply End With End Sub
Я правильно понимаю, что для расчета используется только один диапазон, который выделяем?
да Процедуру сортировки можно убрать вообще - если там отсортировано по возрастанию квартиры внутри домов. Макрос делает проход по дому - и если дома вперемешку - будет что попало.
Я правильно понимаю, что для расчета используется только один диапазон, который выделяем?
да Процедуру сортировки можно убрать вообще - если там отсортировано по возрастанию квартиры внутри домов. Макрос делает проход по дому - и если дома вперемешку - будет что попало.SLAVICK