Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - Общий форум

Страница: 1 | 2 | 3 | 4 | 5 |

 

  Вопрос: Удаление дубликатов из массива Добавлено: 11.11.05 15:04  

Автор вопроса:  AsHeS | ICQ: 229759992 

Ответить

  Ответы Всего ответов: 66  

Номер ответа: 16
Автор ответа:
 GSerg



Вопросов: 0
Ответов: 1876


 Профиль | | #16 Добавлено: 12.11.05 23:58
VB занимается обнулением внутренних переменных всегда.
И мы сейчас обсуждаем не скорость сортировки таких-то типов, а отличие рекурсивной сортировки от нерекурсивной (в пределах одного любого типа).

Ответить

Номер ответа: 17
Автор ответа:
 Victor



ICQ: 345743490 

Вопросов: 42
Ответов: 385
 Web-сайт: vt-dbnz.narod.ru
 Профиль | | #17
Добавлено: 13.11.05 00:16
И вообще. Я почти уверен, что причина разницы в скоростях кроется где-то в вариантах. Ну по тому, что не может процедура так долго вызываться, чтоб на фоне работы с вариантами давать замедление в десятки раз...
Не могу однако сказать, в чем именно проблема. Надо подробнее разбираться в этих реализациях.
Но вообще это действительно очень интересное наблюдение.

Ответить

Номер ответа: 18
Автор ответа:
 GSerg



Вопросов: 0
Ответов: 1876


 Профиль | | #18 Добавлено: 13.11.05 00:36
Этому очень интересному наблюдению фиг знает сколько лет.
Потому и родился совет (не у меня родился, не подумай уж) не использовать рекурсивную сортировку в VB...

Ответить

Номер ответа: 19
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #19 Добавлено: 13.11.05 08:07
GSerg
Да заметил я, что массив типа вариант. Потому и запостил.
Зачем нужно приводить (извиняюсь за термин) отстойную реализацию алгоритма, сделаную криво да косо, в качестве подтверждения мысли о недопустимости рекурсии - я не понял.
Так же непонятно, почему был приведен кусок кода, в котором сортируется вариант, в то время как человеку нужно сортировать строки?
Может вы невнимательно прочитали вопрос?

Ответить

Номер ответа: 20
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #20
Добавлено: 13.11.05 08:30
Скорее всего из за универсальности. Сортировка с вариантным массивом может быть использована для любого типа данных, а если невариантно, то прийдётся писать для каждого типа отдельную процедуру.

Ответить

Номер ответа: 21
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #21 Добавлено: 13.11.05 08:42
Если сделать грамотно, то отличается только процедура сравнения двух элементов массива. Весь остальной код один и тот же для любого типа данных, будь то integer, string или double

Ответить

Номер ответа: 22
Автор ответа:
 GSerg



Вопросов: 0
Ответов: 1876


 Профиль | | #22 Добавлено: 13.11.05 14:55
Зачем нужно приводить (извиняюсь за термин) отстойную реализацию алгоритма, сделаную криво да косо

В чём кривизна, очень хочу узнать.

Так же непонятно, почему был приведен кусок кода, в котором сортируется вариант, в то время как человеку нужно сортировать строки?

Видеть проблему там, где её нет, просто потому, что неохота принять иную точку зрения - человеческое свойство, мне непонятное.

Ok, не вопрос.
Переделываем пример на строки.

Form1
Option Explicit

Private Declare Function GetTickCount Lib "kernel32" () As Long

Private Type TimeExp
  Bubble As Long
  Exchange As Long
  Heap As Long
  Insertion As Long
  Quick As Long
  QuickNonRec As Long
  Shelll As Long
End Type

Private Sub Form_Load()
  Dim a As New Sorting, i As Long, MyArr(-800 To 4000) As String, Reserve(-800 To 4000) As String
  Dim StartTime As Long
  Dim TotalRanks As TimeExp
  
  For i = LBound(MyArr) To UBound(MyArr)
    MyArr(i) = CStr(Rnd * 800)
    Reserve(i) = MyArr(i)
  Next
  
  Randomize Timer

  StartTime = GetTickCount
  a.QuickSort MyArr, LBound(MyArr), UBound(MyArr)
  TotalRanks.Quick = GetTickCount - StartTime
  'SaveArr App.Path + "\Quick.txt", MyArr()
  RestoreArr Reserve(), MyArr()
  
  StartTime = GetTickCount
  a.QuickSortNonRecursive MyArr ', LBound(MyArr), UBound(MyArr)
  TotalRanks.QuickNonRec = GetTickCount - StartTime
  'SaveArr App.Path + "\Quick.txt", MyArr()
  RestoreArr Reserve(), MyArr()
  
  MsgBox "Ага! Вот результаты:" & vbNewLine & vbNewLine & "Пузырёк    " & TotalRanks.Bubble & vbNewLine & _
                                                          "Обмен      " & TotalRanks.Exchange & vbNewLine & _
                                                          "Куча       " & TotalRanks.Heap & vbNewLine & _
                                                          "Вставка    " & TotalRanks.Insertion & vbNewLine & _
                                                          "Быстрая    " & TotalRanks.Quick & vbNewLine & _
                                                          "Быстрая нерекурсивная    " & TotalRanks.QuickNonRec & vbNewLine & _
                                                          "Shell      " & TotalRanks.Shelll & vbNewLine

End Sub

Private Sub RestoreArr(From() As String, Where() As String)
  Dim i As Long
  For i = LBound(From) To UBound(From)
    Where(i) = From(i)
  Next
End Sub


Sorting.cls
Option Explicit

Private Type QuickStack
  Low As Long
  High As Long
End Type

Private Sub SwapString(a As String, b As String)
  Dim tmp As String
  tmp = a: a = b: b = tmp
End Sub

Private Function RandInt(Lower As Long, Upper As Long) As Long
  RandInt = Int(Rnd * (Upper - Lower + 1)) + Lower
End Function

Public Sub QuickSort(SortArray() As String, Low As Long, High As Long)
  Dim RandIndex As Long, Partition As String
  Dim i As Long, j As Long
  
  If Low < High Then
    If Abs(High - Low) = 1 Then 'Abs заюзан опять-таки из-за возможности отриц. индексов
      ' Если у нас два элемента в куске, то правильно их расставляем
      ' и прекращаем рекурсию:
      If SortArray(Low) > SortArray(High) Then Swap SortArray(Low), SortArray(High)
    Else 'Нет, больше двух элементов в куске!
      ' Выбираем случайный элемент, двигаем его в конец:
      RandIndex = RandInt(Low, High)
      SwapString SortArray(High), SortArray(RandIndex)
      Partition = SortArray(High)
      Do
        ' Идём с обоих сторон по направлению к "центральному":
        i = Low: j = High
        Do While (i < j) And (SortArray(i) <= Partition)
          i = i + 1
        Loop
        Do While (j > i) And (SortArray(j) >= Partition)
          j = j - 1
        Loop
        ' Если мы не достигли "центрального", это значит, что два
        ' элемента любой стороне в неправильном порядке, меняем их:
        If i < j Then SwapString SortArray(i), SortArray(j)
      Loop While i < j

      ' Двигаем центральный обратно на его место в массиве:
      SwapString SortArray(i), SortArray(High)
      
      ' Рекурсивно вызываемся (передаём сначала меньший кусок, чтобы занять
      ' меньше стекового пространства):
      If (i - Low) < (High - i) Then
        QuickSort SortArray, Low, i - 1
        QuickSort SortArray, i + 1, High
      Else
        QuickSort SortArray, i + 1, High
        QuickSort SortArray, Low, i - 1
      End If
    End If
  End If
End Sub

Public Sub QuickSortNonRecursive(SortArray() As String)
  Dim i As Long, j As Long, lb As Long, ub As Long
  Dim stack() As QuickStack, stackpos As Long, ppos As Long, pivot As String
  
  ReDim stack(1 To 1024)
  stackpos = 1

  stack(1).Low = LBound(SortArray)
  stack(1).High = UBound(SortArray)
  
  Do
    'Взять границы lb и ub текущего массива из стека.
    lb = stack(stackpos).Low
    ub = stack(stackpos).High
    stackpos = stackpos - 1
    
    Do
      'Шаг 1. Разделение по элементу pivot
      ppos = (lb + ub) \ 2
      i = lb: j = ub: pivot = SortArray(ppos)
      
      Do
        Do While SortArray(i) < pivot: i = i + 1: Loop
        Do While pivot < SortArray(j): j = j - 1: Loop
        
        If i <= j Then
          SwapString SortArray(i), SortArray(j)
          i = i + 1
          j = j - 1
        End If
      Loop While i <= j
      
      'Сейчас указатель i указывает на начало правого подмассива,
      'j - на конец левого lb ? j ? i ? ub.
      'Возможен случай, когда указатель i или j выходит за границу массива
      
      'Шаги 2, 3. Отправляем большую часть в стек  и двигаем lb,ub
      
      If i < ppos Then   'правая часть больше
        If i < ub Then
          stackpos = stackpos + 1
          stack(stackpos).Low = i
          stack(stackpos).High = ub
        End If
        ub = j          'следующая итерация разделения будет работать с левой частью
      Else
        If j > lb Then
          stackpos = stackpos + 1
          stack(stackpos).Low = lb
          stack(stackpos).High = j
        End If
        lb = i
      End If
    Loop While lb < ub
  Loop While stackpos
End Sub



Теперь какие замечания?

Может вы невнимательно прочитали вопрос?

Мы внимательно читали вопрос.
Дискуссия ушла немного в сторону от сабжевого вопроса несколько постов назад.

Ответить

Номер ответа: 23
Автор ответа:
 GSerg



Вопросов: 0
Ответов: 1876


 Профиль | | #23 Добавлено: 13.11.05 15:07
В процедуре QuickSort строку
If SortArray(Low) > SortArray(High) Then Swap SortArray(Low), SortArray(High)

заменить на
If SortArray(Low) > SortArray(High) Then SwapString SortArray(Low), SortArray(High)



Забыл одну.
Впрочем, на картину это не влият ваапще.

Ответить

Номер ответа: 24
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #24 Добавлено: 13.11.05 15:32
Ну раз дискуссия ушла в сторону, то:

Первый вопрос - вариант - вроде решен.

Второй вопрос:
Partition As String
Partition = SortArray(High)
Do While (i < j) And (SortArray(i) <= Partition)


Зачем создавать постоянно копию строки, и сравнивать с копией? На выделение памяти под строку и на копирование уходит ведь достаточно много времени? Почему не избавиться от Partition, и не сравнивать напрямую SortArray(i) с SortArray(High) ?

Третий вопрос:
RandIndex = RandInt(Low, High)

Почему нерекурсивному варианту отказано хотя бы в ppos = (lb + ub) \ 2 ?
Не говоря уже о том, чтобы выбрать по принципу: средний из первого, последнего и стоящего в середине. Такой принцип позволяет сократить количество проходов ~ на 17%. Достаточно заметная величина.

Ответить

Номер ответа: 25
Автор ответа:
 GSerg



Вопросов: 0
Ответов: 1876


 Профиль | | #25 Добавлено: 13.11.05 19:03
Странные люди...
17%, 18%, 20%...

Разница в 50 раз.

Ответить

Номер ответа: 26
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #26 Добавлено: 13.11.05 19:43
В сравнении с чем разница в 50 раз? Сравнение варианта "мог бы" с вариантом "на самом деле"? Тогда чем характеризуется вариант "мог бы"? Отказом от рекурсии, или может отказом от VB? Откуда такая цифра - 50, с какого потолка взята?
Можно ли это уточнить?

Ответить

Номер ответа: 27
Автор ответа:
 GSerg



Вопросов: 0
Ответов: 1876


 Профиль | | #27 Добавлено: 13.11.05 20:14
Разница в 50 раз - между приведёнными QuickSort и QuickSortNonRecursive.

Запускал код хоть?

Ответить

Номер ответа: 28
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #28 Добавлено: 13.11.05 22:32
Запускал :)
В отличие от оппонента не только кривую реализацию Strings.cls :)

Этот класс я дополнил двумя процедурами: quick_sort и partition, которые реализуют нормальный QuickSort.

Результаты:
Быстрая a-la GSerg 563 мс
Быстрая нерекурсивная 16 мс
Нормальная quick_sort 0~15 мс

Код с изменениями:

Option Explicit

Private Declare Function GetTickCount Lib "kernel32" () As Long

Private Type TimeExp
    Bubble As Long
    Exchange As Long
    Heap As Long
    Insertion As Long
    Quick As Long
    QuickNonRec As Long
    Shelll As Long
End Type

Private Sub Form_Load()
    Dim a As New Sorting, i As Long, MyArr(-800 To 4000) As String, Reserve(-800 To 4000) As String
    Dim StartTime As Long
    Dim TotalRanks As TimeExp
  
  
    For i = LBound(MyArr) To UBound(MyArr)
        MyArr(i) = CStr(Rnd * 800)
        Reserve(i) = MyArr(i)
    Next
   
    Randomize Timer

    StartTime = GetTickCount
    a.QuickSort MyArr, LBound(MyArr), UBound(MyArr)
    TotalRanks.Quick = GetTickCount - StartTime
    For i = -800 To 4000
        list1.AddItem MyArr(i)
    Next i
  
    RestoreArr Reserve(), MyArr()
   
    StartTime = GetTickCount
    a.QuickSortNonRecursive MyArr ', LBound(MyArr), UBound(MyArr)
    TotalRanks.QuickNonRec = GetTickCount - StartTime
    For i = -800 To 4000
        list2.AddItem MyArr(i)
    Next i
  
  
    '---------------------------------------------------------------
    Dim arr(4800) As String
    For i = 0 To 4800
        arr(i) = Reserve(i - 800)
    Next i
    '-------------- сортировка ------------------------------------
    StartTime = GetTickCount
    a.quick_sort arr, 0, 4800
    TotalRanks.Shelll = GetTickCount - StartTime
    '--------------- проверка -------------------------------------
    For i = 0 To 4800
        list3.AddItem arr(i)
    Next i

    MsgBox "Ага! Вот результаты:" & vbNewLine & vbNewLine & "Быстрая a-la GSerg    " & TotalRanks.Quick & vbNewLine & _
                                                          "Быстрая нерекурсивная    " & TotalRanks.QuickNonRec & vbNewLine & _
                                                          "Нормальная quick_sort     " & TotalRanks.Shelll & vbNewLine
    
    
End Sub

Private Sub RestoreArr(From() As String, Where() As String)
    Dim i As Long
    For i = LBound(From) To UBound(From)
        Where(i) = From(i)
    Next
End Sub


Sorting.cls

Option Explicit

Private Type QuickStack
  Low As Long
  High As Long
End Type

Private Sub SwapString(a As String, b As String)
  Dim tmp As String
  tmp = a: a = b: b = tmp
End Sub

Private Function RandInt(Lower As Long, Upper As Long) As Long
  RandInt = Int(Rnd * (Upper - Lower + 1)) + Lower
End Function

Public Sub QuickSort(SortArray() As String, Low As Long, High As Long)
  Dim RandIndex As Long ', Partition As String
  Dim i As Long, j As Long
   
  If Low < High Then
    If Abs(High - Low) = 1 Then 'Abs заюзан опять-таки из-за возможности отриц. индексов
      ' Если у нас два элемента в куске, то правильно их расставляем
      ' и прекращаем рекурсию:
      If SortArray(Low) > SortArray(High) Then SwapString SortArray(Low), SortArray(High)
    Else 'Нет, больше двух элементов в куске!
      ' Выбираем случайный элемент, двигаем его в конец:
      RandIndex = RandInt(Low, High)
      SwapString SortArray(High), SortArray(RandIndex)
      'Partition = SortArray(High)
      Do
        ' Идём с обоих сторон по направлению к "центральному":
        i = Low: j = High
        Do While (i < j) And (SortArray(i) <= SortArray(High))
          i = i + 1
        Loop
        Do While (j > i) And (SortArray(j) >= SortArray(High))
          j = j - 1
        Loop
        ' Если мы не достигли "центрального", это значит, что два
        ' элемента любой стороне в неправильном порядке, меняем их:
        If i < j Then SwapString SortArray(i), SortArray(j)
      Loop While i < j

      ' Двигаем центральный обратно на его место в массиве:
      SwapString SortArray(i), SortArray(High)
       
      ' Рекурсивно вызываемся (передаём сначала меньший кусок, чтобы занять
      ' меньше стекового пространства):
      If (i - Low) < (High - i) Then
        QuickSort SortArray, Low, i - 1
        QuickSort SortArray, i + 1, High
      Else
        QuickSort SortArray, i + 1, High
        QuickSort SortArray, Low, i - 1
      End If
    End If
  End If
End Sub

Public Sub QuickSortNonRecursive(SortArray() As String)
  Dim i As Long, j As Long, lb As Long, ub As Long
  Dim stack() As QuickStack, stackpos As Long, ppos As Long, pivot As String
   
  ReDim stack(1 To 1024)
  stackpos = 1

  stack(1).Low = LBound(SortArray)
  stack(1).High = UBound(SortArray)
   
  Do
    'Взять границы lb и ub текущего массива из стека.
    lb = stack(stackpos).Low
    ub = stack(stackpos).High
    stackpos = stackpos - 1
     
    Do
      'Шаг 1. Разделение по элементу pivot
      ppos = (lb + ub) \ 2
      i = lb: j = ub: pivot = SortArray(ppos)
       
      Do
        Do While SortArray(i) < pivot: i = i + 1: Loop
        Do While pivot < SortArray(j): j = j - 1: Loop
         
        If i <= j Then
          SwapString SortArray(i), SortArray(j)
          i = i + 1
          j = j - 1
        End If
      Loop While i <= j
       
      'Сейчас указатель i указывает на начало правого подмассива,
      'j - на конец левого lb ? j ? i ? ub.
      'Возможен случай, когда указатель i или j выходит за границу массива
       
      'Шаги 2, 3. Отправляем большую часть в стек  и двигаем lb,ub
       
      If i < ppos Then   'правая часть больше
        If i < ub Then
          stackpos = stackpos + 1
          stack(stackpos).Low = i
          stack(stackpos).High = ub
        End If
        ub = j          'следующая итерация разделения будет работать с левой частью
      Else
        If j > lb Then
          stackpos = stackpos + 1
          stack(stackpos).Low = lb
          stack(stackpos).High = j
        End If
        lb = i
      End If
    Loop While lb < ub
  Loop While stackpos
End Sub

'--- нормальная реализация -----------

Private Function partition(a() As String, Low As Long, High As Long) As Long
    Dim p As Long, pivot As String, t As String
    Dim i As Long, j As Long
    
    p = Low + ((High - Low) \ 2)
    pivot = a(p)
    a(p) = a(Low)
    i = Low + 1
    j = High
    
    Do While True
        Do While (i < j And (pivot > a(i))):     i = i + 1: Loop
        Do While (j >= i And (a(j) > pivot)):    j = j - 1: Loop
        If (i >= j) Then Exit Do
        t = a(i)
        a(i) = a(j)
        a(j) = t
        j = j - 1
        i = i + 1
    Loop
    a(Low) = a(j)
    a(j) = pivot
    
    partition = j
    
End Function

Public Sub quick_sort(SortArray() As String, Low As Long, High As Long)
    Dim m As Long
    
    Do While Low < High
        m = partition(SortArray(), Low, High)
        If (m - Low <= High - m) Then
            quick_sort SortArray(), Low, m - 1
            Low = m + 1
        Else
            quick_sort SortArray(), m + 1, High
            High = m - 1
        End If
    Loop

End Sub



В форму добавлены 3 листбокса list1, list2, list3 для проверки результатов, если будут сомнения :)

Ну как, сделаем в 50 раз быстрее?

Ответить

Номер ответа: 29
Автор ответа:
 GSerg



Вопросов: 0
Ответов: 1876


 Профиль | | #29 Добавлено: 14.11.05 01:17
Замечание первое.
Выражение "a-la GSerg" уместно не совсем, ибо все приведённые сортировки, кроме быстрой нерекурсивной, написаны в Microsoft и шли в качестве примера.

Замечание второе.
Я не зря отсортированный массив в файл сбрасываю.
Я его потом смотрю. Экселем тем же.
Так вот функция a-la Microsoft сортирует, а функция a-la HOOLIGAN выдаёт в любом порядке, кроме отсортированного.
Небольшой отрывок из середины файла, созданного функцией a-la HOOLIGAN:
"110,9859"
"116,0003"
"119,3966"
"119,9849"
"120,249"
"121,3256"
"121,405"
"122,0798"
"122,1415"
"124,3485"
"786,5334"
"793,9324"
"796,2326"
"797,4592"
"124,5305"
"125,0418"
"128,3532"
"129,395"
"130,2573"
"130,3536"
"131,3873"
"134,2103"
"135,8988"
"139,0187"

Ответить

Номер ответа: 30
Автор ответа:
 Neco



ICQ: 247906854 

Вопросов: 133
Ответов: 882
 Web-сайт: neco.pisem.net
 Профиль | | #30
Добавлено: 14.11.05 02:40
Кстати говоря, куча даёт лучшие результаты в откомпиленном варианте.

Ответить

Страница: 1 | 2 | 3 | 4 | 5 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам