powered by simpleCommunicator - 2.0.29     © 2024 Programmizd 02
Map
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пятничная задача: работнички.
11 сообщений из 61, страница 3 из 3
Пятничная задача: работнички.
    #40124252
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Заготовка для половинного деления и точности исходных данных до 0.001 может быть такая.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
SQL> with w0 as (select speed * 1e3 speed, capacity, delay * 1e3 delay from w)
  2  , r(lvl, m1, m2, k) as
  3   (select 1,
  4           trunc(654321 * min((speed * capacity + delay) / capacity) / count(*)),
  5           ceil(654321 * 2 * max(ceil((speed * capacity + delay) / capacity)) / count(*)),
  6           0
  7      from w0
  8    union all
  9    select lvl + 1,
 10           case when y > 654321 then m1 else (m1 + m2) / 2 end,
 11           case when y > 654321 then (m1 + m2) / 2 else m2 end,
 12           y
 13      from r
 14     cross apply (select sum(trunc(x / (speed * capacity + delay)) * capacity + least(trunc(mod(x, (speed * capacity + delay)) / speed), capacity)) y
 15                    from (select (m1 + m2) / 2 x, w0.* from w0)) a
 16     where m2 - m1 > 0.1)
 17  cycle lvl set cycle to 1 default 0
 18  select round(max(m1)) / 1e3 result from r
 19  /

    RESULT
----------
     135.2

Elapsed: 00:00:00.19
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124267
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop,

Тоже начал
Но пока в пути
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124302
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Хоть тема и ускакала дальше в поисках новых путей решения, позволю себе некоторые замечания
про прошедшей части прямой имитации распределения нагрузки на pl/sql.

1) Про f_box: интересный ход, стоивший мне некоторого ковыряния в носу, прежде чем я понял, как это работает.

Но делать так я бы не стал вот почему, по мере увеличения важности:
У меня инстинктивное отторжение к массивам массивов в pl/sql, главным образом, потому что я не понимаю деталей устройства самих
массивов, а истории про катастрофы в режиме компиляции plsql_debug = true только усиливают такой настрой.
Код такого типа, имхо, сложнее приспосабливать к новым вариантам использования, хотя в простых ситуациях я тоже склонен
использовать нечто схожее.

Главное состоит в том, что в своем исходном виде этот код плохо пригоден для работы с «большим числом коробок» при малом числе
«работников».
На «достаточно большом» числе работников он уверенно держится и забирается довольно далеко по количеству обработанных коробок.

Но когда число работников «недостаточно», то при достаточно большом числе коробок начинает зримо проигрывать
докрученной прямолинейной реализации очереди на массивах и, чем меньше доступно этому алгоритму работников и хуже
их скоростные параметры, тем быстрее он сваливается в ORA-01426

2) По поводу отставания sqlru_workers.get_min_wrk_time от f_box в последнем размещенном сравнении (забудем пока о непонятках с plsql_debug = true).

Думаю так: он обречен отставать при «слишком большом» числе работников с не совпадающими характеристиками,
но может и не отставать, а в некоторых исходных данных и опережать на большом числе коробок, при не слишком большой очереди работников.

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

Следующий момент состоит в том, что в очередь надо размещать не всех вообще работников, а группы работников с одинаковыми характеристиками (speed,capacity,delay).
Т.е. исходная очередь должна создаваться из запроса сорта
Код: plsql
1.
2.
3.
4.
Select speed,capacity,delay, count(*) as grp_workers
From w
Group by speed,capacity,delay
Order by speed



Всем работникам одной и той же производительности нужно выдавать работу одной операцией, увеличив на 1 индивидуальный счетчик и уменьшив на grp_workers количество оставшихся ящиков.
В частном случае, когда после группировки остается единственный элемент, все значения можно получать единственным вычислением, вообще без «верчения циклов».
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124313
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby

Всем работникам одной и той же производительности

можно для одинаковых менять "скорость" (speed/nn, capacity*nn)

но решал я несколько другую задачу,
выдать не только время, но и кто сколько ящиков открыл

я в Ваш код не вникал
но за столь короткое время я б стоко не наваял

у меня на pl/sql получилось очень просто
"параллелизм" не добавлял , ето усложнит(замедлит) расчет, не уверен что етого стоит

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
declare
 k int;
 n int  :=31;
 v_min_time number;
 v_cur_time number;
 v_min_k int;
 CURSOR C1 IS
 with w(id, speed, capacity, delay) as (
select 1 id, 10 speed, 12 capacity, 2 delay from dual
union all select 2, 30, 10, 5 from dual
union all select 3, 20, 4, 5 from dual
union all select 4, 35, 100, 100 from dual
)
 select w.*,0 k2  from w;
 TYPE TT_C1 IS TABLE OF C1%ROWTYPE ;
 v_T_C1 TT_C1;
begin
 open c1;
 FETCH C1 BULK COLLECT INTO  V_T_C1;
 k:=c1%rowcount;
 close c1;
 dbms_output.put_line(k);
 for i in 1..n loop
    null;
    v_min_time:=1e10; --можно брать напр обработку всех коробок 1-м работником
    for j in 1..k loop
       v_cur_time:=V_T_C1(j).speed*(V_T_C1(j).k2+1)+floor(V_T_C1(j).k2/V_T_C1(j).capacity)*V_T_C1(j).delay;
       if v_min_time>v_cur_time then
          v_min_time:=v_cur_time;
          v_min_k:=j;
       end if;
    end loop;
    V_T_C1(v_min_k).k2:=V_T_C1(v_min_k).k2+1;
 end loop;
 dbms_output.put_line(v_min_time);
end;
/

4
152

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.04



.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124324
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
у меня на pl/sql получилось очень просто
Есть несколько вариантов, находящие решения за секунды и половинное деление которое возвращает результат за доли секунды... предлагаю померять сколько будут выполняться вложенные циклы.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
set timing on
declare
  n   int;
begin
  for i in 1 .. 654321 loop
    for j in 1 .. 1e4 loop
      -- some dummy logic
      n   := i + j;
    end loop;
  end loop;
end;
/
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124331
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

предлагаю померять сколько будут выполняться вложенные циклы.

а смысл
пятничная, ето ж sql

pl/sql приплел, лиш потому что у booby уж слишком наворочено

я с 17-го отлучен от оракля,
нет реальной практики, офис (принтеры, сканеры, бумага, PK, win10/7, сеть, доступы, драйвера, и тд)

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

>>> если немного вникнуть в альтернативные решения.
вот именно, что если немного, то

>>> Например, добавить еще один столбец с конкатенацией.
попробовал, с наскока не получилось, плюнул
верю, что можно, но у меня просто не получилось

кто дочитал, с Новым Годом

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124354
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax, структурно, в самой общей идее, у нас с вами одинаковый код – имитировать прямую раздачу работ поштучно.
Это позволяет собирать любую статистику и логировать процесс работы.
Разница в том, что вы ищете следующий минимальный элемент прямым линейным поиском, а у меня список работников
всякий раз перестраивается так, чтобы работник с минимальной стоимостью всегда оказывался в первой позиции.
Это min-heap. Погружает нагруженного работой сотрудника в среднем за log(m), где m – число работников.

Касательно производительности – на малых m именно ваш вариант будет самым быстрым способом распределения по двум причинам.
До естественного предела в 7-8 квадратичный алгоритм предпочтительней – 3^2 = 9, а Log(8) = 3, это одна причина.
Вторая в том, у вас существенно меньше кода + вызовов функций.

Замеров я не делал, но само по себе, уменьшение объема работающего кода должно сдвигать область нейтрального выбора куда-то ближе к двум десяткам.

Про «не написал бы» - мне было откуда скопипастить.

С Новым Годом.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124370
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Идея о том, что надо работать на группах возникла прямо в процессе написания соответствующего поста.
Сейчас дорисовал код под это. Он не шибко чист в некоторых деталях, но как proof of concept, надеюсь, годится.
Решил оформить в виде самостоятельного пакета, вот код

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
create or replace package sqlru_workers2 authid definer
is

-- booby 01.01.2022

 Type T_Worker_Spc Is Record( speed number -- время обработки одной коробки в минутах
                            , capacity number -- число коробок обрабатываемых без перерыва
                            , delay number -- перерыв в минутах после обработки capacity коробок
                            , workers number -- число работников в шруппе, 
                            );

 --------------------
 TYPE T_WRK_CURSOR IS REF CURSOR RETURN T_Worker_Spc;
 -----
/*
  вернет отрицательный результат, если для заказанного положительного объема pN
  не найдется работников в курсоре pcrs вообще
  пример вызова
select sqlru_workers2.get_min_wrk_time(pcrs => CURSOR(
                                                      select speed, capacity, delay, count(*) as workers
                                                      From
                                                      (select 1 as speed, 2 as capacity, 3 as delay from dual
                                                      union all select 3, 10, 3 from dual
                                                      union all select 2, 4, 3 from dual) w
                                                       )
                                                      Group By  speed, capacity, delay
                                                      -- пока код от явной сортировки не зависит, но это может оказаться необходимым в последующих модификациях
                                                      Order by speed asc, capacity desc, delay asc
                         , pN => 10        -- число коробок
                          )
from dual
*/
 function get_min_wrk_time(pcrs in T_WRK_CURSOR -- курсор с работничками, ожидается на вход сортированным по speed
                         , pN in Number        -- число коробок
                         , do_output in integer default 0 -- формировать dbms_output
                          ) Return Number;

end sqlru_workers2;
/
create or replace package body sqlru_workers2 is

 TYPE T_WRKCURSOR_PLDICT is table of T_Worker_Spc index by pls_integer;
 --------------------------------------------------------------------
 Type T_Worker_State Is Record(
                                speed number -- время обработки одной коробки в минутах
                              , capacity number -- число коробок обрабатываемых без перерыва
                              , delay number -- перерыв в минутах после обработки capacity коробок
                              , workers pls_integer -- число работников с указанными характеристиками
                              , cur_run pls_integer -- осталось до перерыва, используется только в счете перерывов total_stops
                              , total_processed pls_integer --Number -- всего назначено коробок, обработано целой группой - total_processed * workers
                              , over_run pls_integer -- остаток, назначенный части группы < workers 
                                                     -- максимальное число работ, выполненное одним исполнителем = total_processed + case overrun > 0 Then 1 Else 0 End
                              , total_stops pls_integer -- число остановок
                              , total_cost Number       -- стоимость = здесь время завершения следующей работы 
                              );
 TYPE T_WORKERS_PLDICT is table of T_Worker_State; -- index by pls_integer;
 ------------------------------------------------------------------
 SUBTYPE T_QUEUE_ITEM is pls_integer;
 Type T_wrk_queue is Table of T_QUEUE_ITEM index by pls_integer;

 -- вернет отрицательный результат, если для заказанного положительного объема pN
 -- не найдется работников в курсоре pcrs вообще
 function get_min_wrk_time(pcrs in T_WRK_CURSOR -- курсор с работничками, ожидается на вход сортированным по speed
                         , pN in Number        -- число коробок
                         , do_output in integer default 0 -- 1 - формировать dbms_output
                          ) Return Number
 Is
   nstop_time Number := 0.0;
   N Number := Coalesce(pN, 0.0);
   a_workers T_WORKERS_PLDICT := T_WORKERS_PLDICT(); -- работнички
   a_wrkstate T_wrk_queue;                           -- очередь доступности
   i_worker pls_integer;

   isDbms Boolean := (Coalesce(do_output,0) != 0);
   i_cnt simple_integer := 0;
   work_batch Number;
  ------------------------------------------------------------------------------
  -- процедуры работы с очередью работников
  -------------------------------------------
  /*
  Function Is_Empty(pa_wrk_states in T_wrk_queue) return Boolean
    is
  Begin
    Return pa_wrk_states.COUNT = 0;
  End;
  */
  -- получение индекса родителя
  function get_parent_idx(p_idx_in in Pls_integer) return Pls_integer Deterministic
    is
  begin
    return Floor(0.5*(p_idx_in));
  end;
  --
  Function get_result_cost(pj in pls_integer) return Number
  Is
  Begin
    Return a_workers(pj).speed * (a_workers(pj).total_processed + case when a_workers(pj).over_run > 0 Then 1 Else 0 End)           
--           + floor((a_workers(pj).total_processed-1)/a_workers(pj).capacity)*a_workers(pj).delay
           + floor((a_workers(pj).total_processed + case when a_workers(pj).over_run > 0 Then 1 Else 0 End - 1)/a_workers(pj).capacity)*a_workers(pj).delay
           
           ;
  End;
  --
  -- сравнение элементов по значению
  Function Less(pi_parent in T_QUEUE_ITEM 
              , pi_child in T_QUEUE_ITEM 
               ) Return Boolean
  Is
    child_cost Number := a_workers(pi_child).total_cost;
    parent_cost Number := a_workers(pi_parent).total_cost;
  Begin

     Return Case when child_cost < parent_cost
                  Then True
                when child_cost = parent_cost
                  Then a_workers(pi_child).speed < a_workers(pi_parent).speed
                Else False
            End;
  End;
  -- всплытие с указанной позиции
  Procedure Swim( pa_array in out nocopy T_wrk_queue
                , pi_from in Pls_integer
                )
  is
    i_flow Pls_integer := pi_from;
    i_parent Pls_integer;
    r_temp T_QUEUE_ITEM;
  Begin

    PRAGMA INLINE(get_parent_idx,'YES');
    i_parent :=  get_parent_idx(i_flow);
    r_temp := pa_array(pi_from);

    PRAGMA INLINE(Less,'YES');
    While i_flow > 1
        And Less( pi_parent => pa_array(i_parent)
               ,  pi_child => r_temp
                )
    Loop
      pa_array(i_flow) := pa_array(i_parent);
      i_flow := i_parent;
      PRAGMA INLINE(get_parent_idx,'YES');
      i_parent := get_parent_idx(i_flow);
    End Loop;

    If i_flow != pi_from
      Then
      pa_array(i_flow) := r_temp;
    End if;

  End;
  -- погружение
  Procedure Sink(pa_array in out nocopy T_wrk_queue
                , pi_from in Pls_integer
                 )
  Is
    i_flow Pls_integer := pi_from;
    i_child Pls_integer := 0;
    i_lastPos Pls_integer := pa_array.COUNT;
    --
    r_temp T_QUEUE_ITEM; --Pls_integer;
  Begin
    If i_flow <= i_lastPos
      Then
      r_temp := pa_array(pi_from);
      While (i_flow + i_flow ) <= i_lastPos
      Loop
        i_child := (i_flow + i_flow);

        PRAGMA INLINE(Less,'YES');
        If i_child < i_lastPos
            And Less( pi_parent => pa_array(i_child)
                    , pi_child =>  pa_array(i_child+1)
                    )
          Then
            i_Child := i_child + 1;
        End If;

        PRAGMA INLINE(Less,'YES');
        Exit When Not Less( pi_parent => r_temp
                         ,  pi_child => pa_array(i_Child)
                          );
        pa_array(i_flow) := pa_array(i_child);
        i_flow := i_child;

      End Loop;
      If i_flow != pi_from
        Then
        pa_array(i_flow) := r_temp;
      End If;
    End If;
    --
  End;
  --------------------------------------------------
/* -- здесь не понадобилась  
  -- удаление и возврат максимального (минимального) элемента
  Procedure delMax(pa_array in out nocopy T_wrk_queue --T_wrk_states
                 , pr_Key_out OUT NOCOPY T_QUEUE_ITEM --Pls_integer --T_Worker_State
                   )
  Is
    i_rootPos Pls_integer := 1; --- индекс корня
    ilastPos Pls_integer; -- индекс последнего элемента
  Begin

    ilastPos := pa_array.COUNT;
    pr_Key_out :=  pa_array(i_rootPos);
    pa_array(i_rootPos) := pa_array(ilastPos);
    pa_array.Delete(ilastPos);

    PRAGMA INLINE(Sink,'YES');
    Sink(pa_array, i_rootPos);
    Return;
  End;
*/
  -- постановка работы в очередь ожидания завершения
  Procedure Insert_Q(pa_array in out nocopy  T_wrk_queue, pr_Key in T_QUEUE_ITEM) -- pls_integer) --T_Worker_State)
  Is
    ilastPos Pls_integer;
  Begin
   ilastpos := Coalesce(pa_array.Last + 1, 1 );
    pa_array(ilastpos) := pr_Key;
    PRAGMA INLINE(Swim,'YES');
    Swim( pa_array => pa_array, pi_from => ilastpos);
    Return;
  End;
  -- подсматриваем в текущего первого
  Function Peek_Next_Worker( pa_wrk_state In T_wrk_queue ) return T_QUEUE_ITEM --Pls_integer --T_Worker_State
  Is
  Begin
    Return pa_wrk_state(1);
  End;
 ---------------------------------------------------
   -- подготовка начального состояния работников
   -- с присвоением им начальной стоимости равной скорости обработки одной коробки
   procedure prepare_workers
   Is
    i pls_integer := 0;
    a_wrks T_WRKCURSOR_PLDICT;
                 
   Begin
     nstop_time := -1.0;
     If isDBMS Then
       dbms_output.put_line(' Workers:');
     End If;

     Loop
       Fetch pcrs
       Bulk Collect into a_wrks Limit 1000;
       Exit When a_wrks.Count = 0;
       a_workers.Extend(a_wrks.Count);
       For j in 1..a_wrks.Count
       Loop
         i := i + 1;

         a_workers(i).speed := a_wrks(j).speed;
         a_workers(i).capacity := a_wrks(j).capacity;
         a_workers(i).delay := a_wrks(j).delay;
         a_workers(i).workers := a_wrks(j).workers;
         a_workers(i).cur_run := a_wrks(j).capacity;
         a_workers(i).total_processed := 0.0;
         a_workers(i).over_run := 0.0;
         a_workers(i).total_stops := 0.0;
         a_workers(i).total_cost := a_wrks(j).speed;

         PRAGMA INLINE(Insert_Q,'YES');
         Insert_Q(pa_array => a_wrkstate, pr_Key => i);
       End Loop;
     End Loop;
     If isDbms And a_workers.count > 0 Then
       For j in 1..a_workers.count
       Loop
         dbms_output.put_line(
                              ' id='||j
                            ||' speed='||a_workers(j).speed
                            ||' capacity='|| a_workers(j).capacity
                            ||' delay='||a_workers(j).delay
                            ||' total_processed='||a_workers(j).total_processed
                            );
       End Loop;
     End If;
     Exception
       When Others THen
               dbms_output.put_line(
                            sqlcode||'-'||sqlerrm
                            ||' i='|| i
                            ||' a_wrkstate.Count='||a_wrkstate.Count
                            ||' a_wrkstate.First='||a_wrkstate.First
                            ||' a_wrkstate.Last='||a_wrkstate.Last
                            );
                            raise;
   End;
  -- обработка единственной группы  
  Procedure process_singular_group
  Is
  Begin
    
    i_worker := Peek_Next_Worker(a_wrkstate);
    work_batch := Floor(N/a_workers(i_worker).workers);
    a_workers(i_worker).total_processed := work_batch;
    -----------------------------------------------    
    a_workers(i_worker).over_run :=  N - work_batch;
    a_workers(i_worker).total_stops := Floor(work_batch / a_workers(i_worker).capacity);
    a_workers(i_worker).cur_run := N - a_workers(i_worker).total_stops * a_workers(i_worker).capacity
                                     - case when a_workers(i_worker).over_run > 0 Then 1 Else 0 End;
    
    ------------------------------------------------
    If a_workers(i_worker).cur_run = 0
      Then
        a_workers(i_worker).total_stops := a_workers(i_worker).total_stops + 1;
        a_workers(i_worker).cur_run := a_workers(i_worker).capacity;
    End IF;
    a_workers(i_worker).total_cost := a_workers(i_worker).speed * (a_workers(i_worker).total_processed 
                                    + 1 + case when a_workers(i_worker).over_run > 0 Then 1 Else 0 End)
                                    + a_workers(i_worker).total_stops*a_workers(i_worker).delay;    
    N := 0;
  End;    
 Begin -- основное тело
   If N > 0.0 Then
     PRAGMA INLINE(prepare_workers,'YES');
     prepare_workers;
     If a_workers.count > 0
       Then
       If a_workers.count > 1 
         Then        
        LOOP
          ---------------------------------------------
          PRAGMA INLINE(Peek_Next_Worker,'YES');
          i_worker := Peek_Next_Worker(a_wrkstate);
          work_batch := Least(N, a_workers(i_worker).workers);
          N := N - work_batch;                       

          If work_batch = a_workers(i_worker).workers
            Then
            i_cnt := a_workers(i_worker).total_processed+1;
            a_workers(i_worker).total_processed := a_workers(i_worker).total_processed+1;            
          Else 
            a_workers(i_worker).over_run := work_batch;
          End IF;

          a_workers(i_worker).cur_run := a_workers(i_worker).cur_run - 1;
          If a_workers(i_worker).cur_run = 0 
            Then
              a_workers(i_worker).total_stops := a_workers(i_worker).total_stops + 1;
              a_workers(i_worker).cur_run := a_workers(i_worker).capacity;
          End IF;
          a_workers(i_worker).total_cost := a_workers(i_worker).speed 
                                                  * (a_workers(i_worker).total_processed 
                                                      + 1 + case when a_workers(i_worker).over_run > 0 Then 1 Else 0 End
                                                     )
                                          + a_workers(i_worker).total_stops*a_workers(i_worker).delay;

  ------------------------------------------------------------------------
          PRAGMA INLINE(Sink,'YES');
          Sink(pa_array => a_wrkstate, pi_from => 1);
         Exit When N <=0;
        END LOOP;
      
       Else
         PRAGMA INLINE(process_singular_group,'YES');
         process_singular_group;  
       End If;   
        -- цикл съема результата
        If isDbms Then

         dbms_output.put_line(
                            ' final result:-------------'
                            );
        End If;
        
        For i in 1..a_wrkstate.Count

        Loop
          If isDbms Then
           dbms_output.put_line(
                              ' i='||i
                              ||' total_processed='||a_workers(i).total_processed
                              ||' over_run='||a_workers(i).over_run
                              ||' total_stops='||a_workers(i).total_stops
                              ||' result_cost='||get_result_cost(i)
                              );
          End If;

          PRAGMA INLINE(get_result_cost,'YES');
          nstop_time := Greatest(nstop_time, get_result_cost(i));
        End Loop;
     End If;
   End If;
   Return nstop_time;
 End;

end sqlru_workers2;
/


Скрипт проверки результатов в сравнении с f_box

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
alter package sqlru_workers2 compile package PLSQL_DEBUG = false plsql_code_type = native plsql_optimize_level = 3;
alter function f_box compile PLSQL_DEBUG = false plsql_code_type = native plsql_optimize_level = 3;

-- мало исполнителей, 2 группы
truncate table w;
insert into w(speed, capacity, delay)
(select 5, 2, .1 
from dual connect by level <= 2 
union all
select 1.5, 8, 5.1 
from dual connect by level <= 3
)
/
commit;
set timing on
-- это считается
select f_box(2878787
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 2878787
                                    , do_output => 0                                    
                                     ) result from dual;

-- демонстрация переполнения
select f_box(3878787
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 3878787
                                    , do_output => 0                                    
                                     ) result from dual;
-- случайное зполнение-1, не очень много работников
set timing off
truncate table w;
insert into w(speed, capacity, delay) 
select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 10 + 1)), trunc(dbms_random.value(1, 40 + 1))
    from dual
    connect by level <= 400;
commit;
set timing on
select f_box(777777 
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 777777 
                                     ) result from dual;
-- что там с зависимостями от N
select f_box(9876543
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 9876543
                                     ) result from dual;

-- случайное заполнение-2
set timing off
truncate table w;
insert into w(speed, capacity, delay) 
select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 20 + 1)), trunc(dbms_random.value(1, 20 + 1))
    from dual
    connect by level <= 40000;
commit;
set timing on
select f_box(777777 
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 777777 
                                     ) result from dual;

-- все одинаковые
set timing off
truncate table w;
insert into w(speed, capacity, delay)
select 0.5, 7, 3.1 
from dual connect by level <= 10e3
/
commit;
set timing on
select f_box(7777777 
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 7777777 
                                     ) result from dual;

-- малое число одинаковых групп
set timing off
truncate table w;
insert into w(speed, capacity, delay)                                     
(
select 0.5 speed, 7 capacity, 3 delay
from dual connect by level <= 3e3
union all 
select 0.3, 8, 4.8
from dual connect by level <= 3e3
union all 
select 5.8, 15, 11.4
from dual connect by level <= 3e3
union all
select 2.8, 13, 6.6
from dual connect by level <= 500
union all
select 5.8 speed, 15, 15.5
from dual connect by level <= 500
)
/
commit;
set timing on
--
select f_box(777777 
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 777777
                                     ) result from dual;


-- в общем, бяка
set timing off
truncate table w;
insert into w(speed, capacity, delay)                                     
(
select 0.5 speed, 7 capacity, 3 delay
from dual connect by level <= 3e3
union all 
select 0.3, 8, 4.8
from dual connect by level <= 2e3
union all 
select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 20 + 1)), trunc(dbms_random.value(1, 20 + 1))
    from dual
    connect by level <= 6000
)
/
commit;
set timing on
--
select f_box(876543
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 876543
                                     ) result from dual;
-- что там с зависимостями от N
select f_box(9876543
            ) result from dual;            
select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
                                                     from w
                                                     group by speed, capacity, delay
                                                     order by speed asc, capacity desc, delay asc
                                                     )
                                    , pn => 9876543
                                     ) result from dual;




Прогон на не выдающейся железке.

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
SQL> alter package sqlru_workers2 compile package PLSQL_DEBUG = false plsql_code_type = native plsql_optimize_level = 3;

Пакет изменен.

SQL> alter function f_box compile PLSQL_DEBUG = false plsql_code_type = native plsql_optimize_level = 3;

Функция изменена.

SQL> 
SQL> -- мало разномастных исполнителей
SQL> truncate table w;

Таблица усечена.

SQL> insert into w(speed, capacity, delay)
  2  (select 5, 2, .1
  3  from dual connect by level <= 2 
  4  union all
  5  select 1.5, 8, 5.1
  6  from dual connect by level <= 3
  7  )
  8  /

Создано строк: 5.

SQL> commit;

Фиксация обновлений завершена.

SQL> set timing on
SQL> -- это считается
SQL> select f_box(2878787
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
 1599726,6                                                                      

Затрач.время: 00:00:05.55
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 2878787
  7                                      , do_output => 0
  8                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
 1599726,6                                                                      

Затрач.время: 00:00:00.40
SQL> 
SQL> -- демонстрация переполнения
SQL> select f_box(3878787
  2              ) result from dual;
select f_box(3878787
       *
ошибка в строке 1:
ORA-01426: переполнение числа 
ORA-06512: на  "SCOTT.F_BOX", line 33 


Затрач.время: 00:00:07.30
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 3878787
  7                                      , do_output => 0
  8                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
 2155422,3                                                                      

Затрач.время: 00:00:00.56
SQL> -- случайное зполнение-1, не очень много работников
SQL> set timing off
SQL> truncate table w;

Таблица усечена.

SQL> insert into w(speed, capacity, delay)
  2  select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 10 + 1)), trunc(dbms_random.value(1, 40 + 1))
  3      from dual
  4      connect by level <= 400;

Создано строк: 400.

SQL> commit;

Фиксация обновлений завершена.

SQL> set timing on
SQL> select f_box(777777
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
     11765                                                                      

Затрач.время: 00:00:01.50
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 777777
  7                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
     11765                                                                      

Затрач.время: 00:00:01.34
SQL> -- что там с зависимостями от N
SQL> select f_box(9876543
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
    149458                                                                      

Затрач.время: 00:00:18.36
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 9876543
  7                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
    149458                                                                      

Затрач.время: 00:00:16.62
SQL> 
SQL> -- случайное заполнение-2
SQL> set timing off
SQL> truncate table w;

Таблица усечена.

SQL> insert into w(speed, capacity, delay)
  2  select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 20 + 1)), trunc(dbms_random.value(1, 20 + 1))
  3      from dual
  4      connect by level <= 40000;

Создано строк: 40000.

SQL> commit;

Фиксация обновлений завершена.

SQL> set timing on
SQL> select f_box(777777
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
      78,1                                                                      

Затрач.время: 00:00:01.81
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 777777
  7                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
      78,1                                                                      

Затрач.время: 00:00:01.61
SQL> 
SQL> -- все одинаковые
SQL> set timing off
SQL> truncate table w;

Таблица усечена.

SQL> insert into w(speed, capacity, delay)
  2  select 0.5, 7, 3.1
  3  from dual connect by level <= 10e3
  4  /

Создано строк: 10000.

SQL> commit;

Фиксация обновлений завершена.

SQL> set timing on
SQL> select f_box(7777777
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
     733,1                                                                      

Затрач.время: 00:00:10.99
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 7777777
  7                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
     733,1                                                                      

Затрач.время: 00:00:00.02
SQL> 
SQL> -- малое число одинаковых групп
SQL> set timing off
SQL> truncate table w;

Таблица усечена.

SQL> insert into w(speed, capacity, delay)
  2  (
  3  select 0.5 speed, 7 capacity, 3 delay
  4  from dual connect by level <= 3e3
  5  union all
  6  select 0.3, 8, 4.8
  7  from dual connect by level <= 3e3
  8  union all
  9  select 5.8, 15, 11.4
 10  from dual connect by level <= 3e3
 11  union all
 12  select 2.8, 13, 6.6
 13  from dual connect by level <= 500
 14  union all
 15  select 5.8 speed, 15, 15.5
 16  from dual connect by level <= 500
 17  )
 18  /

Создано строк: 10000.

SQL> commit;

Фиксация обновлений завершена.

SQL> set timing on
SQL> --
SQL> select f_box(777777
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
       106                                                                      

Затрач.время: 00:00:01.09
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 777777
  7                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
       106                                                                      

Затрач.время: 00:00:00.01
SQL> 
SQL> 
SQL> -- в общем, бяка
SQL> set timing off
SQL> truncate table w;

Таблица усечена.

SQL> insert into w(speed, capacity, delay)
  2  (
  3  select 0.5 speed, 7 capacity, 3 delay
  4  from dual connect by level <= 3e3
  5  union all
  6  select 0.3, 8, 4.8
  7  from dual connect by level <= 2e3
  8  union all
  9  select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 20 + 1)), trunc(dbms_random.value(1, 20 + 1))
 10      from dual
 11      connect by level <= 6000
 12  )
 13  /

Создано строк: 11000.

SQL> commit;

Фиксация обновлений завершена.

SQL> set timing on
SQL> --
SQL> select f_box(876543
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
       125                                                                      

Затрач.время: 00:00:01.40
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 876543
  7                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
       125                                                                      

Затрач.время: 00:00:00.48
SQL> -- что там с зависимостями от N
SQL> select f_box(9876543
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
    1433,4                                                                      

Затрач.время: 00:00:15.03
SQL> select sqlru_workers2.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay, count(*) as workers
  2                                                       from w
  3                                                       group by speed, capacity, delay
  4                                                       order by speed asc, capacity desc, delay asc
  5                                                       )
  6                                      , pn => 9876543
  7                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
    1433,4                                                                      

Затрач.время: 00:00:05.26
SQL> 
SQL> 


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

При увеличении масштаба задачи, рано или поздно, придется уходить на «чистый» sql, примерно в том стиле,
как это было показано в посте с кодом на t-sql.
Функциональный индекс на GTT способен полностью решить тему с выбором следующего назначаемого.
Но платить штрафы за использование buffer pool и прочие поддержки транзакционности без специальной необходимости не хочется.
Ускорять что-то усложнением реализации пирамиды в стиле k-way или иным способом - вряд ли того стоит вообще.
Несопоставимо проще уж просто сесть на GTT, при особо острой необходимости.


PS
Надо будет когда-нибудь поинтересоваться, говорит ли теория массового обслуживания что-то полезное про именно такую формулировку задачи.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124434
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
что-то такое рекурсивное получилось на 21xe
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
with
-- сколько штук сделает за период
function getCount(
    period number, speed number,capacity number, delay number
    ) return number
is
    short   number := speed*capacity ;
    lng1    number  := speed*capacity+delay;
begin
    return 
                case
                    when mod(period, lng1) <= short then trunc(mod(period, lng1) / speed)
                    else capacity
                end +
                trunc(period/lng1) * capacity
                ;
end;
--за какое время выполнит работник задание один
function GetPeriodByCount(cnt number, speed number,capacity number, delay number) return number
is
begin
    return cnt * speed + trunc((cnt-1) / capacity) * delay;
end;
workers(speed, capacity , delay) as
(
select 10,  12,   2 from dual union all 
select 30,  10,   5 from dual union all 
select 20,   4,   5 from dual union all 
select 35, 100, 100 from dual 
),
data(accuracy, mn, mx, mn_prev, mx_prev, err, x, lv)
as
(
    select 0.0001 accuracy,
           min(GetPeriodByCount(1, speed, capacity , delay)) mn,
           min(GetPeriodByCount(:p_count, speed, capacity , delay)) mx,
           -1 mn_prev,
           -1 mx_prev,
           1 err,
           1 x,
           1 lv
      from workers
    union all
    select accuracy,
           case when (
                    select sum(getCount((mn+mx)/2, speed, capacity, delay))
                      from workers
                     ) < :p_count then (mn+mx)/2
                else mn
           end mn,
           case when (
                    select sum(getCount((mn+mx)/2, speed, capacity, delay))
                      from workers
                     ) > :p_count then (mn+mx)/2
                when (
                    select sum(getCount((mn+mx)/2, speed, capacity, delay))
                      from workers
                     ) = (
                    select sum(getCount((mn+mx)/2-accuracy, speed, capacity, delay))
                      from workers
                     )
                     and
                     (
                    select sum(getCount((mn+mx)/2, speed, capacity, delay))
                      from workers
                     ) = :p_count
                     then (mn+mx)/2
                else mx
           end mx,
           mn mn_prev,
           mx mx_prev,
           1 err,
           (mn+mx)/2 x,
           lv+1
      from data
 where lv < 111
  and (mx-mn) > accuracy
  and (mx <> mx_prev or mn <> mn_prev)
)
select x, lv from data
ORDER BY lv desc
  FETCH FIRST 1 ROWS ONLY

152,00003814697265625 за 22 шага
:p_count=31
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124435
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
чуть поправил через cross apply
подсмотрел кляузу cycle lv set cycle to 1 default 0

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
with
-- сколько штук сделает за период
function getCount(
    period number, speed number,capacity number, delay number
    ) return number
is
    short   number := speed*capacity ;
    lng1    number  := speed*capacity+delay;
begin
    return 
                case
                    when mod(period, lng1) <= short then trunc(mod(period, lng1) / speed)
                    else capacity
                end +
                trunc(period/lng1) * capacity
                ;
end;
--за какое время выполнит работник задание один
function GetPeriodByCount(cnt number, speed number,capacity number, delay number) return number
is
begin
    return cnt * speed + trunc((cnt-1) / capacity) * delay;
end;
workers(speed, capacity , delay) as
(
select 10,  12,   2 from dual union all 
select 30,  10,   5 from dual union all 
select 20,   4,   5 from dual union all 
select 35, 100, 100 from dual 
),
data(accuracy, mn, mx, mn_prev, mx_prev, err, x, lv)
as
(
    select 0.0001 accuracy,
           min(GetPeriodByCount(1, speed, capacity , delay)) mn,
           min(GetPeriodByCount(:p_count, speed, capacity , delay)) mx,
           -1 mn_prev,
           -1 mx_prev,
           1 err,
           1 x,
           1 lv
      from workers
    union all
    select accuracy,
           case when curcnt < :p_count then a.x
                else mn
           end mn,
           case when curcnt > :p_count                       then a.x
                when curcnt = curcnt_1 and curcnt = :p_count then a.x
                else mx
           end mx,
           mn mn_prev,
           mx mx_prev,
           1 err,
           a.x,
           lv+1
      from data cross apply
        (
        select sum(getCount((mn+mx)/2, speed, capacity, delay)) curcnt,
               sum(getCount((mn+mx)/2-accuracy, speed, capacity, delay)) curcnt_1,
               max((mn+mx)/2) x
          from workers w
        ) a
 where lv < 111
  and (mx-mn) > accuracy
  and (mx <> mx_prev or mn <> mn_prev)
)
cycle lv set cycle to 1 default 0
select x, lv from data
 ORDER BY lv desc
 FETCH FIRST 1 ROWS ONLY

...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124537
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
снизил количество шагов в 3 раза и повысил точность
вроде бы :)
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
with
-- сколько штук сделает за период
function getCount(
    period number, speed number,capacity number, delay number
    ) return number
is
    short   number := speed*capacity ;
    lng1    number  := speed*capacity+delay;
begin
    return 
                case
                    when mod(period, lng1) <= short then trunc(mod(period, lng1) / speed)
                    else capacity
                end +
                trunc(period/lng1) * capacity
                ;
end;
--за какое время выполнит работник задание один
function GetPeriodByCount(cnt number, speed number,capacity number, delay number) return number
is
begin
    return cnt * speed + trunc((cnt-1) / capacity) * delay;
end;
workers(speed, capacity , delay) as
(
select 10,  12,   2 from dual union all 
select 30,  10,   5 from dual union all 
select 20,   4,   5 from dual union all 
select 35, 100, 100 from dual 
),
data(accuracy, mn, mx, mn_prev, mx_prev, err, x, lv, maxMinPeriod, result)
as
(
    select 0.0001 accuracy,
           min(GetPeriodByCount(1, speed, capacity , delay)) mn,
           min(GetPeriodByCount(:p_count, speed, capacity , delay)) mx,
           -1 mn_prev,
           -1 mx_prev,
           1 err,
           1 x,
           1 lv,
           0+null maxMinPeriod,
           0+null result
      from workers
    union all
    select accuracy,
           case when curcnt < :p_count then a.x
                else mn
           end mn,
           case when curcnt > :p_count                       then a.x
                when curcnt = curcnt_1 and curcnt = :p_count then a.x
                else mx
           end mx,
           mn mn_prev,
           mx mx_prev,
           1 err,
           a.x,
           lv+1,
           a.maxMinPeriod maxMinPeriod,
           case
                when curcnt >= :p_count then
                    (
                        select sum(getCount(a.maxMinPeriod, speed, capacity, delay))
                          from workers w
                        having sum(getCount(a.maxMinPeriod, speed, capacity, delay)) >= :p_count
                           and sum(getCount(a.maxMinPeriod-accuracy, speed, capacity, delay)) < :p_count
                    )
           end
           result
      from data cross apply
        (
        select sum(getCount((mn+mx)/2, speed, capacity, delay)) curcnt,
               sum(getCount((mn+mx)/2-accuracy, speed, capacity, delay)) curcnt_1,
               max((mn+mx)/2) x,
               max(GetPeriodByCount(getCount((mn+mx)/2, speed, capacity, delay), speed, capacity, delay)) maxMinPeriod
          from workers w
        ) a
 where lv < 111
  and (mx-mn) > accuracy
  and (mx <> mx_prev or mn <> mn_prev)
  and result is null
)
cycle lv set cycle to 1 default 0
select * from data
 --ORDER BY lv desc
 --FETCH FIRST 1 ROWS ONLY

accuracymnmxmn_prevmx_preverrxlvmaxMinPeriodresult0,000110314-1-111100,000110162103141162216200,0001861621016218638000,0001124162861621124412000,00011431621241621143514200,0001143152,51431621152,56152310
...
Рейтинг: 0 / 0
11 сообщений из 61, страница 3 из 3
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пятничная задача: работнички.
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]