powered by simpleCommunicator - 2.0.29     © 2024 Programmizd 02
Map
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пятничная задача: работнички.
25 сообщений из 61, страница 2 из 3
Пятничная задача: работнички.
    #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
25 сообщений из 61, страница 2 из 3
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Пятничная задача: работнички.
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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