自动化立体仓库 - WMS系统
*
L
6 天以前 5099dfcf8e015c35a2f81986101e08e93a7d7944
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
package com.zy.asrs.utils;
 
import java.util.*;
 
public class OptimizedLockerPackingUtils {
 
    public static class Item {
        String name;
        double unitSpace;
        int maxCapacity;
        int stock;
        int remaining;
 
        public Item(String name, int maxCapacity, int stock) {
            this.name = name;
            this.maxCapacity = maxCapacity;
            this.unitSpace = 1.0 / maxCapacity;
            this.stock = stock;
            this.remaining = stock;
        }
 
        @Override
        public String toString() {
            return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)";
        }
    }
 
    static class Locker {
        int id;
        double remainingSpace;
        long bindingTags;
        Map<String, Integer> contents;
        Set<String> itemTypes;
 
        public Locker(int id) {
            this.id = id;
            this.remainingSpace = 1.0;
            this.contents = new HashMap<>();
            this.itemTypes = new HashSet<>();
            this.bindingTags = System.currentTimeMillis();
 
        }
 
        public boolean canAdd(Item item, int quantity) {
            return remainingSpace >= quantity * item.unitSpace;
        }
 
        public void addItem(Item item, int quantity) {
            double spaceUsed = quantity * item.unitSpace;
            remainingSpace -= spaceUsed;
            contents.put(item.name, contents.getOrDefault(item.name, 0) + quantity);
            itemTypes.add(item.name);
            item.remaining -= quantity;
        }
 
        public boolean containsItemType(String itemName) {
            return itemTypes.contains(itemName);
        }
 
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("储物柜").append(id).append(": ");
            sb.append(String.format("剩余空间%.4f", remainingSpace)).append("\n");
            for (Map.Entry<String, Integer> entry : contents.entrySet()) {
                sb.append("  ").append(entry.getKey()).append(": ").append(entry.getValue()).append("个\n");
            }
            return sb.toString();
        }
    }
 
    static class PackingSolution {
        List<Locker> lockers;
        int totalLockersUsed;
        double overallUtilization;
        int itemTypeChanges;
        int pureLockers; // 纯种类储物柜数量
 
        public PackingSolution() {
            this.lockers = new ArrayList<>();
        }
 
        public void addLocker(Locker locker) {
            lockers.add(locker);
            totalLockersUsed = lockers.size();
        }
 
        public void calculateMetrics() {
            double totalUsedSpace = 0;
            itemTypeChanges = 0;
            pureLockers = 0;
            String lastItemType = null;
 
            for (Locker locker : lockers) {
                totalUsedSpace += (1.0 - locker.remainingSpace);
 
                // 统计纯种类储物柜
                if (locker.itemTypes.size() == 1) {
                    pureLockers++;
                }
 
                for (String itemType : locker.itemTypes) {
                    if (lastItemType != null && !lastItemType.equals(itemType)) {
                        itemTypeChanges++;
                    }
                    lastItemType = itemType;
                }
            }
 
            overallUtilization = totalUsedSpace / totalLockersUsed;
        }
 
        @Override
        public String toString() {
            calculateMetrics();
            StringBuilder sb = new StringBuilder();
            sb.append("=== 优化摆放方案 ===\n");
            sb.append("使用的储物柜数量: ").append(totalLockersUsed).append("个\n");
            sb.append("纯种类储物柜: ").append(pureLockers).append("个\n");
            sb.append("平均空间利用率: ").append(String.format("%.2f", overallUtilization * 100)).append("%\n");
            sb.append("物品种类切换次数: ").append(itemTypeChanges).append("次\n\n");
 
            // 先显示纯种类储物柜,再显示混合储物柜
            List<Locker> pureLockersList = new ArrayList<>();
            List<Locker> mixedLockersList = new ArrayList<>();
 
            for (Locker locker : lockers) {
                if (locker.itemTypes.size() == 1) {
                    pureLockersList.add(locker);
                } else {
                    mixedLockersList.add(locker);
                }
            }
 
            sb.append("【纯种类储物柜】:\n");
            for (Locker locker : pureLockersList) {
                sb.append(locker.toString()).append("\n");
            }
 
            sb.append("【混合储物柜】:\n");
            for (Locker locker : mixedLockersList) {
                sb.append(locker.toString()).append("\n");
            }
 
            return sb.toString();
        }
    }
 
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
 
        System.out.println("请输入物品种类数量:");
        int itemTypes = scanner.nextInt();
        scanner.nextLine();
 
        List<Item> items = new ArrayList<>();
 
        for (int i = 0; i < itemTypes; i++) {
            System.out.println("\n请输入第" + (i + 1) + "种物品的信息:");
            System.out.print("物品名称: ");
            String name = scanner.nextLine();
 
            System.out.print("单种存放最大数量: ");
            int maxCapacity = scanner.nextInt();
 
            System.out.print("库存数量: ");
            int stock = scanner.nextInt();
            scanner.nextLine();
 
            items.add(new Item(name, maxCapacity, stock));
        }
 
        PackingSolution solution = optimizedPacking(items);
        System.out.println("\n" + solution);
 
        scanner.close();
    }
 
    /**
     * 优化装箱算法:先集中处理同种物品,最后处理余料
     */
    public static PackingSolution optimizedPacking(List<Item> items) {
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
 
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
 
        // 第一阶段:对每种物品进行集中处理
        for (Item item : itemsToPack) {
            if (item.remaining == 0) continue;
 
            // 计算这种物品需要多少个完整的储物柜
            int fullLockersNeeded = item.remaining / item.maxCapacity;
            int remainder = item.remaining % item.maxCapacity;
 
            // 分配完整储物柜
            for (int i = 0; i < fullLockersNeeded; i++) {
                Locker locker = new Locker(lockerId++);
                locker.addItem(item, item.maxCapacity);
                solution.addLocker(locker);
            }
 
            // 如果有余料,暂时保留(第二阶段处理)
        }
 
        // 第二阶段:收集所有余料
        List<Item> remainingItems = new ArrayList<>();
        for (Item item : itemsToPack) {
            if (item.remaining > 0) {
                remainingItems.add(item);
            }
        }
 
        // 如果还有余料,用最佳适应算法处理
        if (!remainingItems.isEmpty()) {
            processRemainingItems(remainingItems, solution, lockerId);
        }
 
        return solution;
    }
 
    /**
     * 处理剩余物品(余料)
     */
    private static void processRemainingItems(List<Item> remainingItems, PackingSolution solution, int startLockerId) {
        int lockerId = startLockerId;
 
        // 按空间占用从大到小排序(优先处理大物品)
        remainingItems.sort((a, b) -> Double.compare(b.unitSpace, a.unitSpace));
 
        for (Item item : remainingItems) {
            while (item.remaining > 0) {
                // 优先尝试放入已有同种物品的储物柜
                Locker sameTypeLocker = findSameTypeLocker(solution, item);
                if (sameTypeLocker != null) {
                    int maxCanAdd = (int) Math.floor(sameTypeLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    sameTypeLocker.addItem(item, actualAdd);
                    continue;
                }
 
                // 其次尝试放入现有储物柜(最佳适应)
                Locker bestLocker = findBestLocker(solution, item);
                if (bestLocker != null) {
                    int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    bestLocker.addItem(item, actualAdd);
                    continue;
                }
 
                // 最后创建新储物柜
                Locker newLocker = new Locker(lockerId++);
                int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace);
                int actualAdd = Math.min(item.remaining, maxCanAdd);
                newLocker.addItem(item, actualAdd);
                solution.addLocker(newLocker);
            }
        }
    }
 
    /**
     * 寻找已有同种物品的储物柜
     */
    private static Locker findSameTypeLocker(PackingSolution solution, Item item) {
        Locker bestLocker = null;
        double bestRemaining = -1;
 
        for (Locker locker : solution.lockers) {
            if (locker.containsItemType(item.name) && locker.canAdd(item, 1)) {
                if (locker.remainingSpace > bestRemaining) {
                    bestLocker = locker;
                    bestRemaining = locker.remainingSpace;
                }
            }
        }
 
        return bestLocker;
    }
 
    /**
     * 寻找最佳适应的储物柜
     */
    private static Locker findBestLocker(PackingSolution solution, Item item) {
        Locker bestLocker = null;
        double bestRemaining = Double.MAX_VALUE;
 
        for (Locker locker : solution.lockers) {
            if (locker.canAdd(item, 1) && locker.remainingSpace < bestRemaining) {
                bestLocker = locker;
                bestRemaining = locker.remainingSpace;
            }
        }
 
        return bestLocker;
    }
 
    /**
     * 高级优化:智能余料分配
     */
    public static PackingSolution advancedOptimizedPacking(List<Item> items) {
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
 
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
 
        // 第一阶段:集中处理每种物品
        Map<String, Integer> remainders = new HashMap<>();
 
        for (Item item : itemsToPack) {
            if (item.remaining == 0) continue;
 
            int fullLockers = item.remaining / item.maxCapacity;
            int remainder = item.remaining % item.maxCapacity;
 
            for (int i = 0; i < fullLockers; i++) {
                Locker locker = new Locker(lockerId++);
                locker.addItem(item, item.maxCapacity);
                solution.addLocker(locker);
            }
 
            if (remainder > 0) {
                remainders.put(item.name, remainder);
            }
        }
 
        // 第二阶段:智能余料分配
        if (!remainders.isEmpty()) {
            smartRemainderProcessing(remainders, itemsToPack, solution, lockerId);
        }
 
        return solution;
    }
 
    /**
     * 智能余料处理:尝试将余料组合成完整储物柜
     */
    private static void smartRemainderProcessing(Map<String, Integer> remainders,
                                                 List<Item> items,
                                                 PackingSolution solution,
                                                 int startLockerId) {
        int lockerId = startLockerId;
 
        // 尝试找到可以完美组合的余料
        boolean foundCombination;
        do {
            foundCombination = false;
 
            // 寻找可以填满一个储物柜的余料组合
            for (Item item1 : items) {
                if (remainders.getOrDefault(item1.name, 0) == 0) continue;
 
                for (Item item2 : items) {
                    if (item1.name.equals(item2.name) || remainders.getOrDefault(item2.name, 0) == 0) continue;
 
                    double spaceNeeded = remainders.get(item1.name) * item1.unitSpace +
                            remainders.get(item2.name) * item2.unitSpace;
 
                    if (spaceNeeded <= 1.0 + 1e-6) { // 考虑浮点误差
                        Locker locker = new Locker(lockerId++);
                        locker.addItem(item1, remainders.get(item1.name));
                        locker.addItem(item2, remainders.get(item2.name));
                        solution.addLocker(locker);
 
                        remainders.put(item1.name, 0);
                        remainders.put(item2.name, 0);
                        foundCombination = true;
                        break;
                    }
                }
                if (foundCombination) break;
            }
        } while (foundCombination);
 
        // 处理剩余的零散余料
        for (Item item : items) {
            int remainder = remainders.getOrDefault(item.name, 0);
            if (remainder > 0) {
                Locker bestLocker = findBestLockerForRemainder(solution, item, remainder);
                if (bestLocker != null) {
                    bestLocker.addItem(item, remainder);
                } else {
                    Locker newLocker = new Locker(lockerId++);
                    newLocker.addItem(item, remainder);
                    solution.addLocker(newLocker);
                }
            }
        }
    }
 
    private static Locker findBestLockerForRemainder(PackingSolution solution, Item item, int quantity) {
        Locker bestLocker = null;
        double bestFit = Double.MAX_VALUE;
 
        for (Locker locker : solution.lockers) {
            if (locker.canAdd(item, quantity)) {
                double remainingAfterAdd = locker.remainingSpace - quantity * item.unitSpace;
                if (remainingAfterAdd < bestFit) {
                    bestLocker = locker;
                    bestFit = remainingAfterAdd;
                }
            }
        }
 
        return bestLocker;
    }
 
    /**
     * 性能测试和比较
     */
    public static void performanceTest() {
        System.out.println("=== 性能测试 ===");
 
        // 测试数据
        List<Item> testItems = Arrays.asList(
                new Item("X", 20, 87),
                new Item("Y", 15, 63),
                new Item("Z", 34, 125),
                new Item("T", 25, 48),
                new Item("U", 40, 92)
        );
 
        System.out.println("测试物品数量: " + testItems.size());
        int totalItems = testItems.stream().mapToInt(item -> item.stock).sum();
        System.out.println("总物品数量: " + totalItems);
 
        long startTime = System.currentTimeMillis();
        PackingSolution solution = optimizedPacking(testItems);
        long endTime = System.currentTimeMillis();
 
        System.out.println("\n优化算法结果:");
        System.out.println("处理时间: " + (endTime - startTime) + "ms");
        System.out.println("储物柜数量: " + solution.totalLockersUsed);
        System.out.println("纯种类储物柜: " + solution.pureLockers);
        System.out.println("空间利用率: " + String.format("%.2f", solution.overallUtilization * 100) + "%");
    }
}