powered by simpleCommunicator - 2.0.29     © 2024 Programmizd 02
Map
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пятничная задача: работнички.
61 сообщений из 61, показаны все 3 страниц
Пятничная задача: работнички.
    #40122078
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Всем привет! Предлагаю отвлечься от годовой отчетности и финальных релизов и немного размять мозги.

Задача

Работники обрабатывают коробки на складе, при это работники имеют следующие характеристики
speed - время обработки одной коробки в минутах
capacity - число коробок, которые работник в состоянии обработать без перерыва
delay - длительность перерыва в минутах

За какое минимальное время работники смогут обработать N коробок (задаётся биндом или константой, не суть)?

Пример данных

Код: plsql
1.
2.
3.
4.
create table w(speed, capacity, delay) as 
(select 1, 2, 3 from dual
union all select 3, 10, 3 from dual
union all select 2, 4, 3 from dual);


Если число коробок 10, то работники их смогут обработать за 8 минут.
Двое обработали по 4-ре коробки и один две (по три минуты каждую).

Как обычно, объявляется конкурс на SQL решение. Если кто захочет решить на 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.
SQL> var n number
SQL>
SQL> create table w(speed, capacity, delay) as
  2  (select 1, 2, 3 from dual
  3  union all select 3, 10, 3 from dual
  4  union all select 2, 4, 3 from dual);

Table created.

SQL>
SQL> exec :n := 10;

PL/SQL procedure successfully completed.

SQL>
...
 12  /

    RESULT
----------
         8

SQL>
SQL> insert into w select 3.5, 100, 100 from dual;

1 row created.

SQL>
SQL> exec :n := 20;

PL/SQL procedure successfully completed.

SQL>
...
 12  /

    RESULT
----------
        15


PS. Не дождался пятницы. Праздники и всё такое.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122088
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop,

Добавте id работника, будет интереснее (легче) анализировать

1-й 4 коробки
2-й 2 коробки
3-й 4 коробки


зы
авторвремя обработки одной коробки в минутах

10 коробок, на таких данных время не может быть меньше 10

ззы
к слову, время может быть дробным (напр 1.5 полтора минуты)

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122098
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
Добавте id работника, будет интереснее (легче) анализировать
Да, с айдишником на них было бы лушче ссылаться, но я надеюсь всё достаточно тривиально и глубокого анализа не потребуется.
Данных предоставлено ровно столько сколько требуется для нахождения решения.
Stax
10 коробок, на таких данных время не может быть меньше 10
Параллельная обработка творит чудеса.
Stax
к слову, время может быть дробным (напр 1.5 полтора минуты)
Да, это так.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122107
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

всё достаточно тривиально и глубокого анализа не потребуется.

с тривиальностью у меня как-раз проблема

подожду решений (без модельки), и гляну где я ступил

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122164
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop
...всё достаточно тривиально и глубокого анализа не потребуется.
...

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

Пока я не вполне ясно понимаю, как это красиво сделать на sql (вероятно, рекурсивный запрос должен в итоге как надо подкрутиться)
а для pl/sql это правда не сильно сложная задача.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122190
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby,

dbms_photoshop решил без Ид (11строк кода)

как-то он хитро раздает коробки

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

старею, утро-вечера мудренее

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122197
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
booby,

dbms_photoshop решил без Ид (11строк кода)

как-то он хитро раздает коробки

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

старею, утро-вечера мудренее

.....
stax
Никаких завязок на rowid у меня в решении нет.
Если не удаётся решить без ID работника - не понимаю в чём проблема проставить его с помощью rownum.

Написанное ниже вполне имеет смысл и не завязано на ID работника (это, впрочем, не значит что я решал так :)).
booby
надо первым выбирать для следующей работы того, у кого время обработки наименьшее

Если SQL вызывает трудности можно в конце концов написать на PL/SQL и прикинуть переписываемо ли оно на pure SQL.

На мой взгляд на SQL решается вполне изящно и гармонично.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122199
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax,

пронумеровать работников по их скорости в любом случае можно.
Это, так или иначе, почти наверно пригодится.
А 11 строк кода надо сначала посмотреть....
Мне такую задачу проще сначала записать в pl/sql решить.
И там (в pl/sql) ее нельзя решить правильно, если порядок по скорости работы не будет явно поддерживаться.

Я так как-то думаю..
попробую записать на pl/sql чуть позже.
Про sql потом яснее станет.

PS
в этой задаче два порядка - по времени следующей доступности работника и по быстродействию самих работников.
решение должно правильно их комбинировать.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122204
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop
.
На мой взгляд на SQL решается вполне изящно и гармонично.


Скажем так, model я вряд ли был бы готов признать за гармонию, а в альтернативах надо аккуратно разбираться с порядком следования, что для sql исторически несколько чужеродно.

возможно и гармонично, я не отрицаю пока, но хочу сначала увидеть, как это должно выглядеть на pl/sql
позже положу
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122206
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

Никаких завязок на rowid у меня в решении нет.


верю, просто я не в ту сторону думаю

залезло в голову, кто сколько коробок обработал, и мешает


ps
алгоритм придумал (без ид), завтра реализую
.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122254
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
...
залезло в голову, кто сколько коробок обработал, и мешает
....
stax

это тоже не трудно получить, но в исходной постановке не требуется.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122264
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop,

не причисывал

Код: 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.
with w(id,speed, capacity, delay) as (
              select 1,1, 2, 3 from dual
union all select 2,3, 10, 3 from dual
union all select 3,2, 4, 3 from dual
union all select 4,3.5, 100, 100 from dual
)
, r (id,speed, capacity, delay,k2,l,s) as (
 select  w.*
  ,lag(0,1,1) over (order by speed)
  ,1
  ,speed
 from w
union all
 select  id,speed, capacity, delay
  ,k2+lag(0,1,1) over (order by speed*(k2+1)+floor(k2/capacity)*delay,id)
  ,l+1
  ,speed*(k2+1)+floor(k2/capacity)*delay
 from r where l<20
)
select min(s) m from r where l=20
--order by l,id
/
SQL> /

         M
----------
        15

ed
....
SQL> /

         M
----------
         8





.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122274
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
на pl/sql получилось так.
Строк вместе 271 на спецификацию и тело пакет, 237 на тело.
8 на первоначальных исходных данных получено
ну, и я, более-менее, без долгого тестирования верю, в то, что здесь написано.
Осталось быстро поверить в 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.
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.
create or replace package sqlru_workers authid definer
is
 
-- booby 22.12.2021

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

 --------------------                               
 TYPE T_WRK_CURSOR IS REF CURSOR RETURN T_Worker_Spc;
 -----
/*
  вернет отрицательный результат, если для заказанного положительного объема pN 
  не найдется работников в курсоре pcrs вообще
  пример вызова:
select sqlru_workers.get_min_wrk_time(pcrs => CURSOR(
                                                      select speed, capacity, delay 
                                                      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
                                                       )
                         , pN => 10        -- число коробок
                          )
from dual 
*/
 function get_min_wrk_time(pcrs in T_WRK_CURSOR -- курсор с работничками
                         , pN in Number        -- число коробок
                          ) Return Number; 

end sqlru_workers;
/
create or replace package body sqlru_workers is

 TYPE T_WORKERS_PLDICT is table of T_Worker_Spc index by pls_integer; 

 Type T_Worker_State Is Record( inum pls_integer -- номер работника в списке работников
                              , current_capacity Number -- текущее число обработанных коробок работником
                              , next_avalible Number -- следующее время доступности работника
                              );
 TYPE T_wrk_states is table of T_Worker_State Index by pls_integer;
 
 -- вернет отрицательный результат, если для заказанного положительного объема pN 
 -- не найдется работников в курсоре pcrs вообще
 function get_min_wrk_time(pcrs in T_WRK_CURSOR -- курсор с работничками
                         , pN in Number        -- число коробок
                          ) Return Number
 Is
   nstop_time Number := 0.0;
   N Number := Coalesce(pN, 0.0);
   a_workers T_WORKERS_PLDICT; -- работнички
   a_wrkstate T_wrk_states;     -- их рабочее состояние
   --
   ncur_time Number := 0.0; -- текущее время
   r_worker T_Worker_State;

  ------------------------------------------------------------------------------
  -- процедуры работы с очередью занятых работников
  -------------------------------------------
  Function Is_Empty(pa_wrk_states in T_wrk_states) 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;  
  -- копирование элемента 
  Procedure Copy_ItemTo(pa_array in out nocopy T_wrk_states
                      , pi_from in Pls_integer -- индекс с которого копируется элемент
                      , pi_to in Pls_Integer   -- индекс в который копируется элемент
                      )
  is
  Begin
    pa_array(pi_to) := pa_array(pi_from);
  End;   
  -- сравнение элементов по значению
  Function Less(pr_parent in T_Worker_State
              , pr_child in T_Worker_State
               ) Return Boolean
  Is
  Begin          
    Return Case 
             When  pr_child.next_avalible < pr_parent.next_avalible 
               Then True
             When pr_child.next_avalible = pr_parent.next_avalible       
               Then (a_workers(pr_child.inum).speed < a_workers(pr_parent.inum).speed)
           Else False
           End;
  End;  
  -- всплытие с указанной позиции
  Procedure Swim( pa_array in out nocopy T_wrk_states
                , pi_from in Pls_integer
                )
  is
    i_flow Pls_integer;
    i_parent Pls_integer;
    r_temp T_Worker_State;
  Begin
    i_flow := pi_from;
    i_parent :=  get_parent_idx(i_flow);
    r_temp := pa_array(pi_from);    
    
    While i_flow > 1
        And Less( pr_parent => pa_array(i_parent)
               ,  pr_child => r_temp 
                )   
    Loop
      pa_array(i_flow) := pa_array(i_parent); 
      i_flow := i_parent;
      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_states
                , pi_from in Pls_integer
                 )
  Is
    i_flow Pls_integer;
    i_child Pls_integer;
    i_lastPos Pls_integer;

    r_temp T_Worker_State;
  Begin
    i_lastPos := pa_array.COUNT;
    i_flow := pi_from;   
    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); 

        If i_child < i_lastPos
            And Less( pr_parent => pa_array(i_child)
                    , pr_child =>  pa_array(i_child+1)
                    )
          Then
            i_Child := i_child + 1;
        End If;
        
        Exit When Not Less( pr_parent => r_temp
                         ,  pr_child => pa_array(i_Child)
                          ); 
                          
        Copy_ItemTo(pa_array => pa_array, pi_from => i_child, pi_to => i_flow);
        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_states
                 , pr_Key_out OUT NOCOPY T_Worker_State
                   )
  Is
    i_rootPos Pls_integer := 1; --- индекс корня
    ilastPos Pls_integer; -- индекс последнего элемента
  Begin

    ilastPos := pa_array.COUNT;
    -- возвращаемое (снимаемое) значение
    pr_Key_out :=  pa_array(i_rootPos);

    Copy_ItemTo(pa_array => pa_array, pi_from => ilastPos, pi_to => i_rootPos); 
    pa_array.Delete(ilastPos);
    Sink(pa_array, i_rootPos); 
    Return;
  End;  

  -- постановка работы в очередь ожидания завершения
  Procedure Insert_Q(pa_array in out nocopy  T_wrk_states, pr_Key in T_Worker_State)
  Is
    ilastPos Pls_integer;
  Begin
    ilastpos := Coalesce(pa_array.Last + 1, 1 );
    pa_array(ilastpos) := pr_Key;
    Swim( pa_array => pa_array, pi_from => ilastpos);
    Return;
  End;

   -- получение следующего освободившегося работника
  Function get_Next_Event( pa_evt In OUT Nocopy T_wrk_states ) return T_Worker_State
    Is
    r_ret T_Worker_State; 
  Begin    
    If Not Is_Empty(pa_evt) Then
       delMax(pa_evt, r_ret);
    End If;
    Return r_ret;
  End;
 ---------------------------------------------------
   -- подготовка состояния работников 
   -- с одновременной загрузкой их начальной работой
   procedure prepare_workers
   Is
    r_worker T_Worker_State;
   Begin  
     nstop_time := -1.0;
     fetch pcrs Bulk Collect Into a_workers;
     If a_workers.count > 0 Then
       nstop_time := 0.0;       
       For i in 1..a_workers.count
       Loop
         r_worker.inum := i;
         r_worker.current_capacity := 1;
         r_worker.next_avalible := a_workers(i).speed;
         
         Insert_Q(pa_array => a_wrkstate, pr_Key => r_worker);       
         -- nstop_time := Greatest(nstop_time, r_worker.next_avalible);         
         N := N-1;         
         Exit When N <= 0;
       End Loop;  
     End If;       
   End;  
  
 Begin -- основное тело 
   If N > 0.0 Then
     prepare_workers;
     If a_workers.count > 0
       Then        
        Loop
          r_worker := get_Next_Event(a_wrkstate);
          ncur_time := r_worker.next_avalible;
          
          If r_worker.current_capacity < a_workers(r_worker.inum).capacity
            Then
              r_worker.current_capacity := r_worker.current_capacity + 1;
              r_worker.next_avalible := r_worker.next_avalible + a_workers(r_worker.inum).speed;
              N := N-1;
          Else
             r_worker.current_capacity := 0; 
             r_worker.next_avalible := r_worker.next_avalible + a_workers(r_worker.inum).delay;              
          End If;     
          -- поставить обратно в очередь ожидания выполнения работ
          Insert_Q(pa_array => a_wrkstate, pr_key => r_worker );
          Exit When N <=0;
        End Loop;
        -- цикл съема результата 
        While Not Is_Empty(a_wrkstate)
        Loop
          r_worker := get_Next_Event(a_wrkstate);
          If r_worker.current_capacity > 0
            Then
            nstop_time := Greatest(nstop_time, r_worker.next_avalible);         
          End If;
        End Loop;    
     End If;    
   End If;
   Return nstop_time;
 End;  

end sqlru_workers;
/

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

текущее время в итоге не пригодилось, но забылось удалиться.
Так что, не без дефектов, но он и набросок.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122299
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
не причисывал
Прикольно! Фактически прямолинейная реализация жадного алгоритма по раздаче коробок.
У меня несколько иной подход. Подождём может еще будут предложены решения. Потом чуть детальнее прокоментирую.
booby
ну, и я, более-менее, без долгого тестирования верю, в то, что здесь написано
Вера в себя это замечательно.
Код экстремально избычтоный и я не уверен что легче - переписать его сократив раз в десять или искать ошибку в том, что есть,
но на данных ниже для 31 коробки выдаёт 175 вместо 152 (у меня и Станислава результат одинаков).
Код: plsql
1.
2.
3.
4.
5.
6.
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
)
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122307
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ок, я посмотрю позже, где ошибка.
Но 152 тоже не похоже на правильный ответ.
его как-то надо лучше обосновать, чем указанием на одинаковость, возможно одинаково неправильных решений.


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

исполнители
id speed capacity delay1 10 12 22 30 12 53 20 4 54 35 100 100


трейс работы исполнителей во времени
время Коробка работник всего обработано готовность коробки прим0 1 1 1 10 0 2 3 1 20 0 3 2 1 30 0 4 4 1 35 10 5 1 2 20 20 6 1 3 30 20 7 3 2 40 30 8 1 4 40 30 9 2 2 60 35 10 4 2 70 40 11 1 5 50 40 12 3 3 60 50 13 1 6 60 60 14 1 7 70 60 15 3 4 80 next stop60 16 2 3 90 70 17 1 8 80 70 18 4 3 105 80 19 1 9 90 80 20 3 4 85 отдых85 21 3 5 105 90 22 1 10 100 90 23 2 4 120 100 24 1 11 110 105 25 3 6 125 105 26 4 4 140 110 27 1 12 120 next stop120 28 1 12 122 отдых122 29 1 13 132 125 30 3 7 145 132 31 1 14 142

руками вот так 145 получается
я посмотрю, где у меня ошибка.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122333
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby,

проверил, правильный ответ именно 175
при One off получится 150
чтобы получить 152 надо две ошибки совершить 1) one off 2) присобачить где-то к нему время отдыха 1го работника,
или ошибиться каким-то иным способом.

в предыдущих ручных выкладках я сам ошибся на строках отдыха, засчитав им живые коробки.
правильный финиш, начиная с 20й коробки, такой:
время коробка работник обработал всего готовность прим80 19 1 9 90 последняя правильная в пред. посте80 0 3 4 85 отдых85 20 3 5 105 90 21 1 10 100 90 22 2 4 120 100 23 1 11 110 105 24 3 6 125 105 25 4 4 140 110 26 1 12 120 next stop120 0 1 12 122 отдых120 27 2 5 150 122 28 1 13 132 125 29 3 7 145 132 30 1 14 142 140 31 4 5 175

PS
надеюсь, у нас не найдется повода обсуждать экстремальную избыточность кода,
чтобы не превращать топик совсем уж в посмешище.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122336
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby,

впрочем, 152 действительно получается, если на последней коробке сменить стратегию, и назначать следующую коробку не первому
освободившемуся, а перейти к ожиданию освобождения работника с минимальным суммарным временем
следующая готовность + обработка.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122354
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby,

если у меня
забрать min, то выдаст как работали
забрать where, то пошаговая раздача работы (к-во итераций равно к-ву коробок)

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122392
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

У меня несколько иной подход.

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

1) считаем минимальное время когда работает один человек, получаем стартовое
2) Параллельная обработка творит чудеса (c dbms_photoshop ).
далее время уменьшием (пополам, золотое сечение, ...)
3) считаем скоко за ето время коробок откроют при максимальном паралелизме (работают все)
4) далее если полученное к-во коробок меньше то - увеличиваем время, если больше уменьшаем, если равно то п6
5) п3
6) накручиваем точность
.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122511
Никанор Кузьмич
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я пока не вникал в предложенные выше решения. Писать код пока не пробовал, только думал над решением. Надумал вот что:
Подход 1: раскидывание коробок по сотрудникам пачками. В этом случае у каждого работника есть две характеристики: скорость работы S (заданная в условии) и средняя скорость работы с учетом перерывов, равная C * S / (C + D * S) (если я в формулах не напутал). Соответственно, если коробок мало, и все сотрудники успеют обработать коробки до ухода на перерыв, надо учитывать пиковую скорость работы, а если коробок много - то учитывать среднюю. Есть еще промежуточный вариант, когда часть работников успела сделать перерыв, а часть - нет. Дальше мне показалось, что этот путь тупиковый, и я перешел к
подходу 2: обрабатывать коробки по одной. Берем коробку, смотрим свободных сотрудников. Выбираем самого быстрого (с максимальной скоростью). Даем коробку ему. Берем следующую коробку. Выбираем самого быстрого из оставшихся, и т. д. Если сотрудников больше нет - ждем. Отдельно нужно вести учет времени - когда кто-то освободился, выдавать коробку ему. Чем-то это начинает напоминать недавно проскакивавшую задачу про поезда, где надо было перегружать грузы из одного в другой, и на входе были вагоны с номерами и грузоподьемностью, а на выходе - из какого вагона в какой сколько груза грузить. Только тут у нас поезд 1 - это коробки, а поезд 2 - это минуты. "Грузоподъемность" имеет разные единицы измерения, поэтому нужен "коэффициент пересчета". И это - табличка с работниками.
Это пока все, что надумал.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122535
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Никанор Кузьмич,

Осталась самая малость, перевести на буржуинский select from where

часть перевода (speed, capacity, delay) Александр уже засветил

ps
в задачке с поездами, если камазы не разгружать, а выставить навпротив соответствующих вагонов, то придем к "красивому" решению
.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122568
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
к-во итераций равно к-ву коробок
В моём случае число итераций чуть меньше потому что на каждой итерации я смотрю ближайший момент когда кто-то свободится (shift)
и считаю сколько коробок обработано (finished - может быть 1 или несколько).
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
SQL> with w(id, speed, capacity, delay) as (
  2  select 1 id, 10 speed, 12 capacity, 2 delay from dual
  3  union all select 2, 30, 10, 5 from dual
  4  union all select 3, 20, 4, 5 from dual
  5  union all select 4, 35, 100, 100 from dual
  6  )
  7  , r (time_next, n, time, lvl) as
  8  (select 0, 31, 0, 1 from dual
  9   union all
 10   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
 11   from r
 12  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
 13                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
 14                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
 15  where n > 0)
 16  cycle lvl set cycle to 1 default 0
 17  select max(time) result from r
 18  /

    RESULT
----------
       152

Можно было бы обойтись по сути двумя столбцами time и n вместо четырёх.
lvl фигурирует в решении просто для нумерации проходов.
"cycle lvl set cycle to 1 default 0" указано потому что Ораклу всегда мерещаться циклы при наличии cross apply.

Если сделать "select * from r" то можно увидеть в какие моменты времени и сколько коробок убвало.

Очевидно, что на каждом проходе выполняется перебор всех работников.
Этого можно избежать если выстраивать их в очередь по близости освобождения, но тут уже потребуется PL/SQL.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122576
dba123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если правильно понял задачу
Дано:
1,2,3
3,10,3
2,4,3
Условия:
1) два человека не могут открывать одну коробку (разбирать содержимое)
2) минимальный шаг времени 1 минута
3) коробки идут без перерывов

Тогда получаем, что за 4 мин - втроем сделают 5 коробок и за 5 мин тоже только 5 коробок, а за 6 минут уже 8.
Т.е. 7 коробок все равно сделают за 6 мин и быстрей никак (плюс одна бонусная)
box Job1(mi) Job2(mi) Job3(mi)1 1 3 22 2 =+1 6 =+3 4 =+23 6 =+4 9 =+3 6 =+24 7 =+1 12 =+3 8 =+25 11 =+4 15 =+3 13 =+56 12 =+1 18 =+3 15 =+27 16 =+4 21 =+3 17 =+28 17 =+1 24 =+3 19 =+29 21 =+4 27 =+3 24 =+510 22 =+1 30 =+3 26 =+2

1.тогда на вопрос сколько коробок распакуют за Х минут 3 человека
mi1 2345678910111213141516171819202122box1345589101111121415151718202122222425
2. какое минимальное время потребуется троим рабочим, чтобы разобрать Х коробок
box1 2345678910111213141516171819202122232425mi122346667891112121315151617171819212122
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122586
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dba123
минимальный шаг времени 1 минута
В частном случае когда speed & delay are integers.
dba123
сколько коробок распакуют за Х минут 3 человека
Для этого можно составить относительно простое выражение.
dba123
какое минимальное время потребуется троим рабочим, чтобы разобрать Х коробок
Интересный вопрос выводима ли эта функция аналитически, но я сомневаюсь.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122744
Никанор Кузьмич
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby
ок, я посмотрю позже, где ошибка.
Но 152 тоже не похоже на правильный ответ.
его как-то надо лучше обосновать, чем указанием на одинаковость, возможно одинаково неправильных решений.


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

исполнители
id speed capacity delay1 10 12 22 30 12 53 20 4 54 35 100 100
Ошибка как минимум в исходных данных:
dbms_photoshop
Код: plsql
1.
2.
3.
4.
5.
6.
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
)



Stax
Осталась самая малость, перевести на буржуинский select from where
Держите:
Код: 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.
with w as (
        select  10 s, 12 c, 2 d, 'w1' id from dual union all
        select  30 s, 10 c, 5 d, 'w2' id from dual union all
        select  20 s,  4 c, 5 d, 'w3' id from dual union all
        select  35 s, 100 c, 100 d, 'w4' id from dual),
     b as (
        select rownum n from dual connect by level < 200)
select * 
  from (select tt, 
               max(w1) over (order by tt rows between unbounded preceding and current row) m1, 
               max(w2) over (order by tt rows between unbounded preceding and current row) m2, 
               max(w3) over (order by tt rows between unbounded preceding and current row) m3, 
               max(w4) over (order by tt rows between unbounded preceding and current row) m4
          from (select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
                  from (select s, c, d, id, n, s + case when mod(n, c) = 0 then d else 0 end total_time_with_pause
                          from b, w))
         pivot (max(n) for id in ('w1' w1, 'w2' w2, 'w3' w3, 'w4' w4)))
where m1 + m2 + m3 + m4 <= :N;

        TT         M1         M2         M3         M4
---------- ---------- ---------- ---------- ----------
 ...
       145         14          4          7          4
       150         14          5          7          4
       152         15          5          7          4

26 rows selected
SQL> 

Ну, вроде 152 - правильный ответ. Работники обработают к этому моменту 15, 5, 7 и 4 коробки соответственно.

В общем, я еще немного подумал и представил задачу как будто мы сгружаем груз с одного "поезда" на несколько (каждый работник - это отдельный "поезд" со своими характеристиками).
Сначала все выглядит очень просто: для каждого работника мы выписываем номер коробки - и время, которое пройдет после ее обработки.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
with w as (
        select  5 s, 20 c,  5 d, 'w1' id from dual union all
        select 20 s,  5 c,  5 d, 'w2' id from dual union all
        select  5 s,  5 c, 20 d, 'w3' id from dual union all
        select  8 s, 15 c, 10 d, 'w4' id from dual),
     b as (
        select rownum n from dual connect by level < 200)
select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
  from (select s, c, d, id, n, s + case when mod(n, c) = 0 then d else 0 end total_time_with_pause
          from b, w)

Дальше нужно просто отсортировать по времени и выбрать для каждого работника максимальное значение столбца n - это будет количество коробок, которое он успел обработать к этому моменту. Соответственно, суммируем эти коробки - и вот он результат! К сожалению, на этом месте муза покинула меня, и суммирование выглядит как-то избыточно. Никак не придумаю, как записать короче...
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122775
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Никанор Кузьмич
booby

where
Держите:

к-во работников, неизвестно

время, натуральное число, напр треть минуты (1/3)


Stax

Дальше нужно просто отсортировать по времени и выбрать для каждого работника максимальное значение столбца n - это будет количество коробок, которое он успел обработать к этому моменту. Соответственно, суммируем эти коробки - и вот он результат! К сожалению, на этом месте муза покинула меня, и суммирование выглядит как-то избыточно. Никак не придумаю, как записать короче...


какую задачу решаете?

ps
Код: plsql
1.
За какое минимальное время работники смогут обработать N коробок (задаётся биндом или константой, не суть)?



.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122802
Никанор Кузьмич
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
к-во работников, неизвестно
Да, в курсе этой проблемы, не придумал пока решение.

Stax
время, натуральное число, напр треть минуты (1/3)
Это имеет какое-то отношение к моему решению?

Stax
какую задачу решаете?
Поставленную в топике и решаю. Ну ок, можно в моем решении таблицу завернуть в подзапрос и выбрать max(tt), это будет окончательный ответ в одну строку.


ну или на 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.
declare
  cursor workers is 
    with w as ( -- исходные данные
        select  10 s, 12 c, 2 d, 1 id from dual union all
        select  30 s, 10 c, 5 d, 2 id from dual union all
        select  20 s,  4 c, 5 d, 3 id from dual union all
        select  35 s, 100 c, 100 d, 4 id from dual),
     b as (
        select rownum n from dual connect by level < :N)
    -- раздаем коробки работникам
    select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
      from (select s, c, d, id, n, s + case when mod(n, c) = 0 then d else 0 end total_time_with_pause
              from b, w)
     order by tt;
  -- ассоциативный массив "ИД работника - обработано коробок"
  type nt is table of number index by pls_integer;
  wb nt;
  total_boxes number := :N;
  function processed_boxes(p_wb nt) return number is -- суммируем обработанные коробки
    i number;
    s number := 0;
  begin
    i := p_wb.first;
    loop
      s := s + p_wb(i);
      i := p_wb.next(i);
      exit when i is null;
    end loop;
    return s;
  end;
begin  -- начало тут
  for i in workers loop
    wb(i.id) := i.n;
    if processed_boxes(wb) = total_boxes then
       dbms_output.put_line('Time passed: ' || i.tt);
       exit;
    end if;
  end loop;
end;


Ответ для 31 коробки - 152, как и требовалось.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122814
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Никанор Кузьмич

booby

where
Держите:

Код: 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.
QL> ed
Wrote file afiedt.buf

  1  with w(id, s, c, d) as (
  2              select 'w1', 0.5, 2, 500 from dual
  3    union all select 'w2', 1, 40, 5 from dual
  4    union all select 'w3', 2, 2, 1 from dual
  5    union all select 'w4', 1000, 1, 1000 from dual
  6    ),
  7       b as (
  8          select rownum n from dual connect by level < 200)
  9  select *
 10    from (select tt,
 11                 max(w1) over (order by tt rows between unbounded preceding and current row) m1,
 12                 max(w2) over (order by tt rows between unbounded preceding and current row) m2,
 13                 max(w3) over (order by tt rows between unbounded preceding and current row) m3,
 14                 max(w4) over (order by tt rows between unbounded preceding and current row) m4
 15            from (select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
 16                    from (select s, c, d, id, n, s + case when mod(n, c) = 0 then d else 0 end total_time_with_pause
 17                            from b, w))
 18           pivot (max(n) for id in ('w1' w1, 'w2' w2, 'w3' w3, 'w4' w4)))
 19* where m1 + m2 + m3 + m4 <= &N
SQL> /
Enter value for n: 11
old  19: where m1 + m2 + m3 + m4 <= &N
new  19: where m1 + m2 + m3 + m4 <= 11

no rows selected



.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40122839
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Никанор Кузьмич

как и требовалось.


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
1  declare
  2    cursor workers is
  3  with w(id, s, c, d) as (
  4              select 1, 0.5, 2, 500 from dual
  5    union all select 2, 1, 40, 5 from dual
  6    union all select 3, 2, 2, 1 from dual
  7    union all select 4, 1, 1, 1000 from dual
  8    ),
  9       b as (
 10          select rownum n from dual connect by level < 11)

SQL> /
Time passed: 7

PL/SQL procedure successfully completed.


?
1-2
2-6
3-2
4-1

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123054
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Никанор Кузьмич
Да, в курсе этой проблемы, не придумал пока решение.
Твоё решение элементарно допиливается для общего случая.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
with w(id, s, c, d) as (
select rownum, speed, capacity, delay from w0 w)
, b as (select rownum n from dual connect by level <= :n)
select tt
  from (select t.*, row_number() over (order by tt) k
          from (select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
                  from (select s, c, d, id, n, s + case when mod(n, c) = 1 and n > 1 then d else 0 end total_time_with_pause
                          from b, w)) t)
 where k = :n;

Прелесть в том, что работать будет на любой версии начиная с 8i.
Но минус в том, что как и в решении Stax в общей сумме будет обработано K * W строк (K - коробки, W - работники).
То есть, не учитывается, что много работников могут заканчивать обработку в один и тот же момент времени.

PS. "rows between unbounded preceding and current row" - это окно по умолчанию при наличии сортировки.
"order by tt rows between unbounded preceding and current row" эквивалентно "order by tt".
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123062
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Производительность для вырожденного случая когда все заканчивают одновременно.

1111 коробок, 100 работников
Код: 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.
SQL> drop table w0;

Table dropped.

SQL>
SQL> var n number;
SQL> exec :n := 1111;

PL/SQL procedure successfully completed.

SQL>
SQL> create table w0(speed, capacity, delay) as
  2  select 2,2,2 from dual connect by level <= 100;

Table created.

SQL>
SQL> set timing on
SQL>
SQL> with w(id, speed, capacity, delay) as (
  2  select rownum, w.* from w0 w)
  3  , r (time_next, n, time, lvl) as
  4  (select 0, :n, 0, 1 from dual
  5   union all
  6   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  7   from r
  8  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  9                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
 10                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
 11  where n > 0)
 12  cycle lvl set cycle to 1 default 0
 13  select max(time) result from r
 14  /

    RESULT
----------
        34

Elapsed: 00:00:00.06
SQL>
SQL> with w(id, s, c, d) as (
  2  select rownum, speed, capacity, delay from w0 w)
  3  , b as (select rownum n from dual connect by level <= :n)
  4  select tt
  5    from (select t.*, row_number() over (order by tt) k
  6            from (select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
  7                    from (select s, c, d, id, n, s + case when mod(n, c) = 1 and n > 1 then d else 0 end total_time_with_pause
  8                            from b, w)) t)
  9   where k = :n;

        TT
----------
        34

Elapsed: 00:00:00.15
SQL>
SQL> with w(id, speed, capacity, delay) as (
  2  select rownum, w.* from w0 w)
  3  , r (id,speed, capacity, delay,k2,l,s) as (
  4   select  w.*
  5    ,lag(0,1,1) over (order by speed)
  6    ,1
  7    ,speed
  8   from w
  9  union all
 10   select  id,speed, capacity, delay
 11    ,k2+lag(0,1,1) over (order by speed*(k2+1)+floor(k2/capacity)*delay,id)
 12    ,l+1
 13    ,speed*(k2+1)+floor(k2/capacity)*delay
 14   from r where l<:n
 15  )
 16  select min(s) m from r where l=:n
 17  --order by l,id
 18  /

         M
----------
        34

Elapsed: 00:01:04.95
SQL>
SQL> set timing off


2222 коробок, 1000 работников
Код: 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.
SQL> drop table w0;

Table dropped.

SQL>
SQL> var n number;
SQL> exec :n := 2222;

PL/SQL procedure successfully completed.

SQL>
SQL> create table w0(speed, capacity, delay) as
  2  select 2,2,.1 from dual connect by level <= 1000;

Table created.

SQL>
SQL> set timing on
SQL>
SQL> with w(id, speed, capacity, delay) as (
  2  select rownum, w.* from w0 w)
  3  , r (time_next, n, time, lvl) as
  4  (select 0, :n, 0, 1 from dual
  5   union all
  6   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  7   from r
  8  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  9                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
 10                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
 11  where n > 0)
 12  cycle lvl set cycle to 1 default 0
 13  select max(time) result from r
 14  /

    RESULT
----------
       6.1

Elapsed: 00:00:00.01
SQL>
SQL> with w(id, s, c, d) as (
  2  select rownum, speed, capacity, delay from w0 w)
  3  , b as (select rownum n from dual connect by level <= :n)
  4  select tt
  5    from (select t.*, row_number() over (order by tt) k
  6            from (select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
  7                    from (select s, c, d, id, n, s + case when mod(n, c) = 1 and n > 1 then d else 0 end total_time_with_pause
  8                            from b, w)) t)
  9   where k = :n;

        TT
----------
       6.1

Elapsed: 00:00:03.01
SQL>
SQL> set timing off

...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123104
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby

я посмотрю, где у меня ошибка.


Вот так в итоге.
С небольшими потерями в производительности, исправленными стоимостями и поведением...
В любом случае, желаемые по порядку ~ N*Log(M) сохранены.

Код: 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.
create or replace package sqlru_workers authid definer
is

-- booby 26.12.2021

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

 --------------------
 TYPE T_WRK_CURSOR IS REF CURSOR RETURN T_Worker_Spc;
 -----
/*
  вернет отрицательный результат, если для заказанного положительного объема pN
  не найдется работников в курсоре pcrs вообще
  пример вызова
select sqlru_workers.get_min_wrk_time(pcrs => CURSOR(
                                                      select speed, capacity, delay
                                                      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
                                                       )
                         , pN => 10        -- число коробок
                         , do_output => 0
                          )
from dual
*/
 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;

end sqlru_workers;
/
create or replace package body sqlru_workers 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 коробок
                              , total_processed Number -- всего назначено коробок
                              );
 TYPE T_WORKERS_PLDICT is table of T_Worker_State index by pls_integer;
 ------------------------------------------------------------------
 Type T_wrk_queue is Table of pls_integer 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; -- работнички
   a_wrkstate T_wrk_queue;     -- очередь доступности
   i_worker Pls_integer; 

   isDbms Boolean := (Coalesce(do_output,0) != 0);
  ------------------------------------------------------------------------------
  -- процедуры работы с очередью работников
  -------------------------------------------
  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) 
           + floor((a_workers(pj).total_processed-1)/a_workers(pj).capacity)*a_workers(pj).delay
           ; 
  End;    
  --
  Function get_total_cost(pj in pls_integer) return Number
  Is
    pp Number;
  Begin
    pp := a_workers(pj).total_processed;
    Return a_workers(pj).speed * (pp + 1) 
           + floor(pp/a_workers(pj).capacity)*a_workers(pj).delay          
           ; 
  End;    
  -- сравнение элементов
  Function Less(pi_parent in pls_integer  --in T_Worker_State
              , pi_child in pls_integer --in T_Worker_State
               ) Return Boolean
  Is
  child_cost Number;
  parent_cost Number;  
  Begin
    child_cost := get_total_cost(pi_child);
    parent_cost := get_total_cost(pi_parent);
    
   -- Return child_cost < parent_cost;    
     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;
    i_parent Pls_integer;
    r_temp Pls_integer; 
  Begin
    i_flow := pi_from;
    i_parent :=  get_parent_idx(i_flow);
    r_temp := pa_array(pi_from);

    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;
      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;
    i_child Pls_integer;
    i_lastPos Pls_integer;

    r_temp Pls_integer; 
  Begin
    i_lastPos := pa_array.COUNT;
    i_flow := pi_from;
    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);
        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;

        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 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);
    Sink(pa_array, i_rootPos);
    Return;
  End;

  -- постановка работы в очередь ожидания завершения
  Procedure Insert_Q(pa_array in out nocopy  T_wrk_queue, pr_Key in pls_integer) --T_Worker_State)
  Is
    ilastPos Pls_integer;
  Begin
    ilastpos := Coalesce(pa_array.Last + 1, 1 );
    pa_array(ilastpos) := pr_Key;
    Swim( pa_array => pa_array, pi_from => ilastpos);
    Return;
  End;
  -- подсматриваем в текущего первого
  Function Peek_Next_Worker( pa_wrk_state In T_wrk_queue ) return Pls_integer --T_Worker_State
  Is
    r_ret Pls_integer; --T_Worker_State;
  Begin    
    r_ret :=  pa_wrk_state(1);
    Return r_ret;
  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;
       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).total_processed := 0.0;
       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;
   End;

 Begin -- основное тело
   If N > 0.0 Then
     prepare_workers;
     If a_workers.count > 0
       Then
        LOOP
          ---------------------------------------------
          i_worker := Peek_Next_Worker(a_wrkstate);
          a_workers(i_worker).total_processed := a_workers(i_worker).total_processed + 1;
          ----
          a_wrkstate(1) := a_wrkstate(a_wrkstate.Count);
          a_wrkstate(a_wrkstate.Count) := i_worker;
          Sink(pa_array => a_wrkstate, pi_from => 1);          ----
          N := N - 1;
          ---------------------------------------------
         If isDbms Then
           dbms_output.put_line( 
                              'i_worker='||i_worker
                              ||' N='||(pN - N)
                              ||' total_processed='||a_workers(i_worker).total_processed
                              ||' current_cost='||get_total_cost(i_worker) 
                              ||' result_cost='||get_result_cost(i_worker)                               
                              );          
         End If;
         Exit When N <=0;
        END LOOP;
        -- цикл съема результата
        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
                              ||' result_cost='||get_result_cost(i)
                              );            
          End If;  
          nstop_time := Greatest(nstop_time, get_result_cost(i));
        End Loop;
     End If;
   End If;
   Return nstop_time;
 End;

end sqlru_workers;
/
alter package sqlru_workers compile package  PLSQL_DEBUG = False  plsql_code_type =native plsql_optimize_level = 3;



варианты вызова

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
select sqlru_workers.get_min_wrk_time(pcrs => CURSOR(
                                                       select speed, capacity, delay From w0 
                                                       )
                         , pN => 2222        -- число коробок
                         , do_output => 0 -- сформировать dbms_output
                          )
from dual
;
declare 
  n Number;
  crs  sqlru_workers.T_WRK_CURSOR;
begin
  open crs for select speed, capacity, delay From w0; 
  n := sqlru_workers.get_min_wrk_time(pcrs => crs
                         , pN => 2222        -- число коробок
                         , do_output => 0 -- сформировать dbms_output
                          );
  dbms_output.put_line('n='||n);
end;
/ 

...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123237
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
сорри за тскл, на моей машине щаз нету оракла. Но тут только нет дюала, и табличную переменную (временную таблицу) ввел, т.к. где-то тскл глючит, пришлось материализовать
результаты совпадают
Код: sql
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.
declare
    @count    int = 31;
declare
    @result TABLE(t numeric(10,1), cnt int, prev int, curr int)
;

with w(speed, capacity, delay) as
/*(
select 1  ,   2,   3 union all
select 3  ,  10,   3 union all
select 2  ,   4,   3 union all
select 3.5, 100, 100
),*/
/*(
select 1, 2, 3  union all 
select 3, 10, 3 union all 
select 2, 4, 3 union all
select 3.5, 100, 100
)*/
(
select 10, 12, 2 union all 
select 30, 10, 5 union all 
select 20, 4,  5 union all 
select 35, 100, 100 
)
,
tree(t,speed, capacity, delay, lv)
as(
select cast(0 as numeric(10,1)) t,
       speed, capacity, delay, 0 lv
  from w
union all
select cast(
       t + speed + case when t > 0 and lv % capacity = 0 then delay else 0 end
       as numeric(10,1)) t,
       speed, capacity, delay, 1+lv
  from tree
 where lv < @count
)
insert
  into @result
select t,
       count(*) cnt,
       sum(count(*)) over(order by t)-count(*) prev,
       sum(count(*)) over(order by t) curr
  from tree
 where t > 0
 group by t

 select *
   from @result
 where @count = curr or @count > prev and @count < curr

...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123280
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreymx
тскл глючит, пришлось материализовать
В чём состоит глючность?
По сути твой алгоритм точно такой же как у Кузьмича и если обойтись без лишних выкрутасов, то всё должно работать.
Код: sql
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.
declare
    @count    int = 31;

with w(speed, capacity, delay) as
(
select 10, 12, 2 union all 
select 30, 10, 5 union all 
select 20, 4,  5 union all 
select 35, 100, 100 
)
,
tree(t,speed, capacity, delay, lv)
as(
select cast(0 as numeric(10,1)) t,
       speed, capacity, delay, 0 lv
  from w
union all
select cast(
       t + speed + case when t > 0 and lv % capacity = 0 then delay else 0 end
       as numeric(10,1)) t,
       speed, capacity, delay, 1+lv
  from tree
 where lv < @count
)
select *
  from (select row_number() over(order by t) k, * from tree where lv > 0) r
 where k = @count
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123282
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby,

Если делать на PL/SQL, то я бы использовал ассоциативный массив проиндексированный по времени освобождения рабочих.
Работает с точностью до 0.001, но у прибора измеряющего время есть дискретность,
так что невозможность проиндексировать по number по сути не является проблемой для этой задачи.
Код: 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.
create or replace function f_box(p in int) return number as
  cursor c is select speed * 1e3 speed, capacity, delay * 1e3 delay, 0 finished, rownum i from w;
  type tp1 is table of c%rowtype index by pls_integer;
  type tp2 is table of tp1 index by pls_integer;
  arr    tp2;
  idx    int;
  k      int := p;
  procedure process(q in out tp2, i int) is
    time int;
    w    c%rowtype;
  begin
    w := q(q.first) (i);
    time := q.first + w.speed + case when mod(w.finished, w.capacity) = 0 and w.finished > 0 then w.delay else 0 end;
    q(time)(i).speed := w.speed;
    q(time)(i).capacity := w.capacity;
    q(time)(i).delay := w.delay;
    q(time)(i).finished := w.finished + 1;
  end;
begin
  for r in c loop
    arr(0)(r.i).speed := r.speed;
    arr(0)(r.i).capacity := r.capacity;
    arr(0)(r.i).delay := r.delay;
    arr(0)(r.i).finished := 0;
  end loop;

  while true loop
    idx := arr(arr.first).first;
    while (idx is not null) loop
      if arr.first > 0 then
        k := k - 1;
      end if;
      process(arr, idx);
      idx := arr(arr.first).next(idx);
    end loop;
    if k <= 0 then
      return arr.first / 1e3;
    else
      arr.delete(arr.first);
    end if;
  end loop;
end;
/

Производительность SQL варианта зависит от специфики данных, тогда как скорость PL/SQL решений зависит только от числа рабочих и величины k.
Код: 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.
SQL> set timing off
SQL> drop table w;

Table dropped.

SQL> create table w(speed, capacity, delay) as
  2  select 2,2,.1 from dual connect by level <= 1e4;

Table created.

SQL> set timing on
SQL>
SQL> with r (time_next, n, time, lvl) as
  2  (select 0, 654321, 0, 1 from dual
  3   union all
  4   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  5   from r
  6  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  7                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
  8                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
  9  where n > 0)
 10  cycle lvl set cycle to 1 default 0
 11  select max(time) result from r
 12  /

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

Elapsed: 00:00:00.48
SQL> select f_box(654321) result from dual;

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

Elapsed: 00:00:02.10
SQL> select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 654321) result from dual;

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

Elapsed: 00:00:21.64
SQL>
SQL> ----------
SQL>
SQL> set timing off
SQL> drop table w;

Table dropped.

SQL> create table w(speed, capacity, delay) as
  2  select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
  3  from dual
  4  connect by level <= 1e4;

Table created.

SQL> set timing on
SQL>
SQL> with r (time_next, n, time, lvl) as
  2  (select 0, 654321, 0, 1 from dual
  3   union all
  4   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  5   from r
  6  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  7                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
  8                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
  9  where n > 0)
 10  cycle lvl set cycle to 1 default 0
 11  select max(time) result from r
 12  /

    RESULT
----------
     163.4

Elapsed: 00:00:15.20
SQL> select f_box(654321) result from dual;

    RESULT
----------
     163.4

Elapsed: 00:00:02.29
SQL> select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 654321) result from dual;

    RESULT
----------
     163.4

Elapsed: 00:00:22.79
SQL>
SQL> ----------
SQL>
SQL> set timing off
SQL> drop table w;

Table dropped.

SQL> create table w(speed, capacity, delay) as
  2  select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 10 + 1)), trunc(dbms_random.value(1, 100 + 1))
  3  from dual
  4  connect by level <= 1e4;

Table created.

SQL> set timing on
SQL>
SQL> with r (time_next, n, time, lvl) as
  2  (select 0, 654321, 0, 1 from dual
  3   union all
  4   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  5   from r
  6  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  7                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
  8                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
  9  where n > 0)
 10  cycle lvl set cycle to 1 default 0
 11  select max(time) result from r
 12  /

    RESULT
----------
     710.4

Elapsed: 00:00:52.40
SQL> select f_box(654321) result from dual;

    RESULT
----------
     710.4

Elapsed: 00:00:02.49
SQL> select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 654321) result from dual;

    RESULT
----------
     710.4

Elapsed: 00:00:22.51
SQL>


src
Код: plaintext
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.
set timing off
drop table w;
create table w(speed, capacity, delay) as
select 2,2,.1 from dual connect by level <= 1e4;
set timing on

with r (time_next, n, time, lvl) as
(select 0, 654321, 0, 1 from dual
 union all
 select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
 from r
cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
                    sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
                   from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
where n > 0)
cycle lvl set cycle to 1 default 0
select max(time) result from r
/
select f_box(654321) result from dual;
select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 654321) result from dual;

----------

set timing off
drop table w;
create table w(speed, capacity, delay) as
select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
from dual
connect by level <= 1e4;
set timing on

with r (time_next, n, time, lvl) as
(select 0, 654321, 0, 1 from dual
 union all
 select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
 from r
cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
                    sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
                   from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
where n > 0)
cycle lvl set cycle to 1 default 0
select max(time) result from r
/
select f_box(654321) result from dual;
select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 654321) result from dual;

----------

set timing off
drop table w;
create table w(speed, capacity, delay) as
select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 10 + 1)), trunc(dbms_random.value(1, 100 + 1))
from dual
connect by level <= 1e4;
set timing on

with r (time_next, n, time, lvl) as
(select 0, 654321, 0, 1 from dual
 union all
 select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
 from r
cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
                    sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
                   from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
where n > 0)
cycle lvl set cycle to 1 default 0
select max(time) result from r
/
select f_box(654321) result from dual;
select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 654321) result from dual;
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123300
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop
andreymx
тскл глючит, пришлось материализовать
В чём состоит глючность?
По сути твой алгоритм точно такой же как у Кузьмича и если обойтись без лишних выкрутасов, то всё должно работать.
Код: sql
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.
declare
    @count    int = 31;

with w(speed, capacity, delay) as
(
select 10, 12, 2 union all 
select 30, 10, 5 union all 
select 20, 4,  5 union all 
select 35, 100, 100 
)
,
tree(t,speed, capacity, delay, lv)
as(
select cast(0 as numeric(10,1)) t,
       speed, capacity, delay, 0 lv
  from w
union all
select cast(
       t + speed + case when t > 0 and lv % capacity = 0 then delay else 0 end
       as numeric(10,1)) t,
       speed, capacity, delay, 1+lv
  from tree
 where lv < @count
)
select *
  from (select row_number() over(order by t) k, * from tree where lv > 0) r
 where k = @count

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


Производительность SQL варианта зависит от специфики данных, тогда как скорость PL/SQL решений зависит только от числа рабочих и величины k.
Код: 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.
SQL> set timing off
SQL> drop table w;

Table dropped.

SQL> create table w(speed, capacity, delay) as
  2  select 2,2,.1 from dual connect by level <= 1e4;

Table created.

SQL> set timing on
SQL>
SQL> with r (time_next, n, time, lvl) as
  2  (select 0, 654321, 0, 1 from dual
  3   union all
  4   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  5   from r
  6  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  7                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
  8                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
  9  where n > 0)
 10  cycle lvl set cycle to 1 default 0
 11  select max(time) result from r
 12  /

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

Elapsed: 00:00:00.48
SQL> select f_box(654321) result from dual;

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

Elapsed: 00:00:02.10
SQL> select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 654321) result from dual;

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

Elapsed: 00:00:21.64
SQL>
SQL> ----------



По первой части у меня пока не сходится.
Правда, я честно забыл откомпилировать f_box в native.
Нужно будет сделать второй прогон.
С учетом этой оговорки, пока у меня так:
1) Elapsed: 00:00:01.54
2) Elapsed: 00:44:46.98
3) Elapsed: 00:00:07.84

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

Там не надо ничего компилировать в native.
Копи-пастишь всё как есть с точностью до символа и запускаешь.

Отдаю должное выдержке если удалось высидеть 45 минут пока выполнится.
Но оно того не стоило. Что-то в твоей консерватории не то.

Для идентичной воспроизводимости можно добавить в начале скрипта dbms_random.seed
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123483
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop


Там не надо ничего компилировать

у меня компиляция в сеансе по умолчанию настроена так:
Код: plsql
1.
PLSQL_DEBUG = true plsql_code_type =interpreted plsql_optimize_level = 2


В режиме PLSQL_DEBUG = true с временеим работы f_box происходит какая-то нелинейная катастрофа по мере роста числа работничков.
у sqlru_workers.get_min_wrk_time такой нелинейной зависимости нет.
причины этого (нелинейности времени работы 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.
SQL> alter package sqlru_workers compile specification PLSQL_DEBUG = true plsql_code_type =interpreted plsql_optimize_level = 2;

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

SQL> alter package sqlru_workers compile body PLSQL_DEBUG = true plsql_code_type =interpreted plsql_optimize_level = 2;

Тело пакета изменено.

SQL> alter function f_box compile PLSQL_DEBUG = true plsql_code_type =interpreted plsql_optimize_level = 2;

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

SQL> 
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, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
  3  select trunc(dbms_random.value(1, 150 + 1)) , trunc(dbms_random.value(1, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
  4  from dual
  5  connect by level <= 300;

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

SQL> 
SQL> set timing on
SQL> with r (time_next, n, time, lvl) as
  2  (select 0, 32769 --654321
  3  , 0, 1 from dual
  4   union all
  5   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  6   from r
  7  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  8                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
  9                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
 10  where n > 0)
 11  cycle lvl set cycle to 1 default 0
 12  select max(time) result from r
 13  /

    RESULT                                                                      
----------                                                                      
      3301                                                                      

Затрач.время: 00:00:02.70
SQL> select f_box(32769 -- 654321
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
      3301                                                                      

Затрач.время: 00:00:06.98
SQL> select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 26703 --654321
  2                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
      2693                                                                      

Затрач.время: 00:00:00.53
SQL> 
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, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
  3  select trunc(dbms_random.value(1, 150 + 1)) , trunc(dbms_random.value(1, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
  4  from dual
  5  connect by level <= 3000;

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

SQL> 
SQL> set timing on
SQL> with r (time_next, n, time, lvl) as
  2  (select 0, 32769 --654321
  3  , 0, 1 from dual
  4   union all
  5   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  6   from r
  7  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  8                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
  9                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
 10  where n > 0)
 11  cycle lvl set cycle to 1 default 0
 12  select max(time) result from r
 13  /

    RESULT                                                                      
----------                                                                      
       309                                                                      

Затрач.время: 00:00:02.54
SQL> select f_box(32769 -- 654321
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
       309                                                                      

Затрач.время: 00:00:54.31
SQL> select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 32769 --654321
  2                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
       309                                                                      

Затрач.время: 00:00:00.98
SQL> 
SQL> set timing off
SQL> alter package sqlru_workers compile specification PLSQL_DEBUG = false plsql_code_type =interpreted plsql_optimize_level = 2;

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

SQL> alter package sqlru_workers compile body PLSQL_DEBUG = false plsql_code_type =interpreted plsql_optimize_level = 2;

Тело пакета изменено.

SQL> alter function f_box compile PLSQL_DEBUG = false plsql_code_type =interpreted plsql_optimize_level = 2;

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

SQL> set timing on
SQL> with r (time_next, n, time, lvl) as
  2  (select 0, 32769 --654321
  3  , 0, 1 from dual
  4   union all
  5   select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
  6   from r
  7  cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
  8                      sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
  9                     from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
 10  where n > 0)
 11  cycle lvl set cycle to 1 default 0
 12  select max(time) result from r
 13  /

    RESULT                                                                      
----------                                                                      
       309                                                                      

Затрач.время: 00:00:02.50
SQL> select f_box(32769 -- 654321
  2              ) result from dual;

    RESULT                                                                      
----------                                                                      
       309                                                                      

Затрач.время: 00:00:00.11
SQL> select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 32769 --654321
  2                                       ) result from dual;

    RESULT                                                                      
----------                                                                      
       309                                                                      

Затрач.время: 00:00:00.78
SQL> 
SQL> 
SQL> spool off



скрипт.
Код: 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.
alter package sqlru_workers compile specification PLSQL_DEBUG = true plsql_code_type =interpreted plsql_optimize_level = 2;
alter package sqlru_workers compile body PLSQL_DEBUG = true plsql_code_type =interpreted plsql_optimize_level = 2;
alter function f_box compile PLSQL_DEBUG = true plsql_code_type =interpreted plsql_optimize_level = 2;

truncate table w;
insert into w(speed, capacity, delay) 
-- select trunc(dbms_random.value(1, 100 + 1)) / 10, trunc(dbms_random.value(1, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
select trunc(dbms_random.value(1, 150 + 1)) , trunc(dbms_random.value(1, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
from dual
connect by level <= 300;

set timing on
with r (time_next, n, time, lvl) as
(select 0, 32769 --654321
, 0, 1 from dual
 union all
 select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
 from r
cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
                    sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
                   from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
where n > 0)
cycle lvl set cycle to 1 default 0
select max(time) result from r
/
select f_box(32769 -- 654321
            ) result from dual;
select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 26703 --654321
                                     ) result from dual;

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, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
select trunc(dbms_random.value(1, 150 + 1)) , trunc(dbms_random.value(1, 100 + 1)), trunc(dbms_random.value(1, 10 + 1))
from dual
connect by level <= 3000;

set timing on
with r (time_next, n, time, lvl) as
(select 0, 32769 --654321
, 0, 1 from dual
 union all
 select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
 from r
cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
                    sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
                   from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
where n > 0)
cycle lvl set cycle to 1 default 0
select max(time) result from r
/
select f_box(32769 -- 654321
            ) result from dual;
select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 32769 --654321
                                     ) result from dual;
                                     
set timing off                                     
alter package sqlru_workers compile specification PLSQL_DEBUG = false plsql_code_type =interpreted plsql_optimize_level = 2;
alter package sqlru_workers compile body PLSQL_DEBUG = false plsql_code_type =interpreted plsql_optimize_level = 2;
alter function f_box compile PLSQL_DEBUG = false plsql_code_type =interpreted plsql_optimize_level = 2;
set timing on
with r (time_next, n, time, lvl) as
(select 0, 32769 --654321
, 0, 1 from dual
 union all
 select r.time_next + a.shift, n - a.finished, r.time_next, r.lvl + 1
 from r
cross apply (select min(case when tmp < speed * capacity then (trunc(tmp / speed) + 1) * speed else speed * (capacity + 1) + delay end - tmp) shift,
                    sum(case when tmp > 0 and tmp <= speed * capacity and mod(tmp, speed) = 0 then 1 else 0 end) finished
                   from (select t.*, mod(time_next, speed * capacity + delay) tmp from w t) tt) a
where n > 0)
cycle lvl set cycle to 1 default 0
select max(time) result from r
/
select f_box(32769 -- 654321
            ) result from dual;
select sqlru_workers.get_min_wrk_time(pcrs => cursor(select speed, capacity, delay from w), pn => 32769 --654321
                                     ) result from dual;





есть еще кое-какие непонятки, но мне надо найти время подумать, прежде чем писать о них дальше.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123529
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

Твоё решение элементарно допиливается для общего случая.

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
  1  with w(id, s, c, d) as (
  2                select 1, 0.5, 2, 500 from dual
  3      union all select 2, 1, 40, 5 from dual
  4      union all select 3, 2, 2, 1 from dual
  5      union all select 4, 1, 1, 1000 from dual
  6  )
  7  , b as (select rownum n from dual connect by level <= 11)
  8  select tt
  9    from (select t.*, row_number() over (order by tt) k
 10            from (select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
 11                    from (select s, c, d, id, n, s + case when mod(n, c) = 1 and n > 1 then d else 0 end total_time_with_pause
 12                            from b, w)) t)
 13*  where k = 11
SQL> /

        TT
----------
         4


правильно 6

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123531
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

Если делать на PL/SQL


Если перевести на PL/SQL "мой" алгоритм
то там все очень просто, надо найти К раз мин для W-работников
даже без усложнения (что много работников могут заканчивать обработку в один и тот же момент времени), должно быть быстро

на современных процесорах цикл К*W "секунды"

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123537
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

То есть, не учитывается, что много работников могут заканчивать обработку в один и тот же момент времени.


не могу понять, как в Вашем варианте вывести, кто сколько коробок обработал

.....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123587
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
правильно 6
Good catch. Я недоправил case expression Кузьмича. Так должно быть ок.
Код: plsql
1.
case when mod(n - 1, c) = 0 and n > 1 then d else 0 end


Stax
Если перевести на PL/SQL "мой" алгоритм
то там все очень просто, надо найти К раз мин для W-работников
даже без усложнения (что много работников могут заканчивать обработку в один и тот же момент времени), должно быть быстро

на современных процесорах цикл К*W "секунды"
Выше были тесты K = 654321, W = 1e4.
Если твой алгоритм переложить на ассоциативные массивы то может и будет достаточно быстро,
а если делать вложенные циклы в лоб, то боюсь не взлетит.
Но если делать с усложнением, то будет проще и быстрее. ;)
Stax
не могу понять, как в Вашем варианте вывести, кто сколько коробок обработал
Например, добавить еще один столбец с конкатенацией.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123589
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
метод последовательного прближения половинным делением еще никто не предлагал?
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123595
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreymx
метод последовательного прближения половинным делением еще никто не предлагал?
Я об этом подумал когда тут отвечал 22413711 .
Второе выражение вычисляешь подыскивая число минут для первого.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123596
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andreymx
метод последовательного прближения половинным делением еще никто не предлагал?

22413444

....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123599
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

Если твой алгоритм переложить на ассоциативные массивы то может и будет достаточно быстро,

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

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

dbms_photoshop

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


не понял как прикрутить, хотел проверить как оно раздало коробки при паралели


......
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40123603
Фотография Stax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dbms_photoshop

Stax
правильно 6
Good catch. Я недоправил case expression Кузьмича. Так должно быть ок.
Код: plsql
1.
case when mod(n - 1, c) = 0 and n > 1 then d else 0 end



как глянуть, как раздались коробки
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
SQL> ed
Wrote file afiedt.buf

  1  with w(id, s, c, d) as (
  2                select 1, 0.3, 1, 1 from dual
  3      union all select 2, 0.5, 2, 0.5 from dual
  4      union all select 3, 0.3, 1, 1 from dual
  5      union all select 3, 1, 1, 1000 from dual)
  6  , b as (select rownum n from dual connect by level <= 12)
  7  select tt
  8    from (select t.*, row_number() over (order by tt) k
  9            from (select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
 10                    from (select s, c, d, id, n, s + case when mod(n - 1, c) = 0 and n > 1 then d else 0 end total_time_with_pause
 11                            from b, w)) t)
 12*  where k = 12
SQL> /

        TT
----------
       4.2

SQL>



у меня вышло 3.5

....
stax
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #40124251
Фотография dbms_photoshop
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Stax
dbms_photoshop

пропущено...
Good catch. Я недоправил case expression Кузьмича. Так должно быть ок.
Код: plsql
1.
case when mod(n - 1, c) = 0 and n > 1 then d else 0 end



как глянуть, как раздались коробки
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
SQL> ed
Wrote file afiedt.buf

  1  with w(id, s, c, d) as (
  2                select 1, 0.3, 1, 1 from dual
  3      union all select 2, 0.5, 2, 0.5 from dual
  4      union all select 3, 0.3, 1, 1 from dual
  5      union all select 3, 1, 1, 1000 from dual)
  6  , b as (select rownum n from dual connect by level <= 12)
  7  select tt
  8    from (select t.*, row_number() over (order by tt) k
  9            from (select id, s, c, d, n, sum(total_time_with_pause) over (partition by id order by n) tt
 10                    from (select s, c, d, id, n, s + case when mod(n - 1, c) = 0 and n > 1 then d else 0 end total_time_with_pause
 11                            from b, w)) t)
 12*  where k = 12
SQL> /

        TT
----------
       4.2

SQL>



у меня вышло 3.5

....
stax
Если не добавлять новые столбцы в исходные наборы вероятно было бы меньше недопонимания.
Предположу что остальные вопросы касательно производительности и того как вывести доп. инфо отпадут если немного вникнуть в альтернативные решения.
...
Рейтинг: 0 / 0
Пятничная задача: работнички.
    #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
61 сообщений из 61, показаны все 3 страниц
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пятничная задача: работнички.
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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