Страницы: Пред. 1 2 3 4 5 6 7 8 След.
RSS
Сортировка в двумерном массиве VBA Excel, Написал тут небольшую процедурку, может кому будет полезна
 
В текущем варианте - это надстройка-симбионт ускоряющая работу основного сортера на средних и больших массивах. Степень ускорения зависит от входных данных. При равномерном распределении, и диапазоне значений (числа) равными/кратными количеству элементов в сортируемом столбце массива, досортировка почти не требуется. Например "голые" (без временных нашлёпок) даты не нуждаются в досортировке - отсюда и высокая скорость обработки. Можно ли считать это РЕШЕНИЕМ? Отчасти, и в основном из уважения к труду AAF, т.к. именно его сортировщик делает основную работу.
---------------------------------
Изменения:
- исправил пару косяков
- добавлен Excel sort на стенд
- сам сортировщик от AAF перенесён в модуль к "клеверу", поэтому если кому нужно, то можно экспортировать модуль и вставить в свой проект.
- выгрузка итогов сортировки идет во входной массив.

Пример использования есть в коде тестового стенда. Более подробно в комментариях к самому сортеру-сателлиту.

П.С.: Изобретение "велосипеда" продолжается :)
---------------------------
Тест на 5КК*1 (картинка):
Скрытый текст
Есть еще вариант разбивки распределителя на модули (числа, даты, текст). Сократится кол-во переменных в каждом отдельно взятом модуле, уменьшится время индексации (не будет проверки на тип входных данных), на деиндексации тоже. Но это когда известно заранее, что за данные в массиве.  
Изменено: oldy7 - 15.01.2018 18:31:45
 
Можно. Но для того, чтобы перенести какую-то тему в Копилку, ее нужно сначала создать, изложить в сообщении функционал и описание, приложить файл.
 
oldy7, может еще прокатать пару раз, ибо мне кажется можно переписать код с оптимизацией с точки зрения интеграции в Ваше решение, а уже потом сделать описание и примеры... Но времени мало и недельку надо еще...  :)
Да, и еще compare text прикрутить надо.
Изменено: AAF - 14.01.2018 12:33:12
 
AAF, я его вчера гонял в течении дня в перерывах между домашними делами и просмотрами серила) Для Compare Text нужно переделывать текстовый распределитель. Я этим занимаюсь, но с целью оптимизации оного и вывода этого распределителя в самостоятельный алгоритм с минимальной досортировкой.
 
SortGen МассивДанных, Столбец, МассивИндексов(Возвращаемый), ПорядокСорта(-1/1)
Скрытый текст

SortStr МассивДанных, Столбец, МассивИндексов(Возвращаемый), ПорядокСорта(-1/1), CompareMode(0/1)
Скрытый текст

Отсортированный массив = SortFull(МассивДанных, Столбец/ы, optional МассивИндексов(Возвращаемый), optional ПорядокСорта(-1/1), optional CompareMode(0/1)
Скрытый текст

Результат складывается в порядке: НЕ текст, текст, "", Empty
Код
'Сортировка по нескольким столбцам
aResult = SortFull(a, Array(2, 1), , Array(1, -1), Array(1, 0))
'Сортировка по одному столбцу
aResult = SortFull(a, 2, , -1, 0)
Изменено: AAF - 15.01.2018 13:59:51
 
AAF, серьёзную работу провели. Мультисортинг при сортировке по индексам так и напрашивался :)
---------------
Исправил еще две ошибки в своем коде. Файлик в первом сообщении на этой странице обновил. Также провёл тест на компе на работе. Картинку тоже добавил.

Борюсь с текстовым распределителем. Вынес себе мозг многократно вложенными массивами:
Скрытый текст
Есть мысль перейти на UDT. Провел пару тестов - скорость записи/чтения высокая.
 
Цитата
oldy7 написал:
серьёзную работу провели
Ничего серьезного, механическая рутина...  :evil:
Цитата
oldy7 написал:
при сортировке по индексам так и напрашивался
Пример был в сообщении 39
Цитата
oldy7 написал:
Вынес себе мозг многократно
Есть такое, но хочется чего-то концептуального, что пройдет красной нитью через все решение, поэтому не делал лишних шагов, а остановился на достигнутом. (До UDT пока не дошел в данном применении :) )
А теперь робкое предложение (прошу не воспринимать как проявление безусловного эгоизма)
Это переписанный код SortFull:
Код
Function SortClever(a(), col, Optional aIdx, Optional orderSort, Optional compareMode)
'Может необходимо добавить еще какие-то входящие переменные
Dim aC() As Long, a0() As Long, cM As Byte, o As Integer, aResult()
Dim aSt0() As Long, aStr() As Long, aEmp() As Long, aNum() As Long
Dim iSt0 As Long, iStr As Long, iEmp As Long, iNum As Long
Dim i As Long, j As Long, x, c As Long, f As Boolean
'Dim какие-то Ваши переменные
If DBound(a) <> 2 Then Exit Function
If DBound(aIdx) = 1 Then
  a0 = aIdx
Else
  ReDim a0(LBound(a) To UBound(a))
  For i = LBound(a) To UBound(a): a0(i) = i: Next
End If
If IsArray(col) Then
  ReDim aC(LBound(col) To UBound(col))
  i = UBound(col)
  For Each x In col
    aC(i) = x: i = i - 1
  Next
Else
  ReDim aC(1 To 1): aC(1) = col
End If
ReDim aO(LBound(aC) To UBound(aC))
ReDim aM(LBound(aC) To UBound(aC))
i = UBound(aC)
If IsArray(orderSort) Then
  For Each x In orderSort
    If i < LBound(aC) Then Exit For
    If x = -1 Then aO(i) = -1 Else aO(i) = 1
    i = i - 1
  Next
  x = aO(i + 1)
  For i = i To LBound(aC) Step -1
    aO(i) = x
  Next
Else
  If orderSort = -1 Then x = -1 Else x = 1
  For i = i To LBound(aC) Step -1
    aO(i) = x
  Next
End If
i = UBound(aC)
If IsArray(compareMode) Then
  For Each x In orderSort
    If i < LBound(aC) Then Exit For
    If x < 1 Or x > 2 Then aM(i) = 0 Else aM(i) = x
    i = i - 1
  Next
  x = aM(i + 1)
  For i = i To LBound(aC) Step -1
    aM(i) = x
  Next
Else
  If compareMode < 1 Or compareMode > 2 Then x = 0 Else x = compareMode
  For i = i To LBound(aC) Step -1
    aM(i) = x
  Next
End If
For j = LBound(aC) To UBound(aC)
  c = aC(j): o = aO(j): cM = aM(j)
'  здесь мы имеем следующие данные:
'  a0 - массив индексов подвергаемые сортировке
'  a - сам собсно массив
'  c - текущий столбец сортировки
'  o - порядок сортировки
'  cM - метод сравнения
'  А теперь все что хочет Clever
'  далее страдаем сепаратизмом согласно некоего алгоритма
'  проводим сортировку внутри фрагментов сепаратора
'  для сортировки используем любые необходимые процедуры/функции
'  проводим сборку
'  получаем в результате a0 (отсортированные индексы)
'  a0 будет передано к следующей сортировке если она есть (столбец для сортировки не один)
Next
ReDim aResult(LBound(a0) To UBound(a0), LBound(a, 2) To UBound(a, 2))
For j = LBound(a, 2) To UBound(a, 2)
  For i = LBound(a0) To UBound(a0)
    aResult(i, j) = a(a0(i), j)
  Next
Next
aIdx = a0: Erase a0
SortClever = aResult
End Function

Просто ради унификации, но если это алгоритмически возможно, естественно :)
Изменено: AAF - 16.01.2018 12:53:26
 
AAF, если это попытка скрестить распределение с сортировкой под одной крышей, то есть пару нюансов:
- по результатам первичной сепарации данных вызов Вашего сортера идет для каждой ветви данных
- только по числам есть два вызова процедуры досортировки, т.е. всего четыре раза
- условия вызова в на каждой ветке немного отличаются (до определенного момента, а точнее до опред.кол-ва досортируемых элементов работает безсвопинговая "вставка" - она быстрее на мелких массивах). Плюс для чисел, дат и текста используются при сравнении разные переменные соотв.типа.
- и если произвести четыре включения Вашего кода в мой, то это будет ядреный листинг на много страниц) Он и так не маленький.
 
Цитата
oldy7 написал:
под одной крышей
Да нет, необязательно, я подразумевал только вызов оттуда одиночных сортировок, которые не переписывают входящий массив, а возвращают отсортированные индексы в виде массива и все. :)
Изменено: AAF - 16.01.2018 12:46:54
 
Цитата
AAF написал:
я подразумевал только вызов оттуда одиночных сортировок, которые не переписывают входящий массив, а возвращают отсортированные индексы в виде массива
Так там (в "клевере") и так можно гонять внутренний сортировщик по столбцам, нужно лишь добавить внешний цикл и  первичную индексацию по первому столбцу из тех, что будут сортироваться. Поэтому там перед окончательной выгрузкой идет сборка субиндексных массивов в один.

Кстати, попробую обойтись без этой сборки, а писать сразу в один общий раз ограничители этих массивов (по типам данных) вычисляются в самом начале. Все быстрее будет)

П.С.: По Вашему примеру убрал замену пустот на массив с индексами по пустотам.
--------------------
Добавил мультисортинг, убрал фильтр (он не стыкуется с сортировкой по нескольким столбцам). Параметров процедуры осталось всего два: вариантный одномерный массив, в котором номера столбцов чередуются с флагами сортировки (False - по возрастанию, True - по убыванию), сортируемый массив. Проверки с автокоррекцией содержимого массива с номерами столбцов нет. Также добавлен распределитель для отрицательных чисел - не тестировался.
--------upd-------
Провёл небольшую оптимизацию по числовым сортировкам и промежуточной сшивке индексных массивов в один.
Изменено: oldy7 - 17.01.2018 18:07:04
 
oldy7, добавил всего одно условие в обе процедуры для оптимизации скорости, после Вашего сепаратора, т.к. при среднестатичстическом распределении это не уменьшит время... Времени на тестирование к сожалению не было... :(
Скрытый текст

Скрытый текст
 
AAF, да, согласен. Гонял миллионы тестируя и оптимизируя обновленный сортер для чисел (все-таки вывел в самостоятельный алгоритм) - по окончательному времени сортировки если и есть разница, то несущественная (это я о сравнении итоговой производительности/скорости). Ваш обновленный вариант еще не тестировал.

Можно без мин-максов попытаться начать распределение, но тогда все сливки в виде равномерно распределенных целых чисел или дат будут пропущены и результат будет хуже. После ряда экспериментов выяснил, что делать ответный массив для распределения индексов равный высоте исходного или диапазону от минимума к максимуму не обязательно. Собственно чем больше массив для распределения, тем дольше идет это самое распределение. В общем достаточно 20%. Мелкие группы индексов добивать вставками, крупные рубить на части. Также замечено, что плавное изменение измерения массива при ReDim Preserve намного дольше, чем так называемое "авансовое" или скачкообразное. Методом научного тыка определил, что определение типа данных быстрее через VarType.
--------- upd ------------
обновил файлик, в прошлом была ошибка.
--------- upd ------------
Изменил систему первоначального распределения по типам данных. Попутно для сравнения на стенд добавил вариант "клевера" со старой системой дифференциации типов и связке AAF Sort + Clever на числах. Убрал нафиг глючный Excel sort со стенда.
--------- upd ------------
Исправил ряд ошибок + стабилизировал по времени. Был разброс по малым (до 100000) массивам от долей секунды до полутора-двух секунд. Всему виной оказались числа с плавающей точкой с мизерным интервалом по значению...
Изменено: Anchoret - 23.01.2018 10:14:41
 
В попытках превзойти связку сортера от AAF с моим распределителем по части текста написал пару альтернативных вариантов.
Алгоритм 1 (LSD - по младшему разряду):
Скрытый текст
Результат по времени
Скрытый текст
Вот собственно и код этого творения:
Скрытый текст

Алгоритм 2 (MSD по старшему разряду):
Скрытый текст
Результат по времени:
Скрытый текст
Код:
Скрытый текст
------------
Маленький эксперимент - попытка скрестить "расчёску" и "вставки":
Скрытый текст
Время вполне приемлемое)
Изменено: Anchoret - 02.02.2018 14:26:21
 
AAF, как писал один из критиков моих потугов на ниве сортерописания в этой теме: "Снимаю шляпу!". Ваш сортировщик лучший из симбионтов по части текста.

П.С.: По тексту откатил все почти в исходное состояние с мелкими коррективами. Немного модернизировал сортер AAF - вместо перезаписи в конце сортировки промежуточного индексного массива в исходный сделал запись в итоговый индексный массив. На доли секунды стало быстрее)
Изменено: Anchoret - 03.02.2018 17:14:25
 
Ну и итоговая версия.

Изменения:
- все субсортеры разнесены по отдельным процедурам
- числовой сортер стал универсальным по положительным и отрицательным числам
- даты отданы на обработку числовому сортеру

Что еще можно сделать:
- продолжить модульную дифференциацию (в доп.к сборному универсальному сортеру добавить ряд процедур обрабатывающих только: числа, даты и текст без предварительного определения типов данных). Это сократит время на первичный анализ массива на типы данных в нем представленные. Но нужно заранее знать, что здесь те или иные типы.
- переделать итоговую деиндексацию массива с целью сократить затраты по памяти.
-----------
Обновил файл, в прошлом была серьезная ошибка.
-----------
Ошибка была не одна... А точнее VBA своеобразно распознает вариантные даты и конвертирует их в числа. Даты отдал соотв.распределителю с досортировкой (если вдруг там будут не только даты, но и время) сортеру от AAF.
Изменено: Anchoret - 05.02.2018 05:59:43
 
Размещено в Копилке
 
Господа, браво!
А расскажите, как юзать, пожалуйста)))
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Jack Famous,
Код
arr = Array(yy, False)
Call CleverSortNew(arr(), qwerty)
CleverSortNew arr(), qwerty
где в одномерном arr() - номер сортируемого столбца, направление сортировки (False - по возрастанию, True - по убыванию).  Столбцов может быть несколько, но по каждому нужно указывать направление сортировки. Т.е. мульти сортинг.
qwerty - сортируемый массив.
Или пройти по ссылке от vikttur.
 
Anchoret, спасибо - глянул. А пример мультисортировки двумерного массива как будет выглядеть? (в Копилке нет)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Jack Famous,
Код
arr = Array(3, False,2, True,1, False)
Call CleverSortNew(arr(), qwerty)
По трем столбцам.
 
Anchoret, спасибо большое! попробую))
Изменено: Jack Famous - 06.02.2018 19:29:35
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Jack Famous, если копнуть код, то можно легко заменить вводные параметры касательно направления сортировки. В коде за это отвечает переменная "d". Т.е. заменить булевы значения на 1/-1 например.
 
Anchoret, в копилке в примере надо добавить End Sub
 
AAF, я знаю) Торопился с утра перед работой, пытался втиснуться в рамки 100 кб, удалил ряд процедур с функциями и этот несчастный "End Sub"... Мне там доступ закрыт на редактирование. В этой теме рабочий вариант.
 
Цитата
Anchoret написал:  В этой теме рабочий вариант.
Заменил файл в Копилке.
 
Тема сепарации оказалась весьма заразной (Спасибо Anchoret :) ). После тщетных попыток выбросить ее из головы, понял что единствый способ отвязаться что-нить нарисовать...
Цитата
Anchoret написал:
По тексту откатил все почти в исходное состояние
Да, я тоже не нашел стабильного (универсального) решения и от сепарации по тексту решил отказаться вообще. Вот если б была функция, которая из n первых символов строки возвращала n-массив их кодов, вот тогда я б развернулся, кроме того сепарацию можно было сделать регулируемой в зависимости от характера данных. А я не знаю таких способов.
Стандартными методами, типа, Asc(), AscW(), а если текстовое сравнение, то еще и UCase()... Все это напоминает шахту по добыче угля, где добытого угля более или менее хватает на отопление шахтерского поселка и то не факт. Вот если данные такого типа, то время потраченное на сепарацию можно считать убитым, но неотъемлемой частью времени потраченного на сортировку.  :evil: Может быть есть другие пути.....
Поэтому ограничился такой схемой сепаратора:
Код
Значение
    Текст
        Len(строки)=0
        Len(строки)>0
    Не текст
        Empty
        Не Empty (и вот для них один общий сепаратор на весь диапазон Double)

Кроме того обработал напильником свои ранее выложенные коды, что б поменьше смахивали на портянку, убрал косяки связанные с граничными условиями и добавил два новых параметра, позволяющими применить к сортировке участок индексов (по информации с сепаратора) не переписывая весь массив индексов.
Вообщем вот код, а файл с генератором таблицы до 10 столбцов и строк до размера листа с генерацией числовых, текстовых и смешанных столбцов, а также с листом результатов, на котором можно созерцать вложенную сортировку выложу вечером.

ОтсортированныйМассив = SortAll(ДвумерныйМассив, СтолбецСортировки, МассивИндексов, НаправлениеСортировки, УчетРегистра)
Скрытый текст

SortBin ДвумерныйМассив, СтолбецСортировки, МассивИндексов, НаправлениеСортировки, НачалоУчастка, КонецУчастка
Скрытый текст

SortStr ДвумерныйМассив, СтолбецСортировки, МассивИндексов, НаправлениеСортировки, УчетРегистра, НачалоУчастка, КонецУчастка
Скрытый текст
Изменено: AAF - 02.03.2018 15:10:45
 
Цитата
AAF написал:
Вот если б была функция, которая из n первых символов строки возвращала n-массив их кодов, вот тогда я б развернулся, кроме того сепарацию можно было сделать регулируемой в зависимости от характера данных. А я не знаю таких способов.
Функция то есть, но она работает со всей строкой.
Код
ByteArr=StrConv(string,128)
На массиве строк 1кк 26 символов длиной на интелах обработка занимает порядка секунды. Моя домашняя AMDшка в два раза дольше обрабатывает.
Можно конечно объявить строковую переменную с фиксированной длиной, но с фикс.строками VBA работает дольше.
Эмпирическим путем было установлено, что такая конструкция работает не слишком долго:
Код
txt=arr(idx, n)
if len(txt)<4 then txt=txt & "   "
ByteArr(StrConv(txt,128)
Есть даже функция по поиску вхождения байтового массива в другой байтовый массив - InStrB
 
Anchoret, будем посмотреть, спасибо.
 
Я пока отошел от темы мега сортеров по причине их непрактичности с точки зрения памяти. Потому как высокая скорость сортировки нужна на очень больших объемах данных. Последний из предложенных вариантов максимум мог работать с 10кк потом OutOfMemory. а на массивах до 100к хватает и досортировщика вроде этого:
Код
Sub ArrSort(mass(), ByVal n%)
Dim a&, b&, c&, i&, xx&, jj&, mm, x1&
Dim arr&(), arr0&(), sArr()
  If UBound(mass, 1) < 2 Then Exit Sub
  ReDim arr(1 To UBound(mass, 1))
  ReDim arr0(1 To UBound(mass, 1)): xx = 1
  For i = 1 To UBound(mass, 1): arr(i) = i: Next
  b = UBound(mass, 1): c = b / 1.247331: i = 1
  Do While c > 2
    Do While i + c <= b
      If mass(arr(i), n) > mass(arr(i + c), n) Then
        x1 = arr(i): arr(i) = arr(i + c): arr(i + c) = x1
      End If
      i = i + 1
    Loop
    c = c / 1.247331: i = 1
  Loop
  jj = xx: arr0(xx) = arr(1)
  For c = 2 To b
    xx = xx + 1: x1 = xx
    mm = mass(arr(c), n)
    Do While mass(arr0(x1 - 1), n) > mm
      arr0(x1) = arr0(x1 - 1): x1 = x1 - 1
      If x1 = jj Then Exit Do
    Loop
    arr0(x1) = arr(c)
  Next
  ReDim sArr(1 To UBound(mass, 1), 1 To UBound(mass, 2))
  For a = 1 To UBound(arr0)
    For c = 1 To UBound(mass, 2)
      sArr(a, c) = mass(arr0(a), c)
    Next c
  Next a: Erase arr: Erase arr0
  mass = sArr: Erase sArr
End Sub
Изменено: Anchoret - 28.02.2018 19:32:09
 
Цитата
Anchoret написал:
Я пока отошел от темы мега сортеров по причине их непрактичности с точки зрения памяти
Мегасортеры начинают меркнуть перед ресурсами сжираемыми мегаданными, особенно двумерными. Так что при увеличении размеров данных эффективность мегасортеров растет. И если компьютер загружен под завязку только исходным массивом, то это проблема уже не мегасортера, а, скорее частная ситуация.  ;)
Да и потом я б свой сортер вряд ли назвал мега, хоть 10 лямов он достойно протянет (я еще проверю, но должен :) ) на 4Gb ОЗУ. Но все зависит от характера входных данных.
А эффективность зачастую теряет в весе перед творческим интересом. Хотя мой интерес не только творческий, т. к. я обработку строю на таком методе сравнения данных и если я запущу в этот огород Excelсортер, то получу проблемы из-за своеобразного порядка сортировки последнего...
Изменено: AAF - 28.02.2018 14:57:27
Страницы: Пред. 1 2 3 4 5 6 7 8 След.
Наверх