Форум программистов, компьютерный форум, киберфорум
Алгоритмы
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.67/18: Рейтинг темы: голосов - 18, средняя оценка - 4.67
0 / 0 / 0
Регистрация: 15.04.2022
Сообщений: 4

Разрезы прямоугольника на детали без отходов

15.04.2022, 13:29. Показов 3915. Ответов 24

Студворк — интернет-сервис помощи студентам
Необходимо разрезать прямоугольник размера x × y на детали прямоугольной формы размера x1 × y1 и x2 × y2, чтобы отходы были минимальными. Возможны только вертикальные и горизонтальные разрезы. Т. е. после первого разреза получается два прямоугольника, которые впоследствии также можно разрезать.

Формат входных данных
На входе содержится шесть целых чисел x, y, x1, y1, x2, y2 (1 ≤ x, y ≤ 300, 1 ≤ x1, x2 ≤ x, 1 ≤ y1, y2 ≤ y).
Формат выходных данных
Выведите минимальную сумму площадей остатков.
Пример входных данных 10 10 2 9 3 4
Ответ 10
Помогите пожалуйста решить, я понимаю примерно что нужно делать но не могу даже составить рекуррентное соотношение.
Задача должна решаться через динамическое программирование.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
15.04.2022, 13:29
Ответы с готовыми решениями:

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

Два прямоугольника заданы координатами своих вершин. Определите, параллельны ли стороны одного прямоугольника сторонам другого прямоугольника.
1 Два прямоугольника заданы координатами своих вершин. Определите, параллельны ли стороны одного прямоугольника сторонам другого...

Нахождение количества точек внутри прямоугольника, на границе прямоугольника, вне прямоугольника
Как найти количество точек внутри прямоугольника, на границе прямоугольника, вне прямоугольника. Дан массив со случайными числами: x (...

24
6180 / 945 / 313
Регистрация: 25.02.2011
Сообщений: 1,381
Записей в блоге: 1
15.04.2022, 21:09
Количество повторов 1й и 2й детали может быть любой?
На сколько я понял, то нужно реализовать гильотинный раскрой (от края до края)
Прямоугольники можно поворачивать?

Посмотрите решение похожей задачи во вложении, оно как раз реализовано с помощью динамического программирования
Количество деталей может быть более 2х
Вложения
Тип файла: rar Контейнеры 2D.rar (38.4 Кб, 46 просмотров)
1
383 / 280 / 112
Регистрация: 28.04.2015
Сообщений: 1,726
16.04.2022, 04:59
m-ch, привет!
ты ведь большой мастак в графах, думал, что не пропустишь эту тему и предложишь какое-нибудь хорошее решение: Два наименьших остовных дерева

зы: нет, так нет)) а Венгерский алгоритм обязательно обсудим (если у тебя будет желание, разумеется, это обсуждать в принципе)...
0
0 / 0 / 0
Регистрация: 15.04.2022
Сообщений: 4
16.04.2022, 14:35  [ТС]
Что вы имеете ввиду под повторами деталей. Как я понял ваш вопрос, то наш изначальный прямоугольник можно разбить хоть на сто деталей допустим 1-ого вида и тогда отходов 0. А можно разбить на 5 деталей 1-ого вида, на 6 деталей 2-ого вида и останется площадь которую мы не сможем никак разбить на нужные нам детали и это будут отходы которые нам нужны(минимальные).Прямоугольники можно поворачивать
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
18.04.2022, 06:39
Цитата Сообщение от mr_wow3 Посмотреть сообщение
Разрезы прямоугольника на детали без отходов
...
Необходимо разрезать прямоугольник размера x × y на детали прямоугольной формы размера x1 × y1 и x2 × y2, чтобы отходы были минимальными
"Без отходов" и "чтобы отходы были минимальными" - это несколько разные вещи.

Цитата Сообщение от mr_wow3 Посмотреть сообщение
Возможны только вертикальные и горизонтальные разрезы. Т. е. после первого разреза получается два прямоугольника, которые впоследствии также можно разрезать.
Это опять - непонятное условие.

Как насчет вот такого способа разреза? Тут все разрезы: вертикальные и горизонтальные. Однако первый разрез ведется не от края до края (негильотинный разрез), то есть после него НЕ получается два прямоугольника. Так можно или нельзя?
Изображения
 
0
0 / 0 / 0
Регистрация: 15.04.2022
Сообщений: 4
19.04.2022, 21:54  [ТС]
Если есть есть возможность разрезать без отходов то режем на наши фигуры, если же не получится разрезать только на наши фигуры, то нужно минимизировать отходы. Разрезы должны быть от края до края.
0
6180 / 945 / 313
Регистрация: 25.02.2011
Сообщений: 1,381
Записей в блоге: 1
19.04.2022, 22:04
mr_wow3, Вы смотрели мое решение, оно не подходит?
Если есть только два вида деталей, то можно упростить решение, в моем примере можно использовать до 20 различных прямоугольных деталей, с возможностью их поворачивать, использовать любое их количество, алгоритм старается сократить отходы, вплоть до нуля.
Потестируйте на различных размерах исходного прямоугольника и различных деталях
0
0 / 0 / 0
Регистрация: 15.04.2022
Сообщений: 4
19.04.2022, 22:10  [ТС]
Я посмотрел архив, но там же только документ эксель, если вы о нем то спасибо за наглядный пример, но я все же не знаю как это запрограммировать.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
19.04.2022, 22:26
Цитата Сообщение от mr_wow3 Посмотреть сообщение
Если есть есть возможность разрезать без отходов то режем на наши фигуры, если же не получится разрезать только на наши фигуры, то нужно минимизировать отходы. Разрезы должны быть от края до края.
Ну то есть задача все таки состоит в минимизации отходов. И задача на гильотинные разрезы.

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

Хотя, тот факт, что деталей всего две (с поворотами - четыре), может упрощать решение.
0
6180 / 945 / 313
Регистрация: 25.02.2011
Сообщений: 1,381
Записей в блоге: 1
20.04.2022, 06:08
mr_wow3, в документе .xlsm есть макрос написанный на VBA. Который производит расчёт, если нажать на кнопку «нажми» то происходит пересчёт (макросы должны быть разрешены), кода в документе на несколько десятков строк.
Если изменить исходные данные и нажать пересчёт, то модель пересчитается.

TheCalligrapher, если задачу свести к динамическому программированию, то это избавит от полного перебора и можно найти одно из эффективных решений, в данном случае как раз и состоит сложность как реализовать динамическое программирование. Исходная задача не требует построения схемы раскроя и используется всего две детали, что упрощает подход к решению, нужно найти только минимальные отходы, думаю что задачу можно свести к динамическому программированию,учитывая, что размеры прямоугольников целые числа не более 300
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
20.04.2022, 07:20
Цитата Сообщение от m-ch Посмотреть сообщение
если задачу свести к динамическому программированию, то это избавит от полного перебора и можно найти одно из эффективных решений
Ну, скажем так, динамическое программирование избавит от необходимости решать одну и ту же подзадачу несколько раз, то есть позволит превратить "уж совсем тупой полный перебор" в просто "полный перебор". Но эффективного алгоритма из этого все равное не получится, если требуется именно точное решение задачи.

Жизнеспособность тут проистекает из того факта, что размер поля всего 300 на 300. То есть можно ожидать, что и перебор пройдет за приемлемое время.
0
6180 / 945 / 313
Регистрация: 25.02.2011
Сообщений: 1,381
Записей в блоге: 1
20.04.2022, 09:10
У меня в реализации используется гильотинный раскрой (от края до края), при этом исходный прямоугольник разрезается на полосы (либо горизонтальные, либо вертикальные). Каждая подзадача (полоса) решается рюкзаком через динамическое программирование с минимизацией отходов, и далее происходит итоговое решение также рюкзаком, когда мы из полос набираем нужный исходный прямоугольник с минимизацией отходов.

Например, возьмем паллет 1200х800 (в дюймах это будет 47х31), а также детали (коробки) 3х17, 5х13, 7х11 (все размеры простые числа, для того, чтобы детали хуже комбинировались друг с другом)

Во вложении пример их укладки по алгоритму описанному выше (в документе все работает и пересчитывается при нажатии на кнопку).
Отходы на прямоугольник 47х31 составили 20 квадратных единиц (потери 1,37%). Предполагаю, что если применять не гильотинный раскрой, как приведен в 5м сообщении, то можно получить более оптимальное решение (но не обязательно). Остается вопрос, сколько требуется времени, чтобы найти более оптимальное решение перебором?
Вложения
Тип файла: zip Контейнеры 2D.zip (44.5 Кб, 23 просмотров)
1
6180 / 945 / 313
Регистрация: 25.02.2011
Сообщений: 1,381
Записей в блоге: 1
20.04.2022, 11:49
Цитата Сообщение от FasterHarder Посмотреть сообщение
Венгерский алгоритм обязательно обсудим
Что то тема остановилась, ни вопросов ни другой информации для обсуждения

Цитата Сообщение от FasterHarder Посмотреть сообщение
предложишь какое-нибудь хорошее решение
Ничего хорошего кроме перебора не приходит на ум
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
20.04.2022, 22:17
Цитата Сообщение от m-ch Посмотреть сообщение
У меня в реализации используется гильотинный раскрой (от края до края), при этом исходный прямоугольник разрезается на полосы
Мы все строим гильотинные раскрои, как сказано в условии задачи.

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

Правильность данного утверждения очевидна: какое бы финальное размещение мы не получили, если у нас есть возможность "вынуть" из этого размещения некое подразмещение и заменить его на другое подразмещение такого же размера, но с меньшим внутренним отходом, то качество финального решения от этого только улучшится.

То есть на каждом этапе перебора для каждого размера частичного решения WxH нас интересует только одно - "самое лучшее" - найденное на данный момент частичное решение. Это существенно сужает перебор.

Написал несложную С++ программку такого перебора. Вашу задачу 47х31, 3х17, 5х13, 7х11 решает мгновенно. Отход в найденном мною лучшем решении - 8 (см. ниже). А вы говорите, что у вас получилось 20. Это плохо.
Миниатюры
Разрезы прямоугольника на детали без отходов  
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
21.04.2022, 04:11
Если для тех же ваших размеров коробок взять палетту 79x57, то возможно идеальное безотходное заполнение. Ваш же Эксель не находит этого решения и оставляет отход 5.
Миниатюры
Разрезы прямоугольника на детали без отходов   Разрезы прямоугольника на детали без отходов  
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
21.04.2022, 04:40
... ну или, если отбросить общие части наших решений, ваш алгоритм не умеет находить безотходное решение для 40x57. И далее: 40x44, 26x44... Все эти варианты допускают идеальное заполнение, но ваш алгоритм его не видит.
0
6180 / 945 / 313
Регистрация: 25.02.2011
Сообщений: 1,381
Записей в блоге: 1
21.04.2022, 06:12
TheCalligrapher, у Вас получились очень хорошие решения, поделитесь кодом и/или описанием алгоритма
Свой метод я описал, он не идеален, но позволяет достаточно быстро решать с приемлемым результатом. Относительно гильотинного раскроя, есть задачи, которые можно решать только так, например, резка стекла. То что у Вас получилось - мне очень понравилось, учитывая, что расчет происходит быстро.
Есть ли у Вас примеры реализации двумерного раскроя с ограниченным количеством деталей?
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
22.04.2022, 00:12
Цитата Сообщение от m-ch Посмотреть сообщение
поделитесь кодом и/или описанием алгоритма
Мое решение построено на основе элементарной идеи попарного слияния.

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



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

В этом и заключается основной скелет алгоритма. А далее начинаются оптимизации перебора. В некоторых узких кругах этот алгоритм называют "алгоритмом Ванга" в честь автора статьи:
P. Y. Wang "Two Algorithms for Constrained Two-Dimensional Cutting Stock Problems"
https://www.jstor.org/stable/170624

---

Вот вариант моего кода, который выдает только размер отхода в найденном оптимальном решении, но держит само решение в тайне.

Это С++ с незначительным использованием С++20 (#include <bit> и оператор сравнения == по умолчанию. Фактически использование С++20 можно легко устранить.)

Вход берется из стандартного входного потока в формате:
<W палетты> <H палетты> <N коробок> <W коробки 1> <H коробки 1> <W коробки 2> <H коробки 2>...

Только отход
C++
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
#include <cassert>
#include <bit>
#include <vector>
#include <unordered_map>
#include <iostream>
 
template <typename T> std::size_t hash_mix(std::size_t hash, T v)
{
  return std::rotl(hash, 1) ^ std::hash<T>()(v);
}
 
struct Rect
{ 
  unsigned dx, dy; 
 
  unsigned area() const
    { return dx * dy; }
 
  bool fits(const Rect &limits) const
    { return dx <= limits.dx && dy <= limits.dy; }
 
  struct Hash
  {
    std::size_t operator ()(const Rect &r) const
      { return hash_mix(hash_mix(0, r.dx), r.dy); }
  };
 
  friend bool operator ==(const Rect &, const Rect &) = default;    
};
 
enum class Composition
{
  ORIGINAL,
  HORIZONTAL,
  VERTICAL
};
 
struct PatternDesc
{
  unsigned waste = std::numeric_limits<unsigned>::max();
};
 
using Patterns = std::unordered_map<Rect, PatternDesc, Rect::Hash>;
 
unsigned get_total_waste(const Patterns::value_type &pattern, const Rect &limits)
{
  const Rect &box = pattern.first;
  const PatternDesc &desc = pattern.second;
  assert(box.area() <= limits.area()); 
  return limits.area() - box.area() + desc.waste;
}
 
void combine(const Patterns::value_type &a, const Patterns::value_type &b, 
             const Rect &limits,
             const Patterns &patterns, Patterns &new_patterns)
{
  const Rect &box_a = a.first, &box_b = b.first;
  const PatternDesc &desc_a = a.second, &desc_b = b.second;
 
  for (Composition composition : { Composition::HORIZONTAL, Composition::VERTICAL })
  {
    Rect box;
    if (composition == Composition::HORIZONTAL)
      box = { box_a.dx + box_b.dx, std::max(box_a.dy, box_b.dy) };
    else
      box = { std::max(box_a.dx, box_b.dx), box_a.dy + box_b.dy };
 
    assert(box_a.area() + box_b.area() <= box.area());
 
    if (!box.fits(limits))
      continue;
 
    unsigned 
      new_waste = box.area() - (box_a.area() + box_b.area()),
      waste = new_waste + desc_a.waste + desc_b.waste;
 
    Patterns::const_iterator it = patterns.find(box);
    if (it != patterns.end())
    {
      const PatternDesc &desc = it->second;
      if (waste >= desc.waste)
        continue;
    }
 
    // New or better pattern
 
    PatternDesc &new_desc = new_patterns[box];
    if (waste >= new_desc.waste)
      continue;
 
    new_desc = { waste };
  }
}
 
int main()
{
  Rect limits;
  std::cin >> limits.dx >> limits.dy;
 
  unsigned n;
  std::cin >> n;
 
  Patterns patterns, new_patterns;
 
  for (unsigned i = 0; i < n; ++i)
  {
    Rect box;
    std::cin >> box.dx >> box.dy;
    new_patterns[box] = { 0 };
    std::swap(box.dx, box.dy);
    new_patterns[box] = { 0 };
  }
 
  do
  {
    std::vector<Patterns::const_iterator> updated;
 
    while (!new_patterns.empty())
    {
      Patterns::node_type node = new_patterns.extract(new_patterns.begin());
 
      auto ir = patterns.insert(std::move(node));
      if (!ir.inserted)
      {
        assert(ir.position->first == ir.node.key());
        ir.position->second = ir.node.mapped();
      }
 
      updated.push_back(ir.position);
    }
 
    if (updated.empty())
      break;
 
    for (Patterns::const_iterator it_a : updated)
      for (const Patterns::value_type &b : patterns)
        combine(*it_a, b, limits, patterns, new_patterns);
 
  } while (true);
 
  Patterns::const_iterator it_min = patterns.begin();
  unsigned min_total_waste = get_total_waste(*it_min, limits);
 
  for (Patterns::const_iterator it = patterns.begin(); it != patterns.end(); ++it)
  {
    unsigned total_waste = get_total_waste(*it, limits);
    if (total_waste < min_total_waste)
    {
      min_total_waste = total_waste;
      it_min = it;
    }
  }
 
  std::cout << "Min waste: " << min_total_waste << std::endl;
}


Вот более расширенный вариант того же кода, который может выдавать решение в файл в формате GDSII-текстовый (если сделано #define DUMP_GDS). Такие файлы можно смотреть при помощи смотрелки KLayout (https://www.klayout.de). Также, иерархическая структура геометрии в файле отражает иерархию попарного слияния блоков в процессе работы алгоритма.

С формированием GDS файла
C++
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
#include <cassert>
#include <bit>
#include <vector>
#include <unordered_map>
#include <iostream>
#include <fstream>
 
#define DUMP_GDS
 
template <typename T> std::size_t hash_mix(std::size_t hash, T v)
{
  return std::rotl(hash, 1) ^ std::hash<T>()(v);
}
 
struct Rect
{ 
  unsigned dx, dy; 
 
  unsigned area() const
    { return dx * dy; }
 
  bool fits(const Rect &limits) const
    { return dx <= limits.dx && dy <= limits.dy; }
 
  struct Hash
  {
    std::size_t operator ()(const Rect &r) const
      { return hash_mix(hash_mix(0, r.dx), r.dy); }
  };
 
  friend bool operator ==(const Rect &, const Rect &) = default;    
};
 
enum class Composition
{
  ORIGINAL,
  HORIZONTAL,
  VERTICAL
};
 
struct PatternDesc
{
  unsigned waste = std::numeric_limits<unsigned>::max();
 
#ifdef DUMP_GDS
  unsigned id;
  Composition composition = Composition::ORIGINAL;
  Rect key_a, key_b; // Patterns::const_iterator it_a, it_b;
  mutable bool is_dumped = false;
#endif // DUMP_GDS
};
 
using Patterns = std::unordered_map<Rect, PatternDesc, Rect::Hash>;
 
unsigned get_total_waste(const Patterns::value_type &pattern, const Rect &limits)
{
  const Rect &box = pattern.first;
  const PatternDesc &desc = pattern.second;
  assert(box.area() <= limits.area()); 
  return limits.area() - box.area() + desc.waste;
}
 
void combine(const Patterns::value_type &a, const Patterns::value_type &b, 
             const Rect &limits,
             const Patterns &patterns, Patterns &new_patterns)
{
  const Rect &box_a = a.first, &box_b = b.first;
  const PatternDesc &desc_a = a.second, &desc_b = b.second;
 
  for (Composition composition : { Composition::HORIZONTAL, Composition::VERTICAL })
  {
    Rect box;
    if (composition == Composition::HORIZONTAL)
      box = { box_a.dx + box_b.dx, std::max(box_a.dy, box_b.dy) };
    else
      box = { std::max(box_a.dx, box_b.dx), box_a.dy + box_b.dy };
 
    assert(box_a.area() + box_b.area() <= box.area());
 
    if (!box.fits(limits))
      continue;
 
    unsigned 
      new_waste = box.area() - (box_a.area() + box_b.area()),
      waste = new_waste + desc_a.waste + desc_b.waste;
 
    Patterns::const_iterator it = patterns.find(box);
    if (it != patterns.end())
    {
      const PatternDesc &desc = it->second;
      if (waste >= desc.waste)
        continue;
    }
 
    // New or better pattern
 
    PatternDesc &new_desc = new_patterns[box];
    if (waste >= new_desc.waste)
      continue;
 
    new_desc = { waste };
 
#ifdef DUMP_GDS
    static unsigned i_desc_id;
    new_desc.id = ++i_desc_id; 
    new_desc.composition = composition;
    new_desc.key_a = box_a;
    new_desc.key_b = box_b;
#endif // DUMP_GDS
  }
}
 
#ifdef DUMP_GDS
 
void dump_gds_sname(std::ofstream &f, Patterns::const_iterator it_cell)
{
  const Rect &box = it_cell->first;
  const PatternDesc &desc = it_cell->second;
  f << (desc.composition == Composition::ORIGINAL ? "Box_" : "Pattern_") << 
    desc.id << "_" << box.dx << "x" << box.dy;
}
 
void dump_gds_rect(std::ofstream &f, unsigned layer, const Rect &r)
{
  f << "BOUNDARY" << std::endl;
  f << "LAYER " << layer << std::endl;
  f << "DATATYPE 0" << std::endl;
  f << "XY 0:0" << std::endl;
  f << r.dx << ":0" << std::endl;
  f << r.dx << ":" << r.dy << std::endl;
  f << "0:" << r.dy << std::endl;
  f << "0:0" << std::endl;
  f << "ENDEL" << std::endl;
}
 
void dump_gds_cell(std::ofstream &f, Patterns::const_iterator it_cell, const Patterns &patterns)
{
  const Rect &box = it_cell->first;
  const PatternDesc &desc = it_cell->second;
 
  if (desc.is_dumped)
    return;
 
  desc.is_dumped = true;
 
  f << "BGNSTR 1/1/2022 00:00:00 1/1/2022 00:00:00" << std::endl;
  f << "STRNAME "; dump_gds_sname(f, it_cell); f << std::endl;
 
  Patterns::const_iterator it_sub_cell_a, it_sub_cell_b;
 
  if (desc.composition == Composition::ORIGINAL)
    dump_gds_rect(f, desc.id, box);
  else
  {
    it_sub_cell_a = patterns.find(desc.key_a);
    it_sub_cell_b = patterns.find(desc.key_b);
    assert(it_sub_cell_a != patterns.end() && it_sub_cell_b != patterns.end());
 
    f << "SREF" << std::endl;
    f << "SNAME "; dump_gds_sname(f, it_sub_cell_a); f << std::endl;
    f << "XY 0:0" << std::endl;
    f << "ENDEL" << std::endl;
 
    f << "SREF" << std::endl;
    f << "SNAME "; dump_gds_sname(f, it_sub_cell_b); f << std::endl;
 
    if (desc.composition == Composition::HORIZONTAL)
      f << "XY " << it_sub_cell_a->first.dx << ":0" << std::endl;
    else
      f << "XY 0:" << it_sub_cell_a->first.dy << std::endl;
 
    f << "ENDEL" << std::endl;
  }
 
  f << "ENDSTR" << std::endl << std::endl;
 
  if (desc.composition != Composition::ORIGINAL)
  {
    dump_gds_cell(f, it_sub_cell_a, patterns);
    dump_gds_cell(f, it_sub_cell_b, patterns);
  }
}
 
void dump_gds(const char *file, Patterns::const_iterator it_root, 
              const Patterns &patterns, const Rect &limits )
{
  std::ofstream f(file);
  if (!f)
    return;
 
  f << "HEADER 600" << std::endl;
  f << "BGNLIB 1/1/2022 00:00:00 1/1/2022 00:00:00" << std::endl;
  f << "LIBNAME " << limits.dx << "x" << limits.dy << std::endl;
  f << "UNITS 0.001 1e-09" << std::endl << std::endl;
 
  f << "BGNSTR 1/1/2022 00:00:00 1/1/2022 00:00:00" << std::endl;
  f << "STRNAME Result_" << limits.dx << "x" << limits.dy << std::endl;
  dump_gds_rect(f, 0, limits);
  f << "SREF" << std::endl;
  f << "SNAME "; dump_gds_sname(f, it_root); f << std::endl;
  f << "XY 0:0" << std::endl;
  f << "ENDEL" << std::endl;
  f << "ENDSTR" << std::endl << std::endl;
 
  dump_gds_cell(f, it_root, patterns);
 
  f << "ENDLIB" << std::endl;
}
 
#endif // DUMP_GDS
 
int main()
{
  Rect limits;
  std::cin >> limits.dx >> limits.dy;
 
  unsigned n;
  std::cin >> n;
 
  Patterns patterns, new_patterns;
 
  for (unsigned i = 0; i < n; ++i)
  {
    Rect box;
    std::cin >> box.dx >> box.dy;
 
    {
      PatternDesc &desc = new_patterns[box];
      desc = { 0 };
#ifdef DUMP_GDS
      desc.id = i + 1;
      desc.composition = Composition::ORIGINAL;
#endif // DUMP_GDS
    }
      
    std::swap(box.dx, box.dy);
 
    {
      PatternDesc &desc = new_patterns[box];
      desc = { 0 };
#ifdef DUMP_GDS
      desc.id = i + 1;
      desc.composition = Composition::ORIGINAL;
#endif // DUMP_GDS
    }
  }
 
  do
  {
    std::vector<Patterns::const_iterator> updated;
 
    while (!new_patterns.empty())
    {
      Patterns::node_type node = new_patterns.extract(new_patterns.begin());
 
      auto ir = patterns.insert(std::move(node));
      if (!ir.inserted)
      {
        assert(ir.position->first == ir.node.key());
        ir.position->second = ir.node.mapped();
      }
 
      updated.push_back(ir.position);
    }
 
    if (updated.empty())
      break;
 
    for (Patterns::const_iterator it_a : updated)
      for (const Patterns::value_type &b : patterns)
        combine(*it_a, b, limits, patterns, new_patterns);
 
  } while (true);
 
  Patterns::const_iterator it_min = patterns.begin();
  unsigned min_total_waste = get_total_waste(*it_min, limits);
 
  for (Patterns::const_iterator it = patterns.begin(); it != patterns.end(); ++it)
  {
    unsigned total_waste = get_total_waste(*it, limits);
    if (total_waste < min_total_waste)
    {
      min_total_waste = total_waste;
      it_min = it;
    }
  }
 
  std::cout << "Min waste: " << min_total_waste << std::endl;
 
#ifdef DUMP_GDS
  std::cout << "Saving GDS file..." << std::endl;
  dump_gds("result.gds", it_min, patterns, limits);
  std::cout << "Done." << std::endl;
#endif // DUMP_GDS
}
Миниатюры
Разрезы прямоугольника на детали без отходов  
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
22.04.2022, 02:42
Работа продолжается...

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

За счет этой модификации на примере 150 150 3 3 17 5 13 7 11 я получаю ускорение где-то в 5-6 раз. А задача 300 300 3 3 17 5 13 7 11 у меня теперь решается где-то 5 минут (нулевой отход).

Тут, кстати, можно хорошо применить многопоточность.
Миниатюры
Разрезы прямоугольника на детали без отходов  
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12919 / 6787 / 1817
Регистрация: 18.10.2014
Сообщений: 17,169
22.04.2022, 02:50
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
А задача 300 300 3 3 17 5 13 7 11 у меня теперь решается где-то 5 минут (нулевой отход).
Ваш скрипт, m-ch, надо заметить, решает это задачу намного быстрее. Тоже с нулевым отходом и менее эклектичным результатом.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
22.04.2022, 02:50
Помогаю со студенческими работами здесь

Вероятность случайно выбраной детали без дефектов
Помогите пожалуйста с решение задачи. Детали попадают на предприятие с 3-их цехов: 50% - с 1-го, 30% - с 2-го и 20% с 3 - го. При этом...

Посоветуйте литературу по выполнению прямоугольной изометрии детали, эскиза детали
посоветуйте литературу по выполнению прямоугольной изометрии детали,эскиза детали

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

Разрезы
Помогите пожалуйста сделать необходимые разрезы.

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


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru