You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求在jqwik属性测试框架中生成图边列表的更优方法

现有实现优化建议

1. 实现逐顶点生成边再合并的需求

你需要的列表拼接逻辑不需要依赖Combine组件的固定值组合,通过Arbitrary.sequence即可实现,逐顶点生成边的结构扩展性也更强:

@Provide
Arbitrary<List<Tuple.Tuple3<Integer,Integer,Integer>>> edgeLists(TypeUsage type, ArbitraryProvider.SubtypeProvider subtype) {
    // 先解析参数,抽成单独的参数对象更清晰
    FlowGenConfig config = parseConfig(type);
    
    // 为每个顶点生成对应出度的边列表Arbitrary
    List<Arbitrary<List<Tuple.Tuple3<Integer,Integer,Integer>>>> perVertexEdges = IntStream.rangeClosed(1, config.vertices)
        .mapToObj(sourceId -> {
            // 先生成当前顶点的出度
            Arbitrary<Integer> degree = Arbitraries.integers().between(config.degreeMin, config.degreeMax);
            // 生成对应数量的目标顶点+流量,此处可直接加禁止自环、禁止重复边等约束
            return degree.flatMap(d -> Combinators.combine(
                Arbitraries.integers().between(1, config.vertices).filter(v -> v != sourceId).list().ofSize(d),
                Arbitraries.integers().between(config.minEdgeFlow, config.maxEdgeFlow).list().ofSize(d)
            ).as((targets, flows) -> IntStream.range(0, d)
                .mapToObj(i -> Tuple.of(sourceId, targets.get(i), flows.get(i)))
                .toList()
            ));
        }).toList();
    
    // 合并所有顶点的出边列表生成全局边列表
    return Arbitraries.sequence(perVertexEdges)
        .map(listOfLists -> listOfLists.stream().flatMap(List::stream).toList());
}

// 把参数解析抽成单独方法,逻辑更清晰
private FlowGenConfig parseConfig(TypeUsage type) {
    int vertices = 10;
    int degreeMin = 1;
    int degreeMax = 4;
    int minEdgeFlow = 1;
    int maxEdgeFlow = 10;
    for (Annotation a : type.getAnnotations()) {
        if (a instanceof MaxFlowParameters params) {
            vertices = Math.max(1, params.vertices());
            degreeMin = Math.max(1, params.degree_min());
            degreeMax = Math.min(vertices, Math.max(degreeMin, params.degree_max()));
            minEdgeFlow = Math.min(vertices, Math.max(0, params.min_edge_flow()));
            maxEdgeFlow = Math.min(vertices, Math.max(minEdgeFlow, params.max_edge_flow()));
        }
    }
    return new FlowGenConfig(vertices, degreeMin, degreeMax, minEdgeFlow, maxEdgeFlow);
}

// 单独存参数的record,Java 16以下版本替换为普通类即可
record FlowGenConfig(int vertices, int degreeMin, int degreeMax, int minEdgeFlow, int maxEdgeFlow) {}

这种结构后续新增约束非常方便,只需要修改对应顶点的边生成逻辑即可,不需要调整全局拼接逻辑。

2. 简化现有嵌套flatMap

如果要保留你原来的生成逻辑,也可以用Combinators.combine替换三层嵌套flatMap,提升可读性:

return Arbitraries.integers().between(degree_min, degree_max).list().ofSize(vertices).map(expand)
    .flatMap(sources -> Combinators.combine(
        Arbitraries.integers().between(1, num_vertices).list().ofSize(sources.size()),
        Arbitraries.integers().between(the_min_edge_flow, the_max_edge_flow).list().ofSize(sources.size())
    ).as((targets, flows) -> IntStream.range(0, sources.size())
        .mapToObj(i -> Tuple.of(sources.get(i), targets.get(i), flows.get(i)))
        .toList()
    ));

3. 测试用例优化

  • searchOrdersEqual方法中Collections.shuffle会直接修改输入的edgeList,每次打乱都基于上一次的结果,建议每次循环都复制一份原列表再打乱,测试逻辑更严谨:
List<Tuple.Tuple3<Integer,Integer,Integer>> original = new ArrayList<>(edgeList);
for (int i = 0; i < 10; i++) {
    List<Tuple.Tuple3<Integer,Integer,Integer>> shuffled = new ArrayList<>(original);
    Collections.shuffle(shuffled, random);
    Graph<String,IntegerFlow> graph = new GraphBuilder().withEdges(shuffled).build();
    // 剩余校验逻辑不变
}
  • validMinCutCandidate的割值计算可以用stream简化,减少嵌套循环的出错概率。

4. 复杂约束扩展建议

如果需要支持弱连通、无环等更复杂的图结构约束,可以在边列表生成完成后加全局filter,或者自定义生成器实现,比在生成过程中硬编码约束要灵活很多。


内容的提问来源于stack exchange,提问作者FeralWhippet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 01:45:07