Имя: Пароль:
1C
1С v8
Оптимизация кода для поиска
,
0 Ayvengo
 
07.09.12
14:57
Есть справочник точки, у которых есть широта, долгота, радиус
Есть регистр сведений, в который постоянно попадают данные где есть ссылка на справочник машины и широта, долгота.
Т.е. 2 таблицы с колонками:
Точка, Широта, Долгота, Радиус и Машина, Широта, Долгота.
Мне нужно обработать каждую строку таблицы с машинами, что бы понять, попала ли она в нужную точку.
Теоретически я могу сделать так

Для Каждого Машина из Машины Цикл
  ТекТочка = Неопределено;
  Для Каждого Точка из Точки Цикл
     Если ТочкаВходитВРадиус(Машина.Широта1, Машина.Долгота1, Точка.Широта2, Точка.Долгота2, Точка.Радиус) Тогда
        ТекТочка = Точка.Ссылка;
        Прервать;
     КонецЕсли;
  КонецЦикла;
  Если ТекТочка <> Неопределено Тогда
     ЗарегистрироватьПрибытиеВТочку(Машина.Ссылка, ТекТочка);
  КонецЕсли;
КонецЦикла;

Допустим , код примерно выглядет так. Хотелось бы его оптимизировать, что бы работало все быстрее. Количество строк в таблице точек = 10000, количество строчек в таблице машин - 10000
1 Happy Bear
 
07.09.12
15:00
(0) у тебя все 10000 машин на выезде постоянно?
2 Ayvengo
 
07.09.12
15:01
(1) у меня у каждой машины по 500 координат
3 Heckfy
 
07.09.12
15:03
Не совсем понял... А запрос с соединением не пойдет?
4 mzelensky
 
07.09.12
15:04
(3) на сколько я понимаю - запросом ты не вычислишь это:

ТочкаВходитВРадиус(Машина.Широта1, Машина.Долгота1, Точка.Широта2, Точка.Долгота2, Точка.Радиус)
5 Ayvengo
 
07.09.12
15:05
В запросе я не могу понять, что "ТочкаВходитВРадиус"
6 mzelensky
 
07.09.12
15:06
(4) но обойти машины и ИХ конкретные точки - это да, запросом! Так меньше обходов будет
7 Галахад
 
гуру
07.09.12
15:06
(0) Может логику поменять?
При записи в регистр проверять ТочкаВходитВРадиус
ну и соответственно чо-то писать.
8 Ayvengo
 
07.09.12
15:07
(6) у меня таблица очищается постоянно, когда точка обрабатывается. Т.е. 10000 строк - они постоянно с каждой секундой растут. GPS шлет данные.
9 mzelensky
 
07.09.12
15:07
(5) ты можешь запросом сразу сделать связку Машина-ТОЧКИ...т.е.  во втором цикле  " Для Каждого Точка из Точки Цикл" ты будешь перебирать не АБСОЛЮТНО ВСЕ ТОЧКИ, а толко точки конкретно этой машины. А дальше как ты написал.
10 Ayvengo
 
07.09.12
15:08
(9) я не могу сделать так, такой связки нет. Машина может прибыть не в свою точку :)
11 mzelensky
 
07.09.12
15:09
(10) и что при этом будет? Приехала машина не в свою точку...что дальше?
12 Ayvengo
 
07.09.12
15:09
(11) нужно оповестить диспетчера, что машина прибыла не в ту точку :)
13 Ayvengo
 
07.09.12
15:10
(11) там дальше жесть вообще, на самом деле. Всяческие событие, пересчет маршрута, оповещения и т.д.
14 AndyD
 
07.09.12
15:10
можно условно принять точку доставки не кругом, а квадратом, тогда будет намного проще проверку делать
15 AndyD
 
07.09.12
15:10
в том числе и в запросе
16 GLazNik
 
07.09.12
15:10
(5) Почему?
Можно показать функцию ТочкаВходитВРадиус?
17 y88
 
07.09.12
15:11
изменить формат хранения точки - добавить долгота_начало, долгота_конец, широта_начало, широта_конец
Получаем запросом точки (проверка на квадрат), далее уточняем на вхождение в круг
18 mzelensky
 
07.09.12
15:11
(12) в таком случае ты никак не оптимизируешь...в любом случае выбирать все-ко всем.
19 Ayvengo
 
07.09.12
15:12
(15) я делал не квадратом, прямоугольником... но в таком случае у меня очень сильно теряется точно. Это по геокоординатам, обрезая знаки, составлял соответствие и далее по нему искал - работает быстро, но не точно. Вот ищу метод лучше.
20 mzelensky
 
07.09.12
15:12
(16) там тригонометрические функции используются (косинус угла)...такое в запросе не посчитать!
21 Ayvengo
 
07.09.12
15:13
(19) точность теряется. Прямоугольник, т.к. изменение на сотую часть широты и долготы разные. т.е. смещение 59.50 - до 59-51 по широте отличается от 59.50 до 59.51 по долготе. Расстояние разное.
22 GLazNik
 
07.09.12
15:13
(20) а таки покажи... я так понимаю что нужно просто определить что точка попадает в круг... куда там косинус и угол...
23 Ayvengo
 
07.09.12
15:14
(22) не круг, земля же не круглая :Р

   Пи          = 3.14159;
   РадиусЗемли = 6372795;
   Расстояние  = РадиусЗемли*ATAN(Sqrt(Pow(COS(Пи*Широта2/180)*SIN(ABS(Пи*Долгота2/180-Пи*Долгота1/180)),2)+Pow(COS(Пи*Широта1/180)*SIN(Пи*Широта2/180)-SIN(Пи*Широта1/180)*COS(Пи*Широта2/180)*COS(ABS(Пи*Долгота2/180-Пи*Долгота1/180)),2))/(SIN(Пи*Широта1/180)*SIN(Пи*Широта2/180)+COS(Пи*Широта1/180)*COS(Пи*Широта2/180)*COS(ABS(Пи*Долгота2/180-Пи*Долгота1/180))));
   Возврат Расстояние <= Радиус;
24 Aprobator
 
07.09.12
15:14
(0) СКД с выводом результат в коллекцию значений. Правда функцию придется пихать в общий модуль.
25 vmv
 
07.09.12
15:15
тут математика и физика нужна, т.е. на формум морской навигации шагом марш
26 Ayvengo
 
07.09.12
15:15
(24) связь по функции???
27 acsent
 
07.09.12
15:15
для упрощения бери не круглый радиус а квадратный
28 mzelensky
 
07.09.12
15:16
(22) :) наивный :) Там учитывается широта, на которой находится точка!
29 LAAry
 
07.09.12
15:16
(23) Разложи гармонические функции и корень в ряд, выбери адекватное  для себя количество членов ряда и замени функцию на выражение в запросе
30 Aprobator
 
07.09.12
15:17
(26) вычисляемое поле и отбор по нему.
31 Ayvengo
 
07.09.12
15:17
(22) хотел x*x + y*y >= r*r? :)
32 LAAry
 
07.09.12
15:17
+(29) выражение получится огромным...
33 mzelensky
 
07.09.12
15:18
(23) я вот так считал:

//расчет вхождения
               Если Выборка.ШиротаКонтра<>0 и Выборка.ДолготаКонтра<>0 тогда
                   
                   //расстояние в секундах
                   РасчШирота= ? (  (Выборка.ШиротаКонтра - Выборка.ШиротаДока) >=0,(Выборка.ШиротаКонтра - Выборка.ШиротаДока), -(Выборка.ШиротаКонтра - Выборка.ШиротаДока) ) *60;  
                   РасчДолгота= ? (  (Выборка.ДолготаКонтра - Выборка.ДолготаДока) >=0,(Выборка.ДолготаКонтра - Выборка.ДолготаДока), -(Выборка.ДолготаКонтра - Выборка.ДолготаДока) ) *60;
                   
                   //расстояние в метрах
                   РасчШирота=РасчШирота*30.86;
                   РасчДолгота=РасчДолгота*30.86* cos(  Цел(Выборка.ШиротаДока/100)  );
                   
                   //рассчитываем удаленность точки
                   Удаленность=Sqrt( РасчШирота*РасчШирота + РасчДолгота*РасчДолгота);
                   
                   Если Удаленность<=100 тогда
                       КменЗачет=КменЗачет+1;                        
                       КДеньЗачет=КДеньЗачет+1;
                       Нстрока.Попадание="ДА";    
                   Иначе
                       КменНеЗачет=КменНеЗачет+1;
                       КДеньНеЗачет=КДеньНеЗачет+1;
                       Нстрока.Попадание="Нет";
                       КонецЕсли;
               Иначе
                   КменНовые=КменНовые+1;
                   КДеньНовые=КДеньНовые+1;
                   Нстрока.Попадание="Нет контрольной точки";
               КонецЕсли;
34 Aprobator
 
07.09.12
15:18
(32) с чего бы? Выражение в общей функции. В поле только имя функции с параметрами.
35 Aprobator
 
07.09.12
15:19
а млин соррь - мазнул. В (32) все верно сказано.
36 LAAry
 
07.09.12
15:22
Еще вариант. В запросе отбирать с 4 ближайшие точки для каждой машины по неточной формуле, а потом для этих 4-х точек считать точно.
37 Ayvengo
 
07.09.12
15:24
(33) так а чем это связано с оптимизацией, что-то не понимаю. Не трогаем функцию ТочкаВходитВРадиус :)
Тут больше интересно, может быть какие-то объекты можно использовать, которые работают быстрее чем ТЗ. Допустим ранее я использовал соответствие.
Было как-то так, но тут погрешность большая:

   Запрос = Новый Запрос(
   "ВЫБРАТЬ
   |    ТР_Точки.Ссылка КАК ТекущаяТочка,
   |    ТР_Точки.Широта,
   |    ТР_Точки.Долгота,
   |    ТР_Точки.Округление
   |ИЗ
   |    Справочник.тр_Точки КАК ТР_Точки
   |ГДЕ
   |    НЕ ТР_Точки.ПометкаУдаления");
   Выборка = Запрос.Выполнить().Выбрать();
   Соответствие = Новый Соответствие;
   Пока Выборка.Следующий() Цикл
       Если Не ЗначениеЗаполнено(Выборка.Широта) ИЛИ Не ЗначениеЗаполнено(Выборка.Долгота) или Выборка.Округление = 0 Тогда
           Продолжить;
       КонецЕсли;
       Коэффициент = 1/Число("1"+Формат(0,"ЧЦ="+Выборка.Округление+"; ЧН=; ЧВН=; ЧГ="));
       Широта1        = Окр(Выборка.Широта, Выборка.Округление);
       Широта2    = Широта1 + Коэффициент;
       Широта3    = Широта1 - Коэффициент;
       Долгота1    = Окр(Выборка.Долгота, Выборка.Округление);
       Долгота2    = Долгота1 + Коэффициент;
       Долгота3    = Долгота1 - Коэффициент;
       Соответствие.Вставить(Лев(Строка(Широта1), Выборка.Округление + СтрДлина(Цел(Широта1))) + ":"+ Лев(Строка(Долгота1), Выборка.Округление + СтрДлина(Цел(Долгота1))), Выборка.ТекущаяТочка);
       Соответствие.Вставить(Лев(Строка(Широта1), Выборка.Округление + СтрДлина(Цел(Широта1))) + ":"+ Лев(Строка(Долгота2), Выборка.Округление + СтрДлина(Цел(Долгота2))), Выборка.ТекущаяТочка);
       Соответствие.Вставить(Лев(Строка(Широта1), Выборка.Округление + СтрДлина(Цел(Широта1))) + ":"+ Лев(Строка(Долгота3), Выборка.Округление + СтрДлина(Цел(Долгота3))), Выборка.ТекущаяТочка);
       Соответствие.Вставить(Лев(Строка(Широта2), Выборка.Округление + СтрДлина(Цел(Широта2))) + ":"+ Лев(Строка(Долгота1), Выборка.Округление + СтрДлина(Цел(Долгота1))), Выборка.ТекущаяТочка);
       Соответствие.Вставить(Лев(Строка(Широта2), Выборка.Округление + СтрДлина(Цел(Широта2))) + ":"+ Лев(Строка(Долгота2), Выборка.Округление + СтрДлина(Цел(Долгота2))), Выборка.ТекущаяТочка);
       Соответствие.Вставить(Лев(Строка(Широта2), Выборка.Округление + СтрДлина(Цел(Широта2))) + ":"+ Лев(Строка(Долгота3), Выборка.Округление + СтрДлина(Цел(Долгота3))), Выборка.ТекущаяТочка);
       Соответствие.Вставить(Лев(Строка(Широта3), Выборка.Округление + СтрДлина(Цел(Широта3))) + ":"+ Лев(Строка(Долгота1), Выборка.Округление + СтрДлина(Цел(Долгота1))), Выборка.ТекущаяТочка);
       Соответствие.Вставить(Лев(Строка(Широта3), Выборка.Округление + СтрДлина(Цел(Широта3))) + ":"+ Лев(Строка(Долгота2), Выборка.Округление + СтрДлина(Цел(Долгота2))), Выборка.ТекущаяТочка);
       Соответствие.Вставить(Лев(Строка(Широта3), Выборка.Округление + СтрДлина(Цел(Широта3))) + ":"+ Лев(Строка(Долгота3), Выборка.Округление + СтрДлина(Цел(Долгота3))), Выборка.ТекущаяТочка);
   КонецЦикла;

Но работает супер быстро :)
38 LAAry
 
07.09.12
15:24
+(36) не точная формула - х1<=х<=x2 y1<=y<=y2, тогда 4 точки: (x1,y1),(x1,y2),(x2,y1),(x2,y2)
39 LAAry
 
07.09.12
15:26
(37) так в вызове "ТочкаВходитВРадиус()" то и тратится все время. Нужно запросом, иначе не ускорить.
40 LAAry
 
07.09.12
15:27
Можно немного ускорить, заменив обращение к функции на ее содержание. не красиво, но выхлоп будет небольшой.
41 GLazNik
 
07.09.12
15:31
(28) Есть немного
(37) так посмотреть в отладчике, на чем основная нагрузка. но скорее всего все будет на вызове функции ТочкаВходитВРадиус.
А преобразовывать в удобные для условий значений в момент записи в регистр не предлагать?
42 mzelensky
 
07.09.12
15:31
(0) попробуй вот это:

Для Каждого Точка из Точки Цикл
     Если ТочкаВходитВРадиус(Машина.Широта1, Машина.Долгота1, Точка.Широта2, Точка.Долгота2, Точка.Радиус) Тогда
        ТекТочка = Точка.Ссылка;
        Прервать;
     КонецЕсли;
  КонецЦикла;

написать в одну строку.
43 Ayvengo
 
07.09.12
15:33
(42) что-то слышал про одну строку - это реально ускоряет? :)
44 neckto
 
07.09.12
15:35
Что из себя представляют точки? Может машина находясь в точке А попасть в точку С минуя точку Б, которая находится между А и С. Может стоит перебирать не все точки? Если мы знаем, что машина находилась в точке А, то ее следует ожидать в точке Б, остальные точки выкинуть из перебора?
45 mzelensky
 
07.09.12
15:35
(43) попробуй :) просто это будет заметно только на очень длинных циклах - как раз твой случай!
46 mzelensky
 
07.09.12
15:36
(44) а что мешает машине объехать точку Б..двигаясь от А к С??? Ну скажем по другой дороге?
47 LAAry
 
07.09.12
15:38
попробовал бы все же ряды Маклорена :)
математика наше все!
48 GLazNik
 
07.09.12
15:38
(42) и (43)... не... вы это типа серьезно? то что идет перебор 100000000 строк, это так... мелочи... главное код в одну строку сделать
однозначно нужно выборку сокращать...
49 mzelensky
 
07.09.12
15:39
(48) ты реально ОЧЕНЬ наивный чувак :) читай по-больше литературы :)
50 neckto
 
07.09.12
15:44
(46) Значит машина появится в точке Г, которая рядом с А.
51 pumbaEO
 
07.09.12
15:45
52 GLazNik
 
07.09.12
15:47
(49) да и так читаю... вот колобка заканчиваю, за что дальше браться не подскажешь?
53 mzelensky
 
07.09.12
15:48
(52) берись за винипуха - многое подчеркнешь для себя :)
54 Ayvengo
 
07.09.12
15:49
(46) блин, ребят .. я не хочу рассказывать про бизнеспроцессы.. об этом в любом случае должен быть уведомлен диспетчер. Т.к. маршрут прокладывается по точкам.
(52) не проверишь, пока не поверишь ;)
55 Азазелло
 
07.09.12
16:02
(23) а формулу упростить не пробовали? во-первых, мне не понравилось, что  используются Ш1, Д1, Ш2, Д2, а не разницы между ними. это раз. во-вторых, для твоего случая формулу вычисления расстояний можно и нужно линеаризовывать.
56 Йохохо
 
07.09.12
16:03
самый скучный вариант, обойти все ПочтиПриехали(37) СовсемПриехали(0), будет самым быстрым
(37) можно еще оптимизировать сортировкой
57 Ayvengo
 
07.09.12
16:05
А вот интересно, такое подойдет условие
иксы - это широта, игреки - это долгота.
(x-x1)квадрат + (y-y1)квадрат <= Радиус в квадрате?
58 mzelensky
 
07.09.12
16:06
(57) читай внимательно (33) :)
59 acsent
 
07.09.12
16:06
(57) это же тэйбл скан
60 LAAry
 
07.09.12
16:06
Ну сам же писал, что это отбрасывание кривизны планеты.
61 Азазелло
 
07.09.12
16:07
(57) нет. для того, чтобы использовать данное условие, тебе нужно ШД перевести в х-у, причем, заранее условиться о точке отсчета.
62 acsent
 
07.09.12
16:07
(58) это что за хрень в (33)
63 Ayvengo
 
07.09.12
16:10
Тогда вопрос, как первести геокоординаты в координаты х, у
64 Азазелло
 
07.09.12
16:13
(63) чисто математически - это будет зависеть от того, что ты подразумеваешь под х и у.
65 mzelensky
 
07.09.12
16:15
(67) а ты как думаешь?!
66 Ayvengo
 
07.09.12
16:15
(64) проблема в том, что разные системы исчисления. Я не знаю как метры в геокоординаты переделать, а геокоординаты в метры.
67 Азазелло
 
07.09.12
16:17
(66) метры из радиуса Земли возьмутся )
68 mzelensky
 
07.09.12
16:17
(66) блин, чувак....в очереднйо раз - (33)!!!

там 2 точки с координатам. Широта и долгота это значения в "градусы, минуты, секунды" (как в GPS-приемниках)...а далее идет расчет растояния между двумы точками в МЕТРАХ!
69 pumbaEO
 
07.09.12
16:18
Читай (51) считай координатам хэш, для быстрого поиска входят ли точки в радиус 600 метров ищешь по первым 6 символам, потом по 7 - а дальше уже можешь и кривизну луны или земли или приливы учитывать для определения точно ли машина возле торговой точки.
70 mzelensky
 
07.09.12
16:19
(62) а для тебя, недолекий человек, наводящий вопрос - сколько метров в одной секунде (подсказка - секунде вращения земли)?
71 Азазелло
 
07.09.12
16:19
(70) а меня в (33) вот эта штука насторожила:
РасчДолгота=РасчДолгота*30.86* cos(  Цел(Выборка.ШиротаДока/100)  );

эт чО такое?
72 Азазелло
 
07.09.12
16:20
+(71) точнее, cos(  Цел(Выборка.ШиротаДока/100)  )
73 Азазелло
 
07.09.12
16:24
(66) еще вот эту ветку посмотри: Калькулятор расстояния (гео. координаты А и Б)
74 mzelensky
 
07.09.12
16:28
(71) рассказываю - как раз таки 1 секунда вращения земли НА ЭКВАТОРЕ равна 30.86 метров. Но т.к. земля не круг, а элипс - делается поправка на косинус угола широты точки (это для северного полушария). В результате мы получаем сколько метров в одной секунде на вашей широте. У меня, в Краснодаре, это примерно 21 метр!

И ты не поверишь .сколько времени я убил на то ,чтобы решить эту, как казалось бы, элементарнейщую задачку!
75 Ayvengo
 
07.09.12
16:32
(74) ну тут вообще эту задачу не решить, если учитывать горы ...
76 mzelensky
 
07.09.12
16:34
(75) Какие горы? Тут нужно просто определить - попаает одна точка в другую с определенной погрешность (радиусом) или нет. Если радиус маленький (у меня в 33 это 100 метров), то ни о каких горах собственно речь не идет.
77 Ayvengo
 
07.09.12
16:39
(76) вот скажи, зачем мне использовать твой метод вхождения, если у меня есть свой уже проверенный и работающий с большой точность. Я хочу оптимизировать цикл в цикле, что бы не было такого, что мне приходится обходить кажду точку для каждого события блока GPS на машине.
78 Азазелло
 
07.09.12
16:43
(77)ну извиняйте, это почти как рыбку съесть и ... ну, вы поняли.
вот здесь: http://xpoint.ru/forums/misc/thread/14553.xhtml кстати, очень похожая проблема.
79 Азазелло
 
07.09.12
16:45
+(78) *с большой точностью
это кто сказал? математически эта формула абсолютно точна. для поверхности шара. а Земля, как известно, не шар. И даже не эллипсоид.
80 mzelensky
 
07.09.12
16:45
(77) тебе же уже сказали - при твоих условиях ты там особо ничего не оптимизируешь, т.к. в любом случае нужно обходить ВСЕ для ВСЕХ!

Ты же сам говоришь ,что это ОБЯЗАТЕЛЬНОЕ УСЛОВИЕ...Функцию поиска вхождения ты тоже не хочешь трогать ,т.к. она у тебя правильная и точная - так что ты хочешь оптимизировать?! Больше ничего не остается!
81 Киборг
 
07.09.12
16:49
в запросе можно использовать приблизительную формулу (какую - не знаю), которая могла бы дать значение радиуса с погрешностью сверху

таким образом в запросе будут получены "точки, подозрительные на регистрацию прибытия" :)

их проверяешь точной формулой
82 Ayvengo
 
07.09.12
16:50
(79) ну я об этом и писал в (75)
(80) можно попробовать оптимизировать используя SQL Запрос :Р Там можно синусы, косинусы использовать ... ужс.. чет не хочу я так оптимизировать )))
83 Азазелло
 
07.09.12
16:51
(81) для малых значений радиуса вхождения даже уточнять не нужно. если только ТС хочет отсекать погрешности вплоть до миллиметров.
84 GLazNik
 
07.09.12
16:51
(77) запросом ограничь выборку разбив местность по "квадратам" с запасом на радиус... и ты получишь, что точка машины находится в квадрате с 10 точками. В результате твой цикл будет в разы короче.
85 Азазелло
 
07.09.12
16:52
(82) не, тебе просто нужно от точной формулы перейти к ее аппроксимации для малых значений дельт между Ш и Д.
86 Азазелло
 
07.09.12
16:53
+(85) и тогда уже с чистой совестью в условие для левого джойна запихивать. в принципе то, что в (84) предлагают
87 GLazNik
 
07.09.12
16:56
(82) сомневаюсь что скульный оптимизатор хорошо проглотит синусы и косинусы. и скорее всего он будет так же тупо для каждой записи одной таблицы перебирать записи другой таблицы. Но да, будет быстрее чем через 1С
88 Азазелло
 
07.09.12
17:05
(87) впендюрить врем. табличку со значениями син и кос для углов в диапазоне от 0 до пи с определенным шагом и делать в запросе с помощью этой таблички сплайн-аппроксимации ))
Оптимист верит, что мы живем в лучшем из миров. Пессимист боится, что так оно и есть.