PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » Вопросы по PureBasic » Быстрый способ найти в списке прямоугольник, по координате.


Быстрый способ найти в списке прямоугольник, по координате.

Сообщений 1 страница 30 из 53

1

Привет ребят! Я ищу самый быстрый алгоритм.
Некоторые способы придумал. Но, пока-что самым быстрым у меня остаётся перебор всех элементов списка.
Суть вот в чём. Есть список, с координатами и размерами прямоугольников. Список может быть очень большой.
Задача состоит в том, что нужно максимально быстро найти любой элемент списка, который соответствует указанной координате. Первый созданный элемент, самый нижний, последний, самый верхний. Поэтому, если последний и первый находятся в указанных координатах, то должен быть найден последний.
Нужно учитывать, что элементы списка, могут изменить свои координаты и размеры. Поэтому, каждый новый поиск, это должен учитывать.
Вот код с визуальной демонстрацией и примером поиска по всему списку от последнего к первому:

Код:
Structure DefRect
  x.w
  y.w
  w.u
  h.u
  name.s
EndStructure

Declare IsDebuggerEnabled()

Global gCicle ; Здесь количество проходов, в тесте времени

If IsDebuggerEnabled()
  gCicle = 1000  ; с отладчиком
Else
  gCicle = 10000 ; без отладчика
EndIf


Global NewList gObject.DefRect()

Global gDefaultFont = LoadFont(#PB_Any, "SegoeUI", 8) 
Global gSize

; Отвечает, включён ли отладчик
Procedure IsDebuggerEnabled() ; https://www.purebasic.fr/english/viewtopic.php?t=19001
  !if defined _PB_DEBUGGER_Control
  !mov eax, 1
  !else
  !mov eax, 0
  !end if
  ProcedureReturn
EndProcedure

; Здесь просто вывод сообщения. Если отладчик включён, то вывод в Debug, иначе в MessageRequester
Procedure _P( Text$ ) 
  If IsDebuggerEnabled()
    Debug Text$
  Else
    MessageRequester("Debug",Text$)
  EndIf
EndProcedure


; Вариант 1. Поиск объекта под точкой. Простой перебор сверху вниз
Procedure Search_1( x.w, y.w )
  Protected *Object.DefRect
  If gSize
    PushListPosition( gObject() )
    LastElement( gObject() )
    Repeat
      With gObject()
        If x>=\x And x<\x+\w And y>=\y And y<\y+\h
          *Object = @gObject()
          Break
        EndIf
      EndWith
    Until Not PreviousElement( gObject() )
    PopListPosition( gObject() )
  EndIf
  ProcedureReturn *Object
EndProcedure


Procedure AddRect( x.w, y.w, w.u, h.u )
  ; добавляем элемент и вбиваем данные
  AddElement( gObject() )
  With gObject()
    \x = x : \y = y : \w = w : \h = h: \name = Str(ListIndex(gObject()))
  EndWith
  gSize = ListSize(gObject()) ; запоминаем размер
EndProcedure

; для прорисовки всплывающего сообщения
Global gToggle = 0, gOldX, gOldY
Procedure _PushCanvas( x, y, w, h )
  If Not gToggle And GrabDrawingImage( 0, x, y, w, h )
    gToggle = 1
    gOldX = x
    gOldY = y
  EndIf
EndProcedure
; для прорисовки всплывающего сообщения
Procedure _PopCanvas()
  If gToggle
    DrawImage( ImageID(0), gOldX, gOldY )
    gToggle   = 0
  EndIf
EndProcedure

; создаёт и рисует объекты
Procedure _CreateAndDrawObjects()
  If StartDrawing(CanvasOutput(0))
    Protected x = 5, y = 5, w = 26, h = 32
    Protected OutputW = OutputWidth(), OutputH = OutputHeight()
    If IsFont(gDefaultFont) : DrawingFont(FontID(gDefaultFont)) : EndIf
    Protected xw = 16
    For i=0 To 999 ; сколько объектов
      If x > OutputW - w
        y+18
        x = 5 + xw
        xw + Random(30,5)
        If xw > 60 : xw = Random(30,5) : EndIf
      EndIf  
      AddRect( x, y, w, h )
      DrawingMode( #PB_2DDrawing_AlphaBlend )
      Box( x, y, w, h, $E0000000 | Random($EE0000, $0000EE) )
      DrawingMode( #PB_2DDrawing_Transparent )
      DrawText( x+2, y, Str(i), $111111 )
      DrawingMode( #PB_2DDrawing_Outlined )
      Box( x, y, w, h, $111111)
      x+w + 1
    Next
    StopDrawing()
  EndIf
EndProcedure


Procedure _Event()
  Protected.q TimeS, TimeE
  Protected x = GetGadgetAttribute( 0, #PB_Canvas_MouseX )
  Protected y = GetGadgetAttribute( 0, #PB_Canvas_MouseY )
  
  Protected.DefRect *SearchObj1
  
  ;- Вариант 1. Тест Search_1
  TimeS = ElapsedMilliseconds()
  For n=1 To gCicle
    *SearchObj1 = Search_1( x, y )
  Next
  TimeE = ElapsedMilliseconds() - TimeS
  
  Protected TextObj1$ = "1) Объект не найден"
  If *SearchObj1 : With *SearchObj1
      TextObj1$ = "1) Объект " + \name + " [" +\x+","+\y+","+\w+","+\h+"]. Найден за: " + Str(TimeE) + "мс. Проходов: " + Str(n-1) 
  EndWith : EndIf  
  ; ---
  
  
  If StartDrawing( CanvasOutput(0) )
    ; скрываем сообщение при выходе указателя мыши с холста
    If EventType() = #PB_EventType_MouseLeave
      _PopCanvas()
      StopDrawing()
      ProcedureReturn
    EndIf
    ; вывод сообщения при перемещении указателя мыши по холсту
    Protected Coord$ = "x=" + RSet(Str(x),3,"0") + ", y=" + RSet(Str(y),3,"0")
    _PopCanvas()
    If IsFont(gDefaultFont) : DrawingFont(FontID(gDefaultFont)) : EndIf
    Protected tw1 = TextWidth(TextObj1$), tw2 = TextWidth(TextObj2$), tw
    If tw2 > tw1 : tw = tw2 : Else : tw = tw1 : EndIf
    Protected w = 283, h = 50, th = TextHeight("A")
    If w < tw : w = tw : EndIf
    x+2
    y-h-8
    w+10
    Protected OutputW = OutputWidth(), OutputH = OutputHeight()
    If x+w > OutputW : x = OutputW - w : EndIf
    If y < 0 
      y+h + 5
      x+12
      If x+w > OutputW : y+17 : EndIf
    EndIf
    _PushCanvas( x, y, w, h )
    ClipOutput( x, y, w, h )
    DrawingMode( #PB_2DDrawing_AlphaBlend )
    Box( x, y, w, h, $F0D5EFFF )
    DrawingMode( #PB_2DDrawing_Outlined )
    Box( x, y, w, h, $111111)
    DrawingMode( #PB_2DDrawing_Transparent )
    DrawText( x+5, y, Coord$, 0 ) : y+th
    DrawText( x+5, y, TextObj1$, $8B0000 ) : y+th
    DrawText( x+5, y, TextObj2$, $8B0000 )
    StopDrawing()
  EndIf
EndProcedure

;- окно с холстом
Global gWinW = 940, gWinH = 600
If OpenWindow( 0, 0, 0, gWinW, gWinH, "Тест Скорости определения объекта по координатам", #PB_Window_SystemMenu | #PB_Window_ScreenCentered )
  CanvasGadget( 0, 0, 0, gWinW, gWinH )
  _CreateAndDrawObjects()
  BindGadgetEvent( 0, @_Event(), #PB_EventType_MouseMove )
  BindGadgetEvent( 0, @_Event(), #PB_EventType_MouseLeave ) 
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf

В решении, не обязательно должен быть только список, он существует только пока. Приветствуется любой алгоритм и его реализация, которая будет максимально быстрой.

Отредактировано Webarion (13.11.2022 00:10:20)

0

2

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

0

3

Что то подобное мутил,только в массиве

Код:

Structure koordinaty
  x.i
  y.i
  x1.i
  y1.i
EndStructure

Structure ikrangadgeta
koordinatyikrana.koordinaty
HDC.i
Fon.i
Shrift.i
ControlFon.i
count.l
EndStructure

Structure control
  *text
  textlen.l
  id.a
 koordinatycontrola.koordinaty
 ;ControlFon.i 
EndStructure


Global ikrangadgeta.ikrangadgeta
ikrangadgeta\count=-1
Global Dim control.control(0)

Procedure.l HandleError(Result.l,Text$,flag.i=0);Вывод еррор сообщений
If Result=0
    MessageRequester("Error",Text$,flag)
Else
   ProcedureReturn Result
 EndIf
EndProcedure

Procedure Len_UTF16R(*s);возврат количество символов
!mov eax,DWORD PTR esp+4
!hut15:
!CMP word ptr eax,0h
!Je rr15
!add eax,4
!CMP word ptr eax-2,0h
!Jnz hut15
!sub eax,2
!rr15:
!SUB eax,DWORD PTR esp+4
!Sar eax,1 
!retn 4
EndProcedure

Procedure Customgadget(id,x,y,x1,y1,texst.s)
Protected r.i
 
!mov dword eax,[esp+40]
!mov dword [p.v_r],eax


ikrangadgeta\count+1
If ikrangadgeta\count>0
  ReDim control.control(ikrangadgeta\count)
EndIf



control(ikrangadgeta\count)\text=r

control(ikrangadgeta\count)\textlen=Len_UTF16R(control(ikrangadgeta\count)\text)


control(ikrangadgeta\count)\koordinatycontrola\x=x
control(ikrangadgeta\count)\koordinatycontrola\x1=control(ikrangadgeta\count)\koordinatycontrola\x+x1
control(ikrangadgeta\count)\koordinatycontrola\y=y
control(ikrangadgeta\count)\koordinatycontrola\y1=control(ikrangadgeta\count)\koordinatycontrola\y+y1
control(ikrangadgeta\count)\id=id

EndProcedure  





Procedure vyvodcontrola(prioritet.i)
Protected lenstr.i
Protected i.i
  
;StartDrawing(CanvasOutput(1))
;Box(0, 0, 800,500, $FFFFFF)


 
FillRect_(ikrangadgeta\HDC,@ikrangadgeta\koordinatyikrana,ikrangadgeta\Fon)



For i=0 To ikrangadgeta\count
  ;Debug 888888888
  
If i<>prioritet
 
  

  
FillRect_(ikrangadgeta\HDC,@control(i)\koordinatycontrola,ikrangadgeta\ControlFon)


;Rectangle_(ikrangadgeta\HDC,control(i)\koordinatycontrola\x, control(i)\koordinatycontrola\y,control(i)\koordinatycontrola\x1,control(i)\koordinatycontrola\y1)
;FrameRect_(ikrangadgeta\HDC, @control(i)\koordinatycontrola, $070808)

MoveToEx_(ikrangadgeta\HDC,control(i)\koordinatycontrola\x,control(i)\koordinatycontrola\y,0);
LineTo_(ikrangadgeta\HDC,control(i)\koordinatycontrola\x1,control(i)\koordinatycontrola\y)
LineTo_(ikrangadgeta\HDC,control(i)\koordinatycontrola\x1,control(i)\koordinatycontrola\y1)
LineTo_(ikrangadgeta\HDC,control(i)\koordinatycontrola\x,control(i)\koordinatycontrola\y1)
LineTo_(ikrangadgeta\HDC,control(i)\koordinatycontrola\x,control(i)\koordinatycontrola\y)

TextOut_(ikrangadgeta\HDC,control(i)\koordinatycontrola\x,control(i)\koordinatycontrola\y,control(i)\text,control(i)\textlen)

;ExtTextOut_(Texthdc,x.i,y.i,options.i,*RECT,*lpString,c.i,*lpDx )

EndIf
Next



FillRect_(ikrangadgeta\HDC,@control(prioritet)\koordinatycontrola,ikrangadgeta\ControlFon)
MoveToEx_(ikrangadgeta\HDC,control(prioritet)\koordinatycontrola\x,control(prioritet)\koordinatycontrola\y,0);
LineTo_(ikrangadgeta\HDC,control(prioritet)\koordinatycontrola\x1,control(prioritet)\koordinatycontrola\y)
LineTo_(ikrangadgeta\HDC,control(prioritet)\koordinatycontrola\x1,control(prioritet)\koordinatycontrola\y1)
LineTo_(ikrangadgeta\HDC,control(prioritet)\koordinatycontrola\x,control(prioritet)\koordinatycontrola\y1)
LineTo_(ikrangadgeta\HDC,control(prioritet)\koordinatycontrola\x,control(prioritet)\koordinatycontrola\y)

TextOut_(ikrangadgeta\HDC,control(prioritet)\koordinatycontrola\x,control(prioritet)\koordinatycontrola\y,control(prioritet)\text,control(prioritet)\textlen)


;DrawImage(ImageID(0), WindowMouseX(0), WindowMouseY(0))
;StopDrawing()
  
EndProcedure
Procedure move(clik.i)
  Protected numberobject.i;для поиска объекта,переменная среда от 0 и выше на выход найденного объекта,если объект найден по клику то его
  ;индекс заносится в переменную ocnoobjecta для дольнейшего действия на перемещение мыши,если нет то бездействие и по отпускании кнопки мыши 
  ;возврат в исходное состояние (объект не найден,ocnoobjecta=-1)
  Static ocnoobjecta.i=-1
  Static prioteretobjecta.i;есле объект накладывается поверх другого для отрисовки
  Static xy.Point
  Static xy1.Point
  
  Static xy2.Point
  

  
  If clik=2;
    If ocnoobjecta>-1
        GetCursorPos_(@xy2)
        ScreenToClient_(GadgetID(1), @xy2)
        Debug xy2\y
        
      control(ocnoobjecta)\koordinatycontrola\x=xy2\x-xy\x
      control(ocnoobjecta)\koordinatycontrola\x1=xy2\x+xy1\x
      control(ocnoobjecta)\koordinatycontrola\y=xy2\y-xy\y
      control(ocnoobjecta)\koordinatycontrola\y1=xy2\y+xy1\y
      
      vyvodcontrola(ocnoobjecta)
    EndIf
  ElseIf clik=1
  GetCursorPos_(@xy)
  ScreenToClient_(GadgetID(1), @xy)
  ;xy\x+GetGadgetAttribute(0, #PB_ScrollArea_X)
  ;xy\y+GetGadgetAttribute(0, #PB_ScrollArea_Y)
  ;
  For numberobject=0 To ikrangadgeta\count
    If xy\x>control(numberobject)\koordinatycontrola\x And xy\x<control(numberobject)\koordinatycontrola\x1
     If xy\y>control(numberobject)\koordinatycontrola\y And xy\y<control(numberobject)\koordinatycontrola\y1
        
       ocnoobjecta=numberobject
       ;prioteretobjecta=numberobject
       ;вычислим разницу от краёв x,y объекта и x,y точки  клика,и запомним её
       xy1\x=control(numberobject)\koordinatycontrola\x1-xy\x
       xy\x-control(numberobject)\koordinatycontrola\x
       xy1\y=control(numberobject)\koordinatycontrola\y1-xy\y
       xy\y-control(numberobject)\koordinatycontrola\y
       
       Continue;
       
       ;Debug xy\y
     EndIf
    EndIf
  Next
  ;
 ElseIf clik=0
    ocnoobjecta=-1
    
    
 ElseIf clik=3;#WM_RBUTTONDOWN
  GetCursorPos_(@xy)
  ScreenToClient_(GadgetID(1), @xy)  
   For numberobject=0 To ikrangadgeta\count
    If xy\x>control(numberobject)\koordinatycontrola\x And xy\x<control(numberobject)\koordinatycontrola\x1
     If xy\y>control(numberobject)\koordinatycontrola\y And xy\y<control(numberobject)\koordinatycontrola\y1
       
       
       SetGadgetState(990,numberobject+1)
       
          Continue;
       

     EndIf
    EndIf
   Next    
       
EndIf  
EndProcedure 

Procedure Canvas_calbak(hwnd,msg,wparam,lparam)
  Static Rodnoy_calbak.i
  Static a.a
  Protected Rezultat.i
  If Not msg=1
  Rezultat=CallWindowProc_(Rodnoy_calbak, hWnd, Msg, wParam, lParam);сначала прыгнем в родной обработчик 
  EndIf
  
  ;Debug msg
  Select msg
    Case #WM_CREATE
      ;ScrollAreaGadget(0, 0, 60, 800,500,5000,5000,100)
      ;SetGadgetAttribute(0, #PB_ScrollArea_X ,2500);установка ползунка по х
      ;CanvasGadget(1, 0, 0,5000,5000,#PB_Screen_SmartSynchronization)
      ;GetClientRect_(GadgetID(1),@ikrangadgeta\koordinatyikrana);запишим полученные координаты экрана

      ;InvalidateRect_(GadgetID(2), 0, #True)
      
      
      ;ikrangadgeta\HDC=GetDC_(GadgetID(1));запишим контекст рисования
      ;SetBkMode_(ikrangadgeta\HDC,#TRANSPARENT);#TRANSPARENT(цвет фона прозрачный,остается нетронутым ),#OPAQUE(сначала красит ячейку текста текущим цветом фона)пока так 

      ;ikrangadgeta\Fon=CreateSolidBrush_($00FFFFFF);фон экрана по умолчанию
      ;ikrangadgeta\ControlFon=CreateSolidBrush_($3DFFFC);фон контролов
      
      ;ikrangadgeta\Shrift=CreateFont_(24,0,0,0,0,0,0,0,#RUSSIAN_CHARSET,0,0,#CLEARTYPE_QUALITY,0,"Lucida Console");создаем логический шрифт по умолчанию
     ;If ikrangadgeta\Shrift=0;дескриптор не получен(Шрифт не создан)
     ;  HandleError(0,"Невозможно создать шрифт для CustomEditora")
      ; Goto NOSHRIFT
     ;Else
     ;  SelectObject_(ikrangadgeta\HDC,ikrangadgeta\Shrift);установить шрифт в контекст окна рисования текста
     ;EndIf 
      
      
      
      
      

       ;SetWindowLongPtr_(WindowID(0), #GWL_EXSTYLE, GetWindowLongPtr_(WindowID(0), #GWL_EXSTYLE)|#WS_EX_COMPOSITED)
     ; Rodnoy_calbak= GetWindowLongPtr_(WindowID(0), #GWL_WNDPROC)
       ;SetWindowLongPtr_(WindowID(0), #GWL_WNDPROC,  @Window_calbak());назначаем свой обработчик
       Rodnoy_calbak= GetWindowLongPtr_(GadgetID(1), #GWL_WNDPROC)
       SetWindowLongPtr_(GadgetID(1), #GWL_WNDPROC,@Canvas_calbak());назначаем свой обработчик
       
     Case #WM_ERASEBKGND;при заполнении окна цветом фона  
      ;Debug 66666
      ; FillRect_(ikrangadgeta\HDC,@ikrangadgeta\koordinatyikrana,ikrangadgeta\Fon)
     Case #WM_PAINT 
      ; Debug 44
       vyvodcontrola(0)
       
       
       
       
       
       
        Case #WM_MOUSEMOVE
            
 
       If a=1
   ;Debug wparam
           move(2)

        EndIf
      ;Case #WM_LBUTTONDBLCLK
        
        ;control(0)\status=1
        
     Case #WM_LBUTTONDOWN
        Debug hwnd
        move(1)
       a=1
     Case #WM_LBUTTONUP
       move(0)
       a=0
     Case #WM_RBUTTONDOWN  
       move(3)

     Case #WM_CLOSE  
       CloseWindow_(GadgetID(0))
       CloseWindow_(GadgetID(1))
      
     Case #WM_DESTROY
              DeleteObject_(ikrangadgeta\Fon)
              DeleteObject_(ikrangadgeta\ControlFon)
              DeleteObject_(ikrangadgeta\Shrift);Освободить дескриптор обычного печатного шрифта 
              
              ReleaseDC_(GadgetID(1),ikrangadgeta\HDC)
          
          DestroyWindow_(GadgetID(0))    
          DestroyWindow_(GadgetID(1))
         
      EndSelect
   
  ProcedureReturn Rezultat
EndProcedure











Procedure formy(fr.a)
Protected a.a
For a=0 To 1
 If IsWindow(a) 
  CloseWindow(a) 
 EndIf 
Next

If fr=0 
 If OpenWindow(0, 50, 0, 1200, 600, "")
      SmartWindowRefresh(0, #True); Уменьшаем мерцания окна при изменении его размеров
      HideWindow(0,1);Окно пока не отображается(закрыто) и типа все элементы на нём (гаджеты) создаются в фоновом режиме
      
  PanelGadget(990,0,25,1200,530);1-я панель
  AddGadgetItem(990, -1, "Бyyyy")
  ;
  ScrollAreaGadget(0, 0, 0, 1193,504,5000,5000,100)
  SetGadgetAttribute(0, #PB_ScrollArea_X ,2500);установка ползунка по х
  CanvasGadget(1, 0, 0,5000,5000,#PB_Screen_SmartSynchronization)
  
  GetClientRect_(GadgetID(1),@ikrangadgeta\koordinatyikrana);запишем полученные координаты экрана
   
      ikrangadgeta\HDC=GetDC_(GadgetID(1));запишим контекст рисования
      ;ikrangadgeta\HDC=GetDCEx_(GadgetID(1),0,#DCX_CACHE|#DCX_CLIPCHILDREN|#DCX_CLIPSIBLINGS);контекст с исключением дочерних окон
      SetBkMode_(ikrangadgeta\HDC,#TRANSPARENT);#TRANSPARENT(цвет фона прозрачный,остается нетронутым ),#OPAQUE(сначала красит ячейку текста текущим цветом фона)пока так 

      ikrangadgeta\Fon=CreateSolidBrush_($00FFFFFF);фон экрана по умолчанию
      ikrangadgeta\ControlFon=CreateSolidBrush_($3DFFFC);фон контролов
      
      ikrangadgeta\Shrift=CreateFont_(24,0,0,0,0,0,0,0,#RUSSIAN_CHARSET,0,0,#CLEARTYPE_QUALITY,0,"Lucida Console");создаем логический шрифт по умолчанию
     If ikrangadgeta\Shrift=0;дескриптор не получен(Шрифт не создан)
       HandleError(0,"Невозможно создать шрифт для Панели Блок схема")
      ; Goto NOSHRIFT
     Else
       SelectObject_(ikrangadgeta\HDC,ikrangadgeta\Shrift);установить шрифт в контекст окна рисования текста
     EndIf 
    
  
  
  
  
  
     Canvas_calbak(0,#WM_CREATE,0,0)
     

     Static  s.s{10}
     For i=0 To 1000
       
           Customgadget(i,i*100,i*25,100,25,"privet")

     Next
  
      
 HideWindow(0,0)

      ;
EndIf
EndIf
EndProcedure
formy(0)


Repeat
 Select WaitWindowEvent()
  Case #PB_Event_CloseWindow
    Break
    
    
    
 EndSelect  
ForEver
End

Отредактировано Sergeihik (01.11.2022 00:33:46)

+1

4

Sergeihik написал(а):

Что то подобное мутил,только в массиве

Спасибо за пример! Я задумывался о массиве. Поиск в массиве действительно быстрее, но вот добавление медленнее. Здесь нужно понимать, чем жертвовать в этих двух вариантах.
Тесты:

Код:
Define.q TimeS, TimeE1, TimeE2, TimeE3, TimeE4

Declare IsDebuggerEnabled()

Define Cicle ; Здесь количество проходов, в тесте времени

If IsDebuggerEnabled()
  Cicle = 1000  ; с отладчиком
Else
  Cicle = 10000 ; без отладчика
EndIf

; Отвечает, включён ли отладчик
Procedure IsDebuggerEnabled()
  !if defined _PB_DEBUGGER_Control
  !mov eax, 1
  !else
  !mov eax, 0
  !end if
  ProcedureReturn
EndProcedure

; Здесь просто вывод сообщения. Если отладчик включён, то вывод в Debug, иначе в MessageRequester
Procedure _P( Text$ )
  If IsDebuggerEnabled() : Debug Text$ : Else : MessageRequester("Debug",Text$) : EndIf
EndProcedure

;- ТЕСТИРОВАНИЕ

Structure DefObj
  x.w
  y.w
  w.u
  h.u
  name$
EndStructure

Dim aObj.DefObj(0)
Define NewList lObj.DefObj()

Define CountObjects = 1000

Define SearchX = 21, SearchY = 21

Define Cnt = CountObjects-1
Define CreateCicle = 500 ; количество проходов, при добавлении элементов (для теста)
;- тест времени добавления в массив
TimeS = ElapsedMilliseconds()
For i = 0 To CreateCicle
  ReDim aObj(0) ; это нужно учитывать, т.к без множества проходов для теста, без этой строки будет быстрее
  For n = 0 To Cnt
    ReDim aObj(n)
    With aObj(n)
      \x = n*10 : \y = n*10 : \w = 30 : \h = 30 : \name$ = "Obj_" + Str(n)
    EndWith
  Next
Next
TimeE1 = ElapsedMilliseconds() - TimeS

;- тест времени добавления в список
TimeS = ElapsedMilliseconds()
For i = 0 To CreateCicle
  ClearList( lObj() ) ; это нужно учитывать, т.к без множества проходов для теста, без этой строки будет быстрее
  For n = 0 To Cnt
    AddElement( lObj() )
    With lObj()
      \x = n*10 : \y = n*10 : \w = 30 : \h = 30 : \name$ = "Obj_" + Str(n)
    EndWith
  Next
Next
TimeE2 = ElapsedMilliseconds() - TimeS

Define aSearch$ = "нет", lSearch$ = aSearch$, Last

;- тест времени нахождения в массиве
TimeS = ElapsedMilliseconds()
For i=1 To Cicle
  Last = ArraySize( aObj() )
  For n=Last To 0 Step -1
    With aObj(n)
      If SearchX>=\x And SearchX<\x+\w And SearchY>=\y And SearchY<\y+\h ; поиск по указанным координатам
        aSearch$ = \name$
        Break
      EndIf
    EndWith
  Next
Next
TimeE3 = ElapsedMilliseconds() - TimeS

;- тест времени нахождения в списке
TimeS = ElapsedMilliseconds()

For i=1 To Cicle
  LastElement( lObj() )
  Repeat 
    With lObj()
      If SearchX>=\x And SearchX<\x+\w And SearchY>=\y And SearchY<\y+\h ; поиск по указанным координатам
        lSearch$ = \name$
        Break
      EndIf
    EndWith
  Until Not PreviousElement( lObj() )
Next

TimeE4 = ElapsedMilliseconds() - TimeS


Define Result$ = "Добавление в массив: " + Str(TimeE1) + " мс." + #CRLF$
Result$ + "Добавление в список: " + Str(TimeE2) + " мс." + #CRLF$
Result$ + "Поиск в массиве : "    + Str(TimeE3) + " мс. Найден: " + aSearch$ +  #CRLF$
Result$ + "Поиск в списке  : "    + Str(TimeE4) + " мс. Найден: " + lSearch$ +  #CRLF$

_P( Result$ )

0

5

Не знаю, использует ли здесь кто-либо, методы поиска решений в подсознании. Когда ответы можно находить в пограничном состоянии, между сном и не сном. Я иногда использую.
Вчера пробовал относительно данного вопроса. Во сне мозг что-то решал, но не помню что. А когда пробуждался, в пограничном состоянии, услышал фразу: "Здесь нужен перекрёстный реверс" и увидел вот такое изображение:
https://forumupload.ru/uploads/0009/ae/28/644/t146921.jpg

Не знаю, что это значит, буду обдумывать и этот некий "перекрёстный реверс".
Как вариант, возможно это синхронизированный поиск из нескольких мест в списке или массиве, быть может даже в нескольких потоках. Думаю об этом. Но, конечно приглашаю форумчан, присоединяться. Может кому-то будет интересно.

Отредактировано Webarion (02.11.2022 00:53:36)

0

6

Webarion написал(а):
Код:
; Отвечает, включён ли отладчик
Procedure IsDebuggerEnabled()
  !if defined _PB_DEBUGGER_Control
  !mov eax, 1
  !else
  !mov eax, 0
  !end if
  ProcedureReturn
EndProcedure

Почему не #PB_Compiler_Debugger?

+1

7

Пётр написал(а):

Почему не #PB_Compiler_Debugger?

И правда)
Решение нашёл на fr форуме по поисковому запросу.  Видимо тогда ещё не существовало этой константы.  Сам, про константу не знал. Пробовал как в коде #_PB_DEBUGGER_Control, не вышло. Оставил уже как есть для тестов. А так да - #PB_Compiler_Debugger. Благодарю за подсказку.

Отредактировано Webarion (02.11.2022 02:58:18)

0

8

Webarion написал(а):

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

у меня это как то само собой получается, ни чего не включаю, проблема сама по себе крутится

0

9

newJS написал(а):

у меня это как то само собой получается, ни чего не включаю, проблема сама по себе крутится

:cool:

Пока, самый быстрый поиск, который я нашёл для массива:

Код:
; Принцип поиска - встречный: >--|--<
Procedure _SearchInArray( x, y )
  Protected SearchIndex = -1
  For i = 0 To g_HalfSizeArr ; g_HalfSizeArr = g_SizeArr/2
    With ga_Object(g_SizeArr-i) ; g_SizeArr = ArraySize(ga_Object())
      If x>=\x And x<\x+\w And y>=\y And y<\y+\h
        ProcedureReturn g_SizeArr-i
      EndIf
    EndWith
    With ga_Object(i)
      If x>=\x And x<\x+\w And y>=\y And y<\y+\h
        SearchIndex = i
      EndIf
    EndWith
  Next
  ProcedureReturn SearchIndex
EndProcedure

Поиск идёт с начала и с конца. И цикл доходит только до середины массива.
Тесты:

Код:
Structure DefRect
  x.i
  y.i
  w.i
  h.i
  name.s
EndStructure

Global Dim ga_Object.DefRect(0)

; Здесь просто вывод сообщения. Если отладчик включён, то вывод в Debug, иначе в MessageRequester
Procedure _P( Text$ )
  If #PB_Compiler_Debugger : Debug Text$ : Else : MessageRequester("Debug",Text$) : EndIf
EndProcedure

Global g_SizeArr, g_HalfSizeArr
Procedure _AddArrayObject( x, y, w, h )
  Static trig.a = 0
  Protected New = ArraySize( ga_Object() )
  If trig
    New+1
    ReDim ga_Object(New)
  EndIf
  With ga_Object(New)
    \x = x : \y = y : \w = w : \h = h : \name = "Obj_" + Str(New)
  EndWith
  
  g_SizeArr     = New
  g_HalfSizeArr = g_SizeArr / 2
  
  If Not trig : trig = 1 : EndIf
EndProcedure

Define TimeS.q = ElapsedMilliseconds()
For i=0 To 999
  _AddArrayObject( i*20, i*20, 30, 30 )
Next
Define TimeE.q = ElapsedMilliseconds() - TimeS

; обычный метод поиска
Procedure _SearchInArray( x, y )
  For i = g_SizeArr To 0 Step -1
    With ga_Object(i)
      If x>=\x And x<\x+\w And y>=\y And y<\y+\h
      ProcedureReturn i
    EndIf
    EndWith
  Next
  ProcedureReturn -1
EndProcedure


; Принцип поиска - встречный: >--|--<
Procedure _SearchInArray2( x, y )
  Protected SearchIndex = -1
  For i = 0 To g_HalfSizeArr ; g_HalfSizeArr = g_SizeArr/2
    With ga_Object(g_SizeArr-i) ; g_SizeArr = ArraySize(ga_Object())
      If x>=\x And x<\x+\w And y>=\y And y<\y+\h
        ProcedureReturn g_SizeArr-i
      EndIf
    EndWith
    With ga_Object(i)
      If x>=\x And x<\x+\w And y>=\y And y<\y+\h
        SearchIndex = i
      EndIf
    EndWith
  Next
  ProcedureReturn SearchIndex
EndProcedure



If #PB_Compiler_Debugger
  Define Cicle = 10000
Else 
  Define Cicle = 100000
EndIf

; Вариант1
Define SearchObj1$
TimeS.q = ElapsedMilliseconds()
For i=0 To Cicle
  SearchObj1$ = ga_Object( _SearchInArray( 42, 42 ) )\name
Next
Define ArrTime.q = ElapsedMilliseconds() - TimeS

; Вариант2
Define SearchObj2$
TimeS.q = ElapsedMilliseconds()
For i=0 To Cicle
  SearchObj2$ = ga_Object( _SearchInArray2( 42, 42 ) )\name
Next
Define ArrTime2.q = ElapsedMilliseconds() - TimeS


Define Result$
Result$ = Str(ArrTime)  + "  " + SearchObj1$ + #CRLF$
Result$ + Str(ArrTime2) + "  " + SearchObj2$ + #CRLF$

_P( Result$ )

Разница небольшая, но "копейка рубль бережёт"

Отредактировано Webarion (03.11.2022 11:46:11)

0

10

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

Код:
Structure PosY
  y.i
  h.i
  name.s
EndStructure

Structure PosX
  x.i
  w.i
  List y.PosY()
EndStructure

Если данные координат хранить в отсортированном виде (меньшие значения в начале, а больше в конце или наоборот). Если элемент со значением X больше чем нужно, можно прервать поиск. Аналогично с Y. Это ускорит поиск.

+1

11

Пётр написал(а):

Способов оптимизации может быть много.

Вот мне и интересно найти наиболее быстрый.

Пётр написал(а):

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

Благодарю! Это реально хорошая идея!

Пётр написал(а):

Если данные координат хранить в отсортированном виде (меньшие значения в начале, а больше в конце или наоборот). Если элемент со значением X больше чем нужно, можно прервать поиск. Аналогично с Y. Это ускорит поиск.

Это я протестирую обязательно.
Сейчас пытаюсь исследовать поразрядную запись координат объекта. И поиск также по разрядам числа.

Отредактировано Webarion (03.11.2022 16:00:45)

0

12

Webarion написал(а):

Список может быть очень большой.

Что в вашем понимании "ОЧЕНЬ"?
Если речь о экранных координатах, то современный комп в состоянии хранить инфу принадлежности к "объектам" каждого пикселя.
Т.е. основная нагрузка алгоритма на запоминание положения при формировании, а не на последующий поиск.

Отредактировано useful (04.11.2022 08:38:21)

0

13

useful написал(а):

современный комп в состоянии хранить инфу принадлежности к "объектам" каждого пикселя

Такой вариант тоже рассматривается. Хороший примерчик бы покурить. Кажется, в OpenGL есть определённые возможности, PBO, VBO и что-то в этом роде, надо будет разобраться. Но есть ли возможность такая, не используя OpenGL, и как это может быть кроссплатформенно?

useful написал(а):

Что в вашем понимании "ОЧЕНЬ"?

Ну, пусть будет потолок в 65К. объектов. В идеале, пока не ограничиваю, а дальше по тестам видно будет.

0

14

Код:
EnableExplicit

Define ScrW.i   = 800
Define ScrH.i   = 600

Define NObj     = 1
Define Bw       = 20
Define Bh       = 20
Define Event,x,y,i,j


Dim Scr.i(ScrW,ScrH) ;Массив соответствия пикселя номеру объекта

OpenWindow(0,0,0,ScrW,ScrH,"",#PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
CanvasGadget(0,0,0,ScrW,ScrH)


Repeat
  Event = WaitWindowEvent()
  
  If Event = #PB_Event_Gadget And EventGadget() = 0 
    If EventType() = #PB_EventType_LeftButtonDown Or (EventType() = #PB_EventType_MouseMove And GetGadgetAttribute(0, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton)
      If StartDrawing(CanvasOutput(0))
        x = GetGadgetAttribute(0, #PB_Canvas_MouseX)
        y = GetGadgetAttribute(0, #PB_Canvas_MouseY)
        Box(x, y, Bw, Bh, RGB(Random(255), Random(255), Random(255)))
        StopDrawing()
        ;
        ; в этом месте нужно заполнять фрагмент массива Scr (x, y, Bw, Bh) значением Nobj(номер объекта) с контролем выхода за границы
        ; лень думать, box() то нарисует фрагмент, какой сможет :)
        ; если нужно хранить слои, то наверно массив Scr превратится например в массив списков и там порядок номеров объектов на усмотрение 
        ;
        Nobj + 1
      EndIf
    EndIf
  EndIf    
  
Until Event = #PB_Event_CloseWindow

;На каждом этапе, по мере движения мыши мы получаем массив соответствие пикселя номеру объекта верхнего слоя 
;И в последующем отпадает необходимость поиска

;Допускаю, что я не понял задачу

Отредактировано useful (05.11.2022 11:38:55)

0

15

Если это тупо объёкты в виде прямоугольника то можно взять множество канвасов (как объектов)и обработчиком определить его местоположение,это будет наверное самый быстрый способ.Если что то другое то перебор наверно надо,тогда цикл нужно делать на сопроцессоре.....

0

16

useful написал(а):

Global Dim Scr.i(ScrW,ScrH)

Спасибо за пример. Потестил:

Код:
EnableExplicit

Define ScrW.i   = 800
Define ScrH.i   = 600
Define Event

Global Dim Scr.i(ScrW,ScrH) ; Массив соответствия пикселя номеру объекта

Structure Element
  x.w
  y.w
  w.u
  h.u
  name$
EndStructure

Define NewList Element.Element()


Procedure _ResizePix( x, y, w, h, *El )
  Protected i, j
  For i=x To x+w-1
    For j=y To y+h-1
      Scr(i,j) = *El
    Next
  Next
EndProcedure


Procedure _Event()
  Protected x = GetGadgetAttribute(0, #PB_Canvas_MouseX)
  Protected y = GetGadgetAttribute(0, #PB_Canvas_MouseY)
  Protected *El.Element = Scr(x,y)
  If *El
    Debug *El\name$
  Else
    Debug "Курсор не над элементом"
  EndIf
EndProcedure


OpenWindow( 0, 0, 0, ScrW, ScrH, "", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered )
CanvasGadget( 0, 0, 0, ScrW, ScrH )

; создаём элементы рандомно
If StartDrawing(CanvasOutput(0))
  Define *El, n
  Define TimeS = ElapsedMilliseconds()
  For n=0 To 999
    *El = AddElement( Element() )
    With Element()
      \x     = Random(ScrW-30, 0)
      \y     = Random(ScrH-30, 0)
      \w     = Random(ScrW-\x, 30)
      \h     = Random(ScrH-\y, 30)
      \name$ = "El_" + Str(n)
      
      _ResizePix( \x, \y,\w, \h, *El )
      
      DrawingMode( #PB_2DDrawing_Default )
      Box( \x, \y, \w, \h, RGB(Random(250), Random(250), Random(250)) )
      DrawingMode( #PB_2DDrawing_Outlined )
      Box( \x, \y, \w, \h, 0 )
      DrawingMode( #PB_2DDrawing_Transparent ) 
      DrawText( \x+2, \y, "El_" + Str(n), 0 )
      
    EndWith
  Next
  MessageRequester( "", "Время создания " + Str(n) + " элементов - "+ Str( ElapsedMilliseconds() - TimeS ) + "мс." )
  StopDrawing()
EndIf

BindGadgetEvent( 0, @_Event(), #PB_EventType_MouseMove ) 

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow

Что мне нравится:
* Быстрота в статике. Если сформированные объекты, больше не изменяют своего положения и размеров. То это самая быстрая реализация поиска.

Что мне пока не нравится:
* Время на создание объектов. Приходится заполнять каждый пиксель в массив. Поэтому это самый долгий метод создания.
* Медленное перемещение объекта. При каждом изменении координат и размеров, приходится перезаписывать все пиксели объекта. Динамика здесь не подойдёт. Хотя, может быть можно что-то оптимизировать.

Отредактировано Webarion (05.11.2022 15:37:42)

0

17

Sergeihik написал(а):

Если это тупо объёкты в виде прямоугольника то можно взять множество канвасов (как объектов)и обработчиком определить его местоположение,это будет наверное самый быстрый способ.Если что то другое то перебор наверно надо,тогда цикл нужно делать на сопроцессоре.....

Динамика с множеством холстов, не очень приятная. При изменении размеров элементов, или например размеров окна, на каждом изменяемом нужны StartDrawind()-StopDrawing() - это создаёт задержку и неприятный визуальный эффект. Считаю, что лучше исследовать способы и научиться рисовать на одной поверхности.

Отредактировано Webarion (05.11.2022 15:55:54)

0

18

Webarion написал(а):

* Время на создание объектов.

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

0

19

Sergeihik написал(а):

тогда цикл нужно делать на сопроцессоре.....

Вот об этом подробнее пожалуйста! И если возможно, то примерчик)
Если имеете ввиду поиск в другом потоке, то это пробовал. Пока поток инициализируется, основной поиск уже практически завершён. В общем поиск в потоках не даёт прироста скорости.

0

20

useful написал(а):

Я же совсем не знаю цель происходящего.

Вот как понимаете, так и нормально, думаю, не ошибётесь :idea: варианты я рассматриваю :writing:
Потом уже буду всё комбинировать. Поэтому благодарен за любые идеи.

useful написал(а):

заранее

Да, за ранее сформированные статичные данные, наиболее эффективны для последующего чтения. Я сторонник поиска золотой середины. Сначала собирать алгоритмы и тестировать всё, потом смотреть, нужно ли комбинировать варианты. Это конечно занимает время, но, до ужаса интересно.

0

21

Webarion написал(а):

Но, пока-что самым быстрым у меня остаётся перебор всех элементов списка.

Если это не пара десятков элементов, то перебор выбросить точно сразу и на всегда.
Смотреть в сторону всяких бинарных поисков и прочего или просто sqlite в памяти и заранее препарированный поисковый запрос в таблицу из 5 полей x1,x2,y1,y2,n_obj, где все поля проиндексированы конечно.

http://www.minimdb.com/mbook/mbook.html
стр. 227 гл.3 Индексация данных
Субд конечно своеобразная но глава очень полезная для общей теории.

Отредактировано useful (09.11.2022 09:38:58)

+1

22

useful написал(а):

стр. 227 гл.3 Индексация данных

За книгу спасибо. Всегда полезно и интересно.
Скорость Субд ещё предстоит изучить. Пока нативные средства исследую. Если честно, сомневаюсь, что встроенные средства в PB, для работы с БД, будут настолько шустрыми, даже с индексацией. Но, не утверждаю, так как не исследовал эту тему. Всё ещё предстоит.
Сейчас, я больше склоняюсь к организации памяти и созданию системы чтения и поиска на ASM. Но это не точно))

Вот пример текущий, в котором, я соединил индексацию и поиск, пока, на вскидку, тут ещё есть о чём подумать:

Код:
Global ScrW.u = 900
Global ScrH.u = 600

Global Grid.u = 100

Define SizeCellsX = ScrW / Grid
Define SizeCellsY = ScrH / Grid

Define Event

Structure GridElements
  List *Element()
EndStructure

Global Dim aGrid.GridElements(SizeCellsX, SizeCellsY) ; массив соответствия элемента, ячейке сетки

Structure Element
  x.w
  y.w
  w.u
  h.u
  name$
EndStructure

Global NewList Element.Element()

Global gDefaultFont = LoadFont(#PB_Any, "SegoeUI", 5) 


Procedure _ResizeGrid( x, y, w, h, *El )
  
  Protected xs = x/Grid, xe = (x+w-1)/Grid
  Protected ys = y/Grid, ye = (y+h-1)/Grid
  
  For nx = xs To xe
    For ny = ys To ye
      AddElement( aGrid(nx,ny)\Element() )
      aGrid(nx,ny)\Element() = *El
    Next
  Next
  
EndProcedure


; Вариант 1. Поиск объекта по координатам. Простой перебор сверху вниз
Procedure Search_1( x.w, y.w )
  Protected *El.Element
  If ListSize( Element() )
    PushListPosition( Element() )
    LastElement( Element() )
    Repeat
      With Element()
        If x>=\x And x<\x+\w And y>=\y And y<\y+\h
          *El = @Element()
          Break
        EndIf
      EndWith
    Until Not PreviousElement( Element() )
    PopListPosition( Element() )
  EndIf
  ProcedureReturn *El
EndProcedure

; Вариант 3. Поиск объекта по индексированной сетке
Procedure Search_3( x, y, gx, gy )
  
  With aGrid( gx, gy )
    If ListSize( \Element() )
      Protected *El.Element
      LastElement( \Element() )
      Repeat 
        *El = \Element()
        If x>=*El\x And x<*El\x+*El\w And y>=*El\y And y<*El\y+*El\h
          ProcedureReturn *El
        EndIf
      Until Not PreviousElement( \Element() )
    EndIf
  EndWith
  
  ProcedureReturn 0
EndProcedure


Procedure _Event()
  Protected x.u = GetGadgetAttribute(0, #PB_Canvas_MouseX)
  Protected y.u = GetGadgetAttribute(0, #PB_Canvas_MouseY)
  
  Protected gx = x/Grid
  Protected gy = y/Grid
  
  Protected *El.Element
  
  Protected Cicle = 1000
  
  Protected.s Result1, Result2, Result3
  Protected.q TimeE1, TimeE2, TimeE3
  Define TimeS.q = ElapsedMilliseconds()
  For i=0 To Cicle
    *El = Search_1( x, y )
    If *El
      Result1 = "El_" + *El\name$
    Else
      Result1 = "Нет"
    EndIf
  Next
  TimeE1 = ElapsedMilliseconds() - TimeS
  
  TimeS = ElapsedMilliseconds()
  For i=0 To Cicle
    *El = Search_3( x, y, gx, gy )
    If *El
      Result3 = "El_" + *El\name$
    Else
      Result3 = "Нет"
    EndIf
  Next
  TimeE3 = ElapsedMilliseconds() - TimeS
  
  Define.q diff1 = TimeE1 / TimeE3

  Debug "Проходов: " + Str(Cicle) + " Найден: " + Result1 + " За: " + Str(TimeE1) + "мс."
  Debug "Проходов: " + Str(Cicle) + " Найден: " + Result3 + " За: " + Str(TimeE3) + "мс. В " + Str(diff1) + "раз, быстрее предыдущего."
  Debug "---------------"
  
EndProcedure


Procedure _CreateElements()
  If StartDrawing(CanvasOutput(0))
    If IsFont(gDefaultFont) : DrawingFont(FontID(gDefaultFont)) : EndIf
    
    Protected *El, x, y, Count = 3899
    
    Protected kx = ScrW/48, ky = ScrH/65
    Protected w = kx, h = ky

    For n=0 To Count
      *El = AddElement( Element() )
      With Element()
        \x = x : \y = y
        \w = w : \h = h
        
        x+kx-1
        If x+w > ScrW
          x=0
          y+ky-1
        EndIf
        
        \name$ = Str(n)
        
        _ResizeGrid( \x, \y,\w, \h, *El )
        
        DrawingMode( #PB_2DDrawing_Default )
        Box( \x, \y, \w, \h, RGB(Random(250), Random(250), Random(250)) )
        DrawingMode( #PB_2DDrawing_Outlined )
        Box( \x, \y, \w, \h, 0 )
        DrawingMode( #PB_2DDrawing_Transparent ) 
        DrawText( \x+1, \y+1, \name$, 0 )
      EndWith
    Next
    StopDrawing()
  EndIf
EndProcedure


OpenWindow( 0, 0, 0, ScrW, ScrH, "", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered )
CanvasGadget( 0, 0, 0, ScrW, ScrH )

_CreateElements()

BindGadgetEvent( 0, @_Event(), #PB_EventType_MouseMove ) 

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow

Некоторые элементы находятся в 150 раз быстрее обычного перебора.
Смысл здесь простой. Индексировать элементы не к каждому пикселю, а к квадратам сетки. Размеры сетки можно задавать в зависимости от количества элементов. Больше сетка - меньше затраты памяти, медленнее поиск, но быстрее создание. Меньше сетка - больше памяти, быстрее поиск, но медленнее создание. Тут уже в будущем, буду вычислять формулы, для создания золотой середины между памятью, скоростью создания и скорость поиска.

useful написал(а):

Если речь о экранных координатах

Да, экранные координаты рассматриваются, но рассматриваются и виртуальные. Допустим, на экране помещается, только часть элементов. И эту часть можно отобразить, но, возможно и отобразить другие элементы, используя вертикальную и горизонтальную прокрутку. По сути, это структура виртуальная, но способная отображаться на экране.

0

23

Webarion написал(а):

что встроенные средства в PB, для работы с БД

Причем тут PB?
Sqlite не Фред придумал, им пользуются практически поголовно самые авторитетные компании мира.
За двадцать с лишним лет код вылизан до блеска.
https://www.sqlite.org/inmemorydb.html

0

24

useful написал(а):

Причем тут PB?

Имел виду список команд в PB, работающий с БД. Как они там работают не знаю, не разбирался ещё.

useful написал(а):

код вылизан до блеска

Может быть.

useful написал(а):

поголовно самые авторитетные компании мира

На авторитетов, мне с высокой колокольни... Сам проверю, будет видно. За инфо, благодарю.

0

25

100 тыс. объектов на поле 5000*5000
На моём холодном целероне j1800 стабильно 15-20 миллисекунд и это грубый набросок без каких бы то ни было попыток оптимизации.
Причём в примере вытаскиваются все найденные. Короче есть куда развиваться.
p.s. а оговоренные ранее 65000 - 5-10 миллисекунд.
==============
p.p.s. Ну а с числами
W = 900 ; Ширина поля
H = 600 ; Высота поля
O = 3899 ; Количество оъектов
Выйти за пределы НУЛЯ миллисекунд я так и не смог!!!!!!!!!  :dontknow:
==============

Код:
UseSQLiteDatabase()

Procedure CheckDatabaseUpdate(Database, Query$)
  ;DatabaseUpdate(Database, "BEGIN TRANSACTION;")
  Result = DatabaseUpdate(Database, Query$)
  If Result = 0
    Debug DatabaseError()
  EndIf
  ;DatabaseUpdate(Database, "END TRANSACTION;")
  ProcedureReturn Result
EndProcedure

W = 5000 ; Ширина поля
H = 5000 ; Высота поля
O = 100000 ; Количество оъектов

If OpenDatabase(0, ":memory:", "", "")
  
  CheckDatabaseUpdate(0, "CREATE TABLE tObject (NObject INT, X1 INT, Y1 INT, X2 INT, Y2 INT)")
  CheckDatabaseUpdate(0, "CREATE INDEX iObject ON tObject (NObject, X1, Y1, X2, Y2)")
  
  For i = 1 To O
    
    Ow = Random(100,10)
    Oh = Random(100,10)
    Ox = Random(W-Ow,0)
    Oy = Random(H-Oh,0)
    Req1.s = "INSERT INTO tObject (NObject,X1, Y1, X2, Y2) VALUES ("
    Req2.s = Str(i) + "," 
    Req2.s = Req2 + Str(Ox) + ","
    Req2.s = Req2 + Str(Oy) + ","
    Req2.s = Req2 + Str(Ox+Ow-1) + ","
    Req2.s = Req2 + Str(Oy+Oh-1) + ")"
    
    CheckDatabaseUpdate(0, Req1+Req2)
    
  Next  
  
  x = Random(W-1,0)
  y = Random(H-1,0)
  SetDatabaseLong(0,0,x)
  SetDatabaseLong(0,1,x)
  SetDatabaseLong(0,2,y)
  SetDatabaseLong(0,3,y)
  L = 0
  StartTime.q = ElapsedMilliseconds()
  If DatabaseQuery(0, "SELECT NObject FROM tObject WHERE (X1 <= ?) AND (X2 >= ?) AND (Y1 <= ?) AND (Y2 >= ?) ORDER BY NObject ")
    
    While NextDatabaseRow(0)
      L = GetDatabaseLong(0, 0)
    Wend
    
    FinishDatabaseQuery(0)
  EndIf
  ;Debug "===="
  MessageRequester("Information", Str(L) + "=" + Str( ElapsedMilliseconds() - StartTime), #PB_MessageRequester_Ok | #PB_MessageRequester_Info)
  ;ElapsedMilliseconds() - StartTime 
  CloseDatabase(0)
Else
  Debug "Can't open database !"
EndIf

Отредактировано useful (10.11.2022 09:25:35)

0

26

Структура координат в массиве должна быть отдельно а строка вынесенна в отдельный массив
Потому как она имеет переменную длину.
Либо использовать для неё переменную ссылки на выделенную память(если динамически)
Либо на статическую датасекцию
Возможно пурик в массив (память)с типом строки
и ложит как ссылку?(надо проверять)
Тогда можно и в структуру определить.

Код:
Structure koordinaty
X.i
Yi
X1.i
Y1.i
Endstructure
Global count.i
Global dim koordinary.koordinaty(100000)
Global dim Stroka.s(100000)

Procedure Set_object()
Protected i.i
count=100000
For i=0 to count
koordinaty(i)\X=i
koordinaty(i)\X1=i+100
koordinaty(i)\Y=i
koordinaty(i)\Y1=i+100
Stroka(i)="Stroka"
Next
Endprocedure

Procedure poisk(*massiv,x,y,count);для теста
Count*16
!xor ecx,ecx
!mov dword ebx,[p.v_x]
!mov dword edx,[p.v_y]
!mov dword eax,[p.p_massiv]
!cikl:
!cmp dword ebx,[eax+ecx]
!ja xbolhe;x>koor\x
!cmp dword ecx,[p.v_Count]
!jz vyhod
!add dword ecx,16
!jmp cikl
!xbolhe:
!cmp dword ebx,[eax+ecx+8]
ijl xmenhe;x<koor\x1
!cmp dword ecx,[p.v_Count]
!jz vyhod
!add dword ecx,16
!xmenhe:;x мы между kor\x <x> kor\x1
;
!cmp dword edx,[eax+ecx+4]
!ybolhe; kor\y<y
!cmp dword ecx,[p.v_Count]
!jz vyhod

!ybolhe:

!vyhod:не найдено
Procedurereturn -1
Endprocedure

Ps:приду домой подправлю допишу тест

Отредактировано Sergeihik (10.11.2022 15:42:45)

0

27

useful написал(а):

стабильно 15-20 миллисекунд

Как всегда тесты:

Код:
Global Width      = 5000   ; Ширина поля
Global Height     = 5000   ; Высота поля
Global NumObjects = 100000 ; Количество объектов

Global.q StartTime, EndCreateTime1 = -1, EndCreateTime2 = -1, EndCreateTime3 = -1 ; время создания
Global.q EndSearchTime1 = -1, EndSearchTime2 = -1, EndSearchTime3 = -1            ; время поиска

Global.s Result1 = "Перебор: ", Result2 = "SQLite: ", Result3 = "Тест 3. " ; будут содержать тексты

; искомые координаты
Global FindX = Random(Width-1,0)
Global FindY = Random(Height-1,0)

;- === ТЕСТ 1. Перебор =======================================================================

Structure Object
  x1.i
  x2.i
  y1.i
  y2.i
  num.i
EndStructure

Global NewList gl_Object.Object()

Procedure Create_1()
  Protected Ox, Oy, Ow, Oh
  StartTime = ElapsedMilliseconds()
  For i = 1 To NumObjects
    AddElement( gl_Object() )
    With gl_Object()
      Ow = Random(100,10)
      Oh = Random(100,10)
      Ox = Random(Width-Ow,0)
      Oy = Random(Height-Oh,0)
      
      \x1  = Ox : \x2 = Ox+Ow-1
      \y1  = Oy : \y2 = Oy+Oh-1
      \num = i
    EndWith
  Next
  EndCreateTime1 = ElapsedMilliseconds() - StartTime ; скорость создания
EndProcedure

Procedure Search_1( x, y )
  Protected *Object.Object
  If ListSize( gl_Object() )
    PushListPosition( gl_Object() )
    LastElement( gl_Object() )
    Repeat
      With gl_Object()
        If x>=\x1 And x<=\x2 And y>=\y1 And y<=\y2
          *Object = @gl_Object()
          Break
        EndIf
      EndWith
    Until Not PreviousElement( gl_Object() )
    PopListPosition( gl_Object() )
  EndIf
  ProcedureReturn *Object
EndProcedure

Create_1() ; создаём тестируемый список

Define *Obj.Object
StartTime = ElapsedMilliseconds()
*Obj = Search_1( FindX, FindY )
EndSearchTime1 = ElapsedMilliseconds() - StartTime

Result1 + "Время создания: " + EndCreateTime1 + "мс. "
If *Obj
  Result1 + "Найден: " + Str(*Obj\num) + " За: " + Str(EndSearchTime1) + " мс."
Else
  Result1 + "Объект не найден."
EndIf
; === ТЕСТ 1. Конец ==========================================================================


;- === ТЕСТ 2. SQLite ========================================================================
UseSQLiteDatabase()

Procedure CheckDatabaseUpdate(Database, Query$)
  ;DatabaseUpdate(Database, "BEGIN TRANSACTION;")
  Result = DatabaseUpdate(Database, Query$)
  If Result = 0
    Debug DatabaseError()
  EndIf
  ;DatabaseUpdate(Database, "END TRANSACTION;")
  ProcedureReturn Result
EndProcedure



If OpenDatabase( 0, ":memory:", "", "" )
  
  CheckDatabaseUpdate( 0, "CREATE TABLE tObject (NObject INT, X1 INT, Y1 INT, X2 INT, Y2 INT)" )
  CheckDatabaseUpdate( 0, "CREATE INDEX iObject ON tObject (NObject, X1, Y1, X2, Y2)" )
  
  Define Ox, Oy, Ow, Oh
  FirstElement( gl_Object() )
  StartTime = ElapsedMilliseconds()
  
  For i = 1 To NumObjects
    Req1.s = "INSERT INTO tObject (NObject,X1, Y1, X2, Y2) VALUES ("
    Req2.s = Str(i) + "," 
    With gl_Object() ; пишем, уже созданные координаты
      Req2.s = Req2 + Str(\x1) + ","
      Req2.s = Req2 + Str(\y1) + ","
      Req2.s = Req2 + Str(\x2) + ","
      Req2.s = Req2 + Str(\y2) + ")"
    EndWith
    CheckDatabaseUpdate(0, Req1+Req2)
    NextElement( gl_Object() )
  Next
  EndCreateTime2 = ElapsedMilliseconds() - StartTime
  Result2 + "Время создания: " + EndCreateTime2 + "мс. "
  
  SetDatabaseLong(0,0,FindX)
  SetDatabaseLong(0,1,FindX)
  SetDatabaseLong(0,2,FindY)
  SetDatabaseLong(0,3,FindY)
  L = 0
  
  StartTime = ElapsedMilliseconds()
  If DatabaseQuery(0, "SELECT NObject FROM tObject WHERE (X1 <= ?) AND (X2 >= ?) AND (Y1 <= ?) AND (Y2 >= ?) ORDER BY NObject ")
    While NextDatabaseRow(0) 
      L = GetDatabaseLong(0, 0)
    Wend
    FinishDatabaseQuery(0)
  EndIf
  EndSearchTime2 = ElapsedMilliseconds() - StartTime
  
  If L
    Result2 + "Найден: " + Str(L) + " За: " + Str(EndSearchTime2) + " мс."
  Else
    Result2 + "Объект не найден."
  EndIf
  
  ;- Вывод сообщения
  MessageRequester("Тест по координатам " + Str(FindX) + "," + Str(FindY), 
                   Result1 + #CRLF$ + Result2, #PB_MessageRequester_Ok | #PB_MessageRequester_Info)
  
  
  CloseDatabase(0)
Else
  Debug "Can't open database !"
EndIf
; === ТЕСТ 2. Конец ==========================================================================

У меня обычный перебор быстрее находит.

0

28

Sergeihik написал(а):

а строка вынесенна в отдельный массив

Там можно и не строку, а просто номер объекта.

Sergeihik написал(а):

приду домой подправлю допишу тест

Интересно протестировать рабочий пример на ассемблере. Подумаю о вашем варианте.

0

29

ДА на асме нужно переделывать доделывать зделал и удалил
оказалось что  переменную count глобальную компилятор путал с count в процедуре.....
и не работало как на асме так и в этом коде,пришлось имя поменять.
причём тут

Код:
Procedure Set_object(count1)
  ReDim koordinaty.koordinaty(count1)
  ReDim Stroka.s(count1)

сделал так заработало

Код:

Structure koordinaty
X.i
Y.i
X1.i
Y1.i
EndStructure
Global count.i
Global Dim koordinaty.koordinaty(0)
Global Dim Stroka.s(0)

Procedure Set_object(count1)
  ReDim koordinaty.koordinaty(count1)
  ReDim Stroka.s(count1)
Protected i.i
count=count1
For i=0 To count
koordinaty(i)\X=i
koordinaty(i)\X1=i+100
koordinaty(i)\Y=i
koordinaty(i)\Y1=i+100
Stroka(i)=Str(i)
Next
EndProcedure

Procedure.i poisk(*massiv.koordinaty,x,y,countt);для теста 
 Protected r.i=*massiv
  countt*16
countt+*massiv
cikl:
If x>*massiv\X And x<*massiv\X1 And y>*massiv\Y And y<*massiv\Y1
  *massiv-r
  *massiv/16
  ProcedureReturn *massiv
Else  
  If *massiv<>countt
    *massiv+16
    Goto cikl
  Else 
    ProcedureReturn -1
  EndIf
EndIf
EndProcedure




Set_object(1000000)


TimeS.q = ElapsedMilliseconds()
  index.l= poisk(@koordinaty(),count+50,count+50,count)
ArrTime.q = ElapsedMilliseconds() - TimeS

MessageRequester("Тест","index  "+Str(index)+" stroka "+Stroka.s(index)+"  время теста  "+Str(ArrTime))


Отредактировано Sergeihik (10.11.2022 19:39:19)

0

30

Sergeihik написал(а):

сделал так заработало

По логике вашего поиска сделал так:

Код:
Procedure poisk2( x, y )
  For i=0 To count
    With koordinaty(i)
      If x>=\X And x<=\X1 And y>=\Y And y<=\Y1
        ProcedureReturn i+1
      EndIf
    EndWith
  Next
  ProcedureReturn -1
EndProcedure

Это по сути обычный перебор. Немного быстрее с отладчиком, чуть медленнее без него. Но на миллионе объектов, разница всего в 3мс.
Накидал тестов:

Код:
Structure koordinaty
  X.i
  Y.i
  X1.i
  Y1.i
EndStructure

Global count.i
Global Dim koordinaty.koordinaty(0)
Global Dim Stroka.s(0)


Procedure Set_object(count1)
  ReDim koordinaty.koordinaty(count1)
  ReDim Stroka.s(count1)
  Protected i.i
  count=count1
  For i=0 To count
    koordinaty(i)\X=i
    koordinaty(i)\X1=i+100
    koordinaty(i)\Y=i
    koordinaty(i)\Y1=i+100
    Stroka(i)=Str(i)
  Next
EndProcedure


Procedure.i poisk(*massiv.koordinaty, x, y, countt) ; для теста 
  
  Protected r.i=*massiv
  countt*16
  countt+*massiv
  cikl:
  If x>*massiv\X And x<*massiv\X1 And y>*massiv\Y And y<*massiv\Y1
    *massiv-r
    *massiv/16
    ProcedureReturn *massiv
  Else  
    If *massiv<>countt
      *massiv+16
      Goto cikl
    Else 
      ProcedureReturn -1
    EndIf
  EndIf
EndProcedure

; учитывая логику процедуры poisk
Procedure poisk2( x, y )
  For i=0 To count
    With koordinaty(i)
      If x>=\X And x<=\X1 And y>=\Y And y<=\Y1
        ProcedureReturn i+1
      EndIf
    EndWith
  Next
  ProcedureReturn -1
EndProcedure

; поиск с нужной логикой (Нужно найти верхний, по указанным координатам, поэтому лучше искать от последнего к первому)
; а ещё лучше применить встречный поиск
Procedure poisk3( x, y )
  For i=count-1 To 0 Step -1
    With koordinaty(i)
      If x>=\X And x<=\X1 And y>=\Y And y<=\Y1
        ProcedureReturn i+1
      EndIf
    EndWith
  Next
  ProcedureReturn -1
EndProcedure


Set_object(1000000)


TimeS.q   = ElapsedMilliseconds()
index.l   = poisk( @koordinaty(), count+50, count+50, count )
ArrTime.q = ElapsedMilliseconds() - TimeS
MessageRequester("Тест poisk","index  "+Str(index)+" stroka "+Stroka.s(index)+"  время теста  "+Str(ArrTime))

TimeS   = ElapsedMilliseconds()
index   = poisk2( count+50, count+50 )
ArrTime = ElapsedMilliseconds() - TimeS
MessageRequester("Тест poisk2","index  "+Str(index)+" stroka "+Stroka.s(index)+"  время теста  "+Str(ArrTime))

; а это, чтобы протестировать нахождение дальнего объекта, опираясь на логику первого поиска
TimeS   = ElapsedMilliseconds()
index   = poisk3( 49, 49 ) 
ArrTime = ElapsedMilliseconds() - TimeS
MessageRequester("Тест 2 poisk3","index  "+Str(index)+" stroka "+Stroka.s(index)+"  время теста  "+Str(ArrTime))

; тут поиск с нужной логикой
TimeS   = ElapsedMilliseconds()
index   = poisk3( count-50, count-50 ) ; чтобы найти нужный элемент из прошлых тестов
ArrTime = ElapsedMilliseconds() - TimeS
MessageRequester("Тест poisk3","index  "+Str(index)+" stroka "+Stroka.s(index)+"  время теста  "+Str(ArrTime))

Отредактировано Webarion (10.11.2022 23:34:06)

0


Вы здесь » PureBasic - форум » Вопросы по PureBasic » Быстрый способ найти в списке прямоугольник, по координате.