0

0

Java中处理全排列:模拟雇佣问题与概率计算的正确姿势

碧海醫心

碧海醫心

发布时间:2025-08-18 21:22:22

|

393人浏览过

|

来源于php中文网

原创

Java中处理全排列:模拟雇佣问题与概率计算的正确姿势

本文深入探讨了在Java中如何正确生成并逐一处理数组的所有排列,以解决一个经典的模拟雇佣问题,并计算特定事件(如雇佣恰好两次)的概率。文章分析了原始代码中将排列扁平化处理的常见错误,并提供了基于 List> 结构,逐个遍历并应用业务逻辑的正确解决方案,确保概率计算的准确性。

1. 引言:排列组合与概率计算

在计算机科学和算法设计中,我们经常需要处理数据的各种排列。一个典型的应用场景是模拟实验或计算概率。本教程将以一个经典的“雇佣问题”为例,展示如何生成一个集合的所有排列,并对每个排列应用特定的逻辑,最终计算出满足特定条件的排列所占的比例(即概率)。

问题的核心在于:给定一组按排名顺序的候选人(例如1到n),我们按随机顺序面试他们。每面试一个候选人,如果他比之前面试过的所有候选人都优秀(排名更低),我们就雇佣他。我们的目标是计算在所有可能的面试顺序(即所有排列)中,恰好雇佣了两次的概率。

原始代码尝试通过生成所有排列来解决此问题,但在将这些排列传递给核心的雇佣逻辑时,出现了一个常见的错误:将所有排列扁平化处理,导致无法对单个排列进行独立分析。

2. 核心组件与雇佣策略解析

为了理解并修正代码,我们首先分析其核心组件:

2.1 hireAssistant1 方法:模拟雇佣过程

这是模拟雇佣逻辑的关键方法。它接收一个 int[] 数组作为候选人序列(一个排列),并返回雇佣的次数。

立即学习Java免费学习笔记(深入)”;

public static int hireAssistant1(int[] arr, int n) {
    ArrayList hired = new ArrayList();
    // 第一个候选人总是被雇佣
    int best = arr[0];
    hired.add(best);
    // 遍历后续候选人
    for (int i = 1; i < n; i++) {
        // 如果当前候选人比目前为止的最佳候选人更优秀(排名更低),则雇佣
        if (arr[i] < best) {
            best = arr[i];
            hired.add(best);
        }
    }
    return hired.size(); // 返回雇佣的总人数
}

该方法的核心思想是:始终雇佣第一个面试的候选人,之后只雇佣比当前已雇佣的最佳候选人更优秀的候选人。

2.2 permute 方法:生成所有排列

此方法及其辅助方法 permuteHelper 负责生成给定数组的所有可能排列。它返回一个 List>,其中每个内部 List 代表一个独立的排列。

public List> permute(int[] arr) {
    List> list = new ArrayList<>();
    permuteHelper(list, new ArrayList<>(), arr);
    return list;
}

private void permuteHelper(List> list, List resultList, int[] arr) {
    // 递归终止条件:当resultList的长度等于原始数组长度时,表示一个完整的排列已生成
    if (resultList.size() == arr.length) {
        list.add(new ArrayList<>(resultList)); // 将当前排列添加到结果列表中
    } else {
        // 遍历原始数组中的每个元素
        for (int i = 0; i < arr.length; i++) {
            // 如果当前元素已在resultList中,则跳过(避免重复)
            if (resultList.contains(arr[i])) {
                continue;
            }
            resultList.add(arr[i]); // 将元素添加到当前排列中
            permuteHelper(list, resultList, arr); // 递归调用
            resultList.remove(resultList.size() - 1); // 回溯:移除最后一个元素,尝试其他路径
        }
    }
}

2.3 辅助方法

  • makeArray(int n): 创建一个包含 1 到 n 的有序数组。
  • factorial(int n): 计算 n 的阶乘,用于确定总排列数。
  • toIntArray(List list): 将 List 转换为 int[]。
  • listToList(List> list): 这是一个有问题的辅助方法,它将 List> 扁平化为一个 List

3. 原始代码的问题剖析:扁平化陷阱

原始 main 方法中的核心问题在于对 permute 方法返回结果的处理:

Anyword
Anyword

AI文案写作助手和文本生成器,具有可预测结果的文案 AI

下载
public static void main(String[] args) {
    Assignment8 pa = new Assignment8();
    int n = 6;
    List> p = pa.permute(makeArray(n)); // p 包含所有独立的排列
    List list = listToList(p); // 错误:将所有排列扁平化为一个大列表
    System.out.println("N = 6");
    methodThreePerm(list, n); // 将扁平化列表传递给处理方法
}

这里的 listToList(p) 调用是问题的根源。例如,如果 n=3,permute 可能会返回 [[1,2,3], [1,3,2], [2,1,3], ...]。经过 listToList 后,list 可能会变成 [1,2,3,1,3,2,2,1,3,...]。

随后,methodThreePerm 接收这个扁平化的 List,并将其转换为一个巨大的 int[] arr。当 hireAssistant1(arr, n) 被调用时,它会将这个巨大的数组当作一个单一的、长度为 n 的序列来处理,但实际上这个数组的长度远大于 n,且包含了多个排列的拼接。这导致 hireAssistant1 无法正确地模拟单个排列的雇佣过程,从而使最终的概率计算结果完全错误。

hireAssistant1 方法期望的输入是一个独立的排列(例如 [1,2,3] 或 [3,1,2]),而不是所有排列拼接在一起的巨大序列。

4. 解决方案:逐个排列处理

解决这个问题的关键在于,我们应该直接利用 permute 方法返回的 List> 结构,逐个遍历每个独立的排列,并对其应用 hireAssistant1 逻辑。

正确的处理流程如下:

  1. 生成所有排列,得到 List>。
  2. 初始化一个计数器 hiresTwoCount,用于统计雇佣次数恰好为2的排列数量。
  3. 遍历 List> 中的每一个内部 List(即每一个独立的排列)。
  4. 对于每一个独立的排列:
    • 将其转换为 int[] 数组。
    • 调用 hireAssistant1 方法,获取该排列的雇佣次数。
    • 如果雇佣次数等于2,则 hiresTwoCount 加1。
  5. 计算最终概率:hiresTwoCount / factorial(n)。

5. 代码实现与验证

以下是修正后的 main 方法示例,它直接在 permute 的结果上进行迭代,确保每个排列都被正确处理:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors; // For listToList, though it's removed in solution

public class PermutationHiring {

    // 辅助方法:创建包含1到n的数组
    public static int[] makeArray(int n) {
        int arr[] = new int[n];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i + 1;
        }
        return arr;
    }

    // 雇佣模拟方法
    public static int hireAssistant1(int[] arr, int n) {
        // ArrayList hired = new ArrayList(); // 实际上不需要存储被雇佣者,只需计数
        int hires = 0;
        if (n == 0) return 0; // 空数组没有雇佣
        if (n == 1) return 1; // 只有一个候选人,必然雇佣一次

        int best = arr[0];
        hires = 1; // 第一个候选人总是被雇佣

        for (int i = 1; i < n; i++) {
            if (arr[i] < best) {
                best = arr[i];
                hires++;
            }
        }
        return hires;
    }

    // 计算阶乘
    public static int factorial(int n) {
        if (n < 0) throw new IllegalArgumentException("Factorial is not defined for negative numbers.");
        if (n == 0 || n == 1) return 1;
        int result = 1;
        for (int i = 2; i <= n; i++) {
            result *= i;
        }
        return result;
    }

    // List 转换为 int[]
    static int[] toIntArray(List list) {
        int[] ret = new int[list.size()];
        for (int i = 0; i < ret.length; i++)
            ret[i] = list.get(i);
        return ret;
    }

    // 生成所有排列的核心逻辑
    public List> permute(int[] arr) {
        List> list = new ArrayList<>();
        permuteHelper(list, new ArrayList<>(), arr);
        return list;
    }

    private void permuteHelper(List> list, List resultList, int[] arr) {
        if (resultList.size() == arr.length) {
            list.add(new ArrayList<>(resultList));
        } else {
            for (int i = 0; i < arr.length; i++) {
                if (resultList.contains(arr[i])) {
                    continue;
                }
                resultList.add(arr[i]);
                permuteHelper(list, resultList, arr);
                resultList.remove(resultList.size() - 1);
            }
        }
    }

    public static void main(String[] args) {
        PermutationHiring ph = new PermutationHiring(); // 实例化类以调用非静态方法
        int n = 6;
        // 生成所有排列
        List> allPermutations = ph.permute(makeArray(n));

        int hiresTwoCount = 0; // 统计雇佣次数恰好为2的排列数
        int totalPermutations = factorial(n); // 总排列数

        // 遍历每一个独立的排列
        for (List permutation : allPermutations) {
            int[] currentPermutationArray = toIntArray(permutation);
            int hires = hireAssistant1(currentPermutationArray, n);
            if (hires == 2) {
                hiresTwoCount++;
            }
        }

        double probability = (double) hiresTwoCount / totalPermutations;
        System.out.println("N = " + n);
        System.out.println("雇佣次数恰好为2的排列数: " + hiresTwoCount);
        System.out.println("总排列数 (n!): " + totalPermutations);
        System.out.println("雇佣次数恰好为2的概率: " + probability);

        // 原始答案中提供的Method 1,用于计算不同的概率(例如,期望雇佣次数的某个变体)
        // 注意:这个方法计算的结果与“雇佣恰好两次”的概率不同。
        // static void methodOne

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

841

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

742

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

738

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

399

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号