“在代码的世界里,每一行都是进步的足迹,每一次挑战都是成长的机遇。”

贪心算法学习

import java.util.*;
import java.util.stream.Collectors;

// 托盘类
class Pallet {
    private String palletId;
    private List<Fixture> fixtures;
    private List<Material> materials;

    public Pallet(String palletId) {
        this.palletId = palletId;
        this.fixtures = new ArrayList<>();
        this.materials = new ArrayList<>();
    }

    // getter和setter方法
    public String getPalletId() {
        return palletId;
    }

    public List<Fixture> getFixtures() {
        return fixtures;
    }

    public List<Material> getMaterials() {
        return materials;
    }

    @Override
    public String toString() {
        return "托盘[" + palletId + "]";
    }
}

// 工装类
class Fixture {
    private String code;
    private String model;
    private String type;

    public Fixture(String code, String model, String type) {
        this.code = code;
        this.model = model;
        this.type = type;
    }
    public Fixture() {
    }

    public Fixture(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Fixture fixture = (Fixture) o;
        return Objects.equals(code, fixture.code);
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

// 辅料类
class Material {
    private String code;
    private String uniqueCode;

    public Material(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Material material = (Material) o;
        return Objects.equals(code, material.code);
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }
}

// 出库服务
public class PalletOutboundService {

    /**
     * 优化托盘出库
     * @param inventory 库存中的所有托盘
     * @param requiredFixtures 需要的工装对象列表(可能只包含功能类型)
     * @param requiredMaterialCodes 需要的辅料编码列表
     * @return 最优的出库托盘列表
     */
    public List<Pallet> optimizeOutbound(List<Pallet> inventory,
                                         List<Fixture> requiredFixtures,
                                         List<String> requiredMaterialCodes) {
        // 分离具体编码和功能类型的工装需求
        Set<String> requiredFixtureCodes = new HashSet<>();
        Set<String> requiredFixtureTypes = new HashSet<>();

        for (Fixture fixture : requiredFixtures) {
            if (fixture.getCode() != null && !fixture.getCode().isEmpty()) {
                requiredFixtureCodes.add(fixture.getCode());
            } else if (fixture.getType() != null && !fixture.getType().isEmpty()) {
                requiredFixtureTypes.add(fixture.getType());
            }
        }

        // 处理辅料需求
        Set<String> requiredMaterials = new HashSet<>(requiredMaterialCodes);

        // 复制一份库存,避免修改原始数据
        List<Pallet> inventoryCopy = new ArrayList<>(inventory);

        // 尝试找到包含所有需求的单个托盘
        Pallet perfectMatch = findPerfectMatch(inventoryCopy, requiredFixtureCodes, requiredFixtureTypes, requiredMaterials);
        if (perfectMatch != null) {
            return Collections.singletonList(perfectMatch);
        }

        // 使用贪心算法找到最优解
        return findOptimalPallets(inventoryCopy, requiredFixtureCodes, requiredFixtureTypes, requiredMaterials);
    }

    /**
     * 寻找完美匹配的托盘(包含所有需求)
     */
    private Pallet findPerfectMatch(List<Pallet> inventory,
                                    Set<String> requiredFixtureCodes,
                                    Set<String> requiredFixtureTypes,
                                    Set<String> requiredMaterials) {
        for (Pallet pallet : inventory) {
            // 检查是否满足所有编码需求
            Set<String> palletFixtureCodes = pallet.getFixtures().stream()
                    .map(Fixture::getCode)
                    .collect(Collectors.toSet());

            if (!palletFixtureCodes.containsAll(requiredFixtureCodes)) {
                continue;  // 不满足编码需求,跳过
            }

            // 检查是否满足所有类型需求
            boolean allTypesMatched = true;
            for (String type : requiredFixtureTypes) {
                boolean typeMatched = pallet.getFixtures().stream()
                        .anyMatch(f -> type.equals(f.getType()));

                if (!typeMatched) {
                    allTypesMatched = false;
                    break;
                }
            }

            if (!allTypesMatched) {
                continue;  // 不满足类型需求,跳过
            }

            // 检查是否满足所有辅料需求
            Set<String> palletMaterialCodes = pallet.getMaterials().stream()
                    .map(Material::getCode)
                    .collect(Collectors.toSet());

            if (palletMaterialCodes.containsAll(requiredMaterials)) {
                return pallet;  // 找到完美匹配
            }
        }

        return null;  // 没有找到完美匹配
    }

    /**
     * 使用贪心算法找到最优的托盘组合
     */
    private List<Pallet> findOptimalPallets(List<Pallet> inventory,
                                            Set<String> requiredFixtureCodes,
                                            Set<String> requiredFixtureTypes,
                                            Set<String> requiredMaterials) {
        List<Pallet> selectedPallets = new ArrayList<>();
        Set<String> remainingFixtureCodes = new HashSet<>(requiredFixtureCodes);
        Set<String> remainingFixtureTypes = new HashSet<>(requiredFixtureTypes);
        Set<String> remainingMaterials = new HashSet<>(requiredMaterials);

        // 当还有未满足的需求时继续循环
        while (!remainingFixtureCodes.isEmpty() || !remainingFixtureTypes.isEmpty() || !remainingMaterials.isEmpty()) {
            // 找到最佳托盘(包含最多未满足需求的托盘)
            Pallet bestPallet = null;
            int maxMatchCount = 0;

            for (Pallet pallet : inventory) {
                int matchCount = countMatches(pallet, remainingFixtureCodes, remainingFixtureTypes, remainingMaterials);

                if (matchCount > maxMatchCount) {
                    maxMatchCount = matchCount;
                    bestPallet = pallet;
                }
            }

            // 如果找不到匹配的托盘,说明无法满足所有需求
            if (bestPallet == null || maxMatchCount == 0) {
                throw new RuntimeException("无法满足所有出库需求");
            }

            // 添加最佳托盘到结果中
            selectedPallets.add(bestPallet);

            // 更新剩余需求
            updateRemainingRequirements(bestPallet, remainingFixtureCodes, remainingFixtureTypes, remainingMaterials);

            // 从库存中移除已选择的托盘
            inventory.remove(bestPallet);
        }

        return selectedPallets;
    }

    /**
     * 计算托盘中匹配的需求数量
     */
    private int countMatches(Pallet pallet,
                             Set<String> remainingFixtureCodes,
                             Set<String> remainingFixtureTypes,
                             Set<String> remainingMaterials) {
        int count = 0;
        Set<String> matchedTypes = new HashSet<>();

        // 计算匹配的工装编码数量
        for (Fixture fixture : pallet.getFixtures()) {
            if (remainingFixtureCodes.contains(fixture.getCode())) {
                count++;
            }
        }

        // 计算匹配的工装类型数量
        for (Fixture fixture : pallet.getFixtures()) {
            if (fixture.getType() != null &&
                    remainingFixtureTypes.contains(fixture.getType()) &&
                    !matchedTypes.contains(fixture.getType())) {
                count++;
                matchedTypes.add(fixture.getType());
            }
        }

        // 计算匹配的辅料数量
        for (Material material : pallet.getMaterials()) {
            if (remainingMaterials.contains(material.getCode())) {
                count++;
            }
        }

        return count;
    }

    /**
     * 更新剩余需求
     */
    private void updateRemainingRequirements(Pallet pallet,
                                             Set<String> remainingFixtureCodes,
                                             Set<String> remainingFixtureTypes,
                                             Set<String> remainingMaterials) {
        // 移除已满足的工装编码需求
        for (Fixture fixture : pallet.getFixtures()) {
            remainingFixtureCodes.remove(fixture.getCode());
        }

        // 移除已满足的工装类型需求
        Set<String> matchedTypes = new HashSet<>();
        for (Fixture fixture : pallet.getFixtures()) {
            if (fixture.getType() != null &&
                    remainingFixtureTypes.contains(fixture.getType()) &&
                    !matchedTypes.contains(fixture.getType())) {
                remainingFixtureTypes.remove(fixture.getType());
                matchedTypes.add(fixture.getType());
            }
        }

        // 移除已满足的辅料需求
        for (Material material : pallet.getMaterials()) {
            remainingMaterials.remove(material.getCode());
        }
    }

    // 测试方法
    public static void main(String[] args) {
        // 创建模拟数据库

        // 创建一些工装和辅料
        Fixture f1 = createFixture("F001", "型号A", "夹具");
        Fixture f2 = createFixture("F002", "型号B", "治具");
        Fixture f3 = createFixture("F003", "型号C", "模具");
        Fixture f4 = createFixture("F004", "型号D", "夹具");
        Fixture f5 = createFixture("F005", "型号E", "治具");
        Fixture f6 = createFixture("F006", "型号E", "其他");

        Material m1 = new Material("M001");
        Material m2 = new Material("M002");
        Material m3 = new Material("M003");

        // 创建托盘并添加工装和辅料
        Pallet p1 = new Pallet("P001");
        p1.getFixtures().add(f1);
        p1.getFixtures().add(f2);
        p1.getMaterials().add(m1);

        Pallet p2 = new Pallet("P002");
        p2.getFixtures().add(f2);
        p2.getFixtures().add(f4);
        p2.getMaterials().add(m2);

        Pallet p3 = new Pallet("P003");
        p3.getFixtures().add(f3);
        p3.getFixtures().add(f5);
        p3.getMaterials().add(m3);

        Pallet p4= new Pallet("P004");
        p4.getFixtures().add(f6);

        // 创建库存
        List<Pallet> inventory = Arrays.asList(p1, p2, p3, p4);

        // 创建出库服务
        PalletOutboundService service = new PalletOutboundService();

        // 测试场景1:需要F001, F002和M001(应该只出库P001)
        List<Fixture> requiredFixtures1 = Arrays.asList(
                createFixture("F001", null, null),
                createFixture("F002", null, null)
        );
        List<String> requiredMaterials1 = Arrays.asList("M001");

        System.out.println("测试场景1:按编码匹配");
        List<Pallet> result1 = service.optimizeOutbound(inventory, requiredFixtures1, requiredMaterials1);
        System.out.println("出库托盘数量: " + result1.size());
        for (Pallet p : result1) {
            System.out.println(p);
        }

        // 测试场景2:需要F001, F002, F003和M001, M003(应该出库P001和P003)
        List<Fixture> requiredFixtures2 = Arrays.asList(
                createFixture("F001", null, null),
                createFixture("F002", null, null),
                createFixture("F003", null, null)
        );
        List<String> requiredMaterials2 = Arrays.asList("M001", "M003");

        System.out.println("\n测试场景2:按编码匹配多个托盘");
        List<Pallet> result2 = service.optimizeOutbound(inventory, requiredFixtures2, requiredMaterials2);
        System.out.println("出库托盘数量: " + result2.size());
        for (Pallet p : result2) {
            System.out.println(p);
        }

        // 测试场景3:按类型匹配 - 需要一个"夹具"类型的工装和M002(应该出库P002)
        List<Fixture> requiredFixtures3 = Arrays.asList(
                createFixture(null, null, "夹具")
        );
        List<String> requiredMaterials3 = Arrays.asList("M002");

        System.out.println("\n测试场景3:按类型匹配");
        List<Pallet> result3 = service.optimizeOutbound(inventory, requiredFixtures3, requiredMaterials3);
        System.out.println("出库托盘数量: " + result3.size());
        for (Pallet p : result3) {
            System.out.println(p);
        }

        // 测试场景4:混合匹配 - 需要一个"夹具"类型和一个F003编码的工装,以及M001(应该出库P001和P003)
        List<Fixture> requiredFixtures4 = Arrays.asList(
                createFixture(null, null, "夹具"),
                createFixture("F003", null, null)
        );
        List<String> requiredMaterials4 = Arrays.asList("M001");

        System.out.println("\n测试场景4:混合匹配(类型+编码)");
        List<Pallet> result4 = service.optimizeOutbound(inventory, requiredFixtures4, requiredMaterials4);
        System.out.println("出库托盘数量: " + result4.size());
        for (Pallet p : result4) {
            System.out.println(p);
        }

        // 测试场景5:多类型匹配 - 需要一个"夹具"和一个"治具"类型的工装(应该只出库P001或P002)
        List<Fixture> requiredFixtures5 = Arrays.asList(
                createFixture(null, null, "模具"),
                createFixture(null, null, "治具"),
                createFixture(null, null, "其他")
        );
        List<String> requiredMaterials5 = Arrays.asList();

        System.out.println("\n测试场景5:多类型匹配");
        List<Pallet> result5 = service.optimizeOutbound(inventory, requiredFixtures5, requiredMaterials5);
        System.out.println("出库托盘数量: " + result5.size());
        for (Pallet p : result5) {
            System.out.println(p);
        }

        // 测试场景6:
        List<String> requiredMaterials6 = Arrays.asList("M001","M002");

        System.out.println("\n测试场景6:单个辅料");
        List<Pallet> result6 = service.optimizeOutbound(inventory, new ArrayList<>(), requiredMaterials6);
        System.out.println("出库托盘数量: " + result6.size());
        for (Pallet p : result6) {
            System.out.println(p);
        }
    }

    /**
     * 创建工装对象
     */
    private static Fixture createFixture(String code, String model, String type) {
        Fixture fixture = new Fixture();
        fixture.setCode(code);
        fixture.setModel(model);
        fixture.setType(type);
        return fixture;
    }

Write your comment Here