[原创]10个人都不坐在自己座位上有多少种可能机率多大



从郑州出差回来,在高铁上,项目经理突然问我这个问题,我们讨论了很久,试了各种方法,也没有得出一个能够通用的计算结果的公式,但是却先答道了答案。
想我堂堂程序员,比不过数学家,但是写个程序求结果还是非常非常容易的。
下面一起来看验证结果的过程。
直接贴代码。

答案是 当人数趋向于无穷大时,结果是 自然数E的倒数 = 1/e

以下是java程序验证。

package com.weiwo.seat;

import java.util.ArrayList;
import java.util.List;

/**
 * 如果一辆车上有10个人(不包括司机和售票员),10个座位,中途休息时10个人都下车了,休息结束时,10个人回到车上。请问,
 * 这10个人都不坐在自己座位上有多少种可能(或者求机有多大)?经理说答案是 自然数E的倒数 = 1/e
 * 
 * @author peaches
 * @date 2015-2-6下午10:57:21
 */
public class Seat {
	public static void main(String[] args) {
		for (int i = 1; i < 11; i++) {
			Seat seat = new Seat();
			seat.peopleNum = i;
			seat.execute();
		}
	}

	private int num = 1;
	public int peopleNum = 5;
	private List<String> peopleList = new ArrayList<String>();// 人
	private List<String> seatList = new ArrayList<String>();// 座位,每个座位默认对应一个人
	private List<List<String>> newPeopleSeatList = new ArrayList<List<String>>();// 排列组合结果

	public void execute() {
		for (int i = 1; i < peopleNum + 1; i++) {
			String people = String.valueOf(i);
			peopleList.add(people);
			seatList.add(people);
		}
		// System.out.println(Arrays.toString(peopleList.toArray()));
		// System.out.println(Arrays.toString(seatList.toArray()));
		generateList(0);
		valid();
	}

	/**
	 * 验证排列组合,只保留所有的都不在原来座位上的。删除任何存在“在自己座位上的排列组合”
	 */
	private void valid() {
		int count = newPeopleSeatList.size();// 所有排列总数
		System.out.println("总人数:" + peopleList.size());
		for (int i = 0; i < newPeopleSeatList.size(); i++) {
			List<String> tempList = newPeopleSeatList.get(i);
			if (!validList(tempList)) {
				newPeopleSeatList.remove(i);
				i--;
			}
		}
		// System.out.println(num++ + ":" +
		// Arrays.toString(newPeopleSeatList.toArray()));
		System.out.println("所有人均不在自己座位上的可能数量:" + newPeopleSeatList.size());
		System.out.println("所有人均不在自己座位上的可能机率:" + String.format("%.3f%%", newPeopleSeatList.size() * 1.0 / count * 100));
		System.out.println("----------------------------------------------------------------");
	}

	private boolean validList(List newList) {
		for (int i = 0; i < seatList.size(); i++) {
			if (newList.get(i).equals(seatList.get(i))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 生成所有的可能的组合
	 * 
	 * 程序的主要思路是: 1.把第1个数换到最前面来(本来就在最前面),准备打印1xx,再对后两个数2和3做全排列。
	 * 2.把第2个数换到最前面来,准备打印2xx,再对后两个数1和3做全排列。 3.把第3个数换到最前面来,准备打印3xx,再对后两个数1和2做全排列。
	 * 
	 * 可见这是一个递归的过程,把对整个序列做全排列的问题归结为对它的子序列做全排列的问题,注意我没有描述Base Case怎么处理,你需要自己想。
	 * 你的程序要具有通用性,如果改变了N和数组a的定义(比如改成4个数的数组),其它代码不需要修改就可以做4个数的全排列(共24种排列)。
	 * 
	 * 完成了上述要求之后再考虑第二个问题:如果再定义一个常量M表示从N个数中取几个数做排列(N==M时表示全排列),原来的程序应该怎么改?
	 * 
	 * 最后再考虑第三个问题:如果要求从N个数中取M个数做组合而不是做排列,就不能用原来的递归过程了,想想组合的递归过程应该怎么描述,编程实现它。
	 */
	public void generateList(int k) {
		if (k == peopleNum) {
			List<String> tempList = new ArrayList<String>();
			tempList.addAll(peopleList);
			newPeopleSeatList.add(tempList);
			// System.out.println(num++ + ":" +
			// Arrays.toString(newPeopleSeatList.toArray()));
		} else {
			for (int i = k; i < peopleNum; ++i) {
				swap(i, k);// 交换前缀
				generateList(k + 1);// 递归
				swap(i, k);// 将前缀换回来,继续做前一次排列
			}
		}
	}

	public void swap(int i, int offset) {
		String tempI = peopleList.get(i);
		String tempOffset = peopleList.get(offset);
		peopleList.remove(i);
		peopleList.add(i, tempOffset);
		peopleList.remove(offset);
		peopleList.add(offset, tempI);
	}
}


最终计算结果是
总人数:1
所有人均不在自己座位上的可能数量:0
所有人均不在自己座位上的可能机率:0.000%
----------------------------------------------------------------
总人数:2
所有人均不在自己座位上的可能数量:1
所有人均不在自己座位上的可能机率:50.000%
----------------------------------------------------------------
总人数:3
所有人均不在自己座位上的可能数量:2
所有人均不在自己座位上的可能机率:33.333%
----------------------------------------------------------------
总人数:4
所有人均不在自己座位上的可能数量:9
所有人均不在自己座位上的可能机率:37.500%
----------------------------------------------------------------
总人数:5
所有人均不在自己座位上的可能数量:44
所有人均不在自己座位上的可能机率:36.667%
----------------------------------------------------------------
总人数:6
所有人均不在自己座位上的可能数量:265
所有人均不在自己座位上的可能机率:36.806%
----------------------------------------------------------------
总人数:7
所有人均不在自己座位上的可能数量:1854
所有人均不在自己座位上的可能机率:36.786%
----------------------------------------------------------------
总人数:8
所有人均不在自己座位上的可能数量:14833
所有人均不在自己座位上的可能机率:36.788%
----------------------------------------------------------------
总人数:9
所有人均不在自己座位上的可能数量:133496
所有人均不在自己座位上的可能机率:36.788%
----------------------------------------------------------------
总人数:10

人数10一直没有算出来,10!=3628800

我写的这个只是用于验证,并没有做优化,算法的时间和空间复杂度比较高,10! 会非常耗时,大约执行了30你分钟也没有执行出来结果……

有觉得有兴趣的朋友可以优化下。


----------------------------------------------------------------------------------------------------------------------------------------------

2015-02-11

优化后的代码



优化后的代码,可以算出10个人的时候的几率,这也只能算出来10个人,11个就内存溢出了,可能是我设置的不够大,但是也说明这不是一个很好的算法。
[code=java]package com.Test;


import java.util.ArrayList;
import java.util.List;


/**
* 如果一辆车上有10个人(不包括司机和售票员),10个座位,中途休息时10个人都下车了,休息结束时,10个人回到车上。请问, 这10个人都不坐在自己座位上有多少种可能(或者求机有多大)?经理说答案是 自然数E的倒数 = 1/e

* @author peaches
* @date 2015-2-6下午10:57:21
*/
public class TestSeat {
        public static void main(String[] args) {
                for (int i = 0; i < 11; i++) {
                        long startTime = System.currentTimeMillis();
                        TestSeat seat = new TestSeat();
                        seat.peopleNum = i;
                        seat.execute();
                        System.out.println("执行时间" + (System.currentTimeMillis() - startTime));
                }
        }


        private final int num = 1;
        public int peopleNum = 5;
        private final List<String> peopleList = new ArrayList<String>();// 人
        private final List<String> seatList = new ArrayList<String>();// 座位,每个座位默认对应一个人
        private final List<List<String>> newPeopleSeatList = new ArrayList<List<String>>();// 排列组合结果


        public void execute() {
                for (int i = 1; i < peopleNum + 1; i++) {
                        String people = String.valueOf(i);
                        peopleList.add(people);
                        seatList.add(people);
                }
                // System.out.println(Arrays.toString(peopleList.toArray()));
                // System.out.println(Arrays.toString(seatList.toArray()));
                generateList(0);
                valid();
        }


        /**
         * 验证排列组合,只保留所有的都不在原来座位上的。删除任何存在“在自己座位上的排列组合”
         */
        private void valid() {
                List<List<String>> resultPeopleSeatList = new ArrayList<List<String>>();// 排列组合结果
                int count = newPeopleSeatList.size();// 所有排列总数
                System.out.println("总人数:" + peopleList.size());
                for (int i = newPeopleSeatList.size() - 1; i > 0; i--) {
                        List<String> tempList = newPeopleSeatList.get(i);
                        if (validList(tempList)) {
                                resultPeopleSeatList.add(newPeopleSeatList.get(i));
                        }
                        newPeopleSeatList.remove(i);
                }
                // System.out.println(num++ + ":" +
                // Arrays.toString(newPeopleSeatList.toArray()));
                System.out.println("所有人均不在自己座位上的可能数量:" + resultPeopleSeatList.size());
                System.out.println("所有人均不在自己座位上的可能机率:" + String.format("%.3f%%", resultPeopleSeatList.size() * 1.0 / count * 100));
                System.out.println("----------------------------------------------------------------");
        }


        private boolean validList(List newList) {
                for (int i = 0; i < seatList.size(); i++) {
                        if (newList.get(i).equals(seatList.get(i))) {
                                return false;
                        }
                }
                return true;
        }


        /**
         * 生成所有的可能的组合
         * 
         * 程序的主要思路是: 1.把第1个数换到最前面来(本来就在最前面),准备打印1xx,再对后两个数2和3做全排列。 2.把第2个数换到最前面来,准备打印2xx,再对后两个数1和3做全排列。 3.把第3个数换到最前面来,准备打印3xx,再对后两个数1和2做全排列。
         * 
         * 可见这是一个递归的过程,把对整个序列做全排列的问题归结为对它的子序列做全排列的问题,注意我没有描述Base Case怎么处理,你需要自己想。 你的程序要具有通用性,如果改变了N和数组a的定义(比如改成4个数的数组),其它代码不需要修改就可以做4个数的全排列(共24种排列)。
         * 
         * 完成了上述要求之后再考虑第二个问题:如果再定义一个常量M表示从N个数中取几个数做排列(N==M时表示全排列),原来的程序应该怎么改?
         * 
         * 最后再考虑第三个问题:如果要求从N个数中取M个数做组合而不是做排列,就不能用原来的递归过程了,想想组合的递归过程应该怎么描述,编程实现它。
         */
        public void generateList(int k) {
                if (k == peopleNum) {
                        List<String> tempList = new ArrayList<String>();
                        tempList.addAll(peopleList);
                        newPeopleSeatList.add(tempList);
                        // System.out.println(num++ + ":" +
                        // Arrays.toString(newPeopleSeatList.toArray()));
                } else {
                        for (int i = k; i < peopleNum; ++i) {
                                swap(i, k);// 交换前缀
                                generateList(k + 1);// 递归
                                swap(i, k);// 将前缀换回来,继续做前一次排列
                        }
                }
        }


        public void swap(int i, int offset) {
                String tempI = peopleList.get(i);
                String tempOffset = peopleList.get(offset);
                peopleList.remove(i);
                peopleList.add(i, tempOffset);
                peopleList.remove(offset);
                peopleList.add(offset, tempI);
        }
}[/code]


执行结果


总人数:0
所有人均不在自己座位上的可能数量:0
所有人均不在自己座位上的可能机率:0.000%
----------------------------------------------------------------
执行时间99
总人数:1
所有人均不在自己座位上的可能数量:0
所有人均不在自己座位上的可能机率:0.000%
----------------------------------------------------------------
执行时间0
总人数:2
所有人均不在自己座位上的可能数量:1
所有人均不在自己座位上的可能机率:50.000%
----------------------------------------------------------------
执行时间1
总人数:3
所有人均不在自己座位上的可能数量:2
所有人均不在自己座位上的可能机率:33.333%
----------------------------------------------------------------
执行时间8
总人数:4
所有人均不在自己座位上的可能数量:9
所有人均不在自己座位上的可能机率:37.500%
----------------------------------------------------------------
执行时间0
总人数:5
所有人均不在自己座位上的可能数量:44
所有人均不在自己座位上的可能机率:36.667%
----------------------------------------------------------------
执行时间3
总人数:6
所有人均不在自己座位上的可能数量:265
所有人均不在自己座位上的可能机率:36.806%
----------------------------------------------------------------
执行时间15
总人数:7
所有人均不在自己座位上的可能数量:1854
所有人均不在自己座位上的可能机率:36.786%
----------------------------------------------------------------
执行时间92
总人数:8
所有人均不在自己座位上的可能数量:14833
所有人均不在自己座位上的可能机率:36.788%
----------------------------------------------------------------
执行时间253
总人数:9
所有人均不在自己座位上的可能数量:133496
所有人均不在自己座位上的可能机率:36.788%
----------------------------------------------------------------
执行时间1175
总人数:10
所有人均不在自己座位上的可能数量:1334961
所有人均不在自己座位上的可能机率:36.788%
----------------------------------------------------------------
执行时间5210


一楼台的,10个人,执行了40分钟没有结果。现在的,平均执行时间5s左右,根据电脑不同会有不同。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值