概率分析和随机算法(1)——算法导论(5)

1. 雇用问题

(1) 提出问题

    你的老板让你为公司雇用一名程序员,现在有n个人投了简历。你每天会随机的从这n份简历中挑选一份,然后让对应的投简历者过来面试,每次面试都将花费C1。而你的雇用原则是:如果当前面试的程序员比目前的程序员优秀,那么你就辞掉目前的程序员,而花高价C2去聘请面试的这位程序员。你的老板愿意为该策略付费,但是想让你估算一下该费用会是多少。

下面是这种策略的伪代码:

image

 

 

 

 

 

 

(2) 不科学的算法

    因为不管最优秀的面试者出现在哪一个位置,我们总是要面试完所有人,因此面试的费用是固定的,为C1*n;假设在这n次面试中,需要雇佣m个人,那么你将花费C2 * m雇佣费。因此总费用w为:  w = C1*n +  C2 * m。

   你可能认为最好的情况是:最优秀的面试者出现在第一位,m=1;最坏的情况是:最优秀的面试者出现在最后一位,m=n。因此平均情况是,m = (n+1)/ 2。这种简单的由上下限取平均的做法在这里是不科学的,因为总费用m的分布并不是均匀的(从下面可以看出)。

2. 概率分析

我们考虑平均要雇用的人数m。设随机变量Xi表示第i名面试的人是否被雇用(若被雇佣Xi为1,否则为0)。

image

那么image因此m的期望E(m) 为:

 

image

因此平均将花费:w = C1*n + C2 * E(m) = O(ln n),这比最坏情况下的雇用费用O(n)有了很大的改进。

3. 随机算法

在上面,我们通过对m的分布分析出平均情况,但是在很多时候,我们是无法得知输入分布信息的。但我们也许可以设计一个随机算法。

针对上面的雇用问题,我们可以在算法运行前先随机地排列应聘者,以加强所有的排列都是等可能出现的。

下面是随机算法的伪代码描述:

     image

    现在我们关心的重点在于如何生成一个随机的排列。不失一般性,我们假定一个数组A,包含1~n 这n个元素,我们的目标是构造出数组A的随机数组。下面介绍两种算法:

    方法① :我们先构造一个长度为n的数组P,数组P中的元素是介于1到n³的随机数。然后我们把数组P中的元素作为数组A中对应位置元素的优先级,数组A中的元素按照优先级大小进行排列(优先级相等的随便排列)。例如,A={1, 2, 3, 4}, P = {13,1, 60, 28}(数组P说明 数组A中,1的优先级是13,2的优先级是1,…),那么排列后的A={2,1,4,3}(按优先级从小到大排列)。伪代码如下:

image

至于排序算法,举不胜数,以后会专门讨论。

证明略(有兴趣的可以自己去看原书)。

    方法②:比较简单,直接贴出伪代码:

image

证明略。

下面给出这两种随机数组的生成算法的Java实现代码:

public static void main(String[] args) {
	printArray(permuteBySort(10));
	printArray(randomizeInPlace(10));
}

/**
 * 生成随机数组(方法1)
 * 
 * @param length
 *            数组规模
 * @return
 */
private static int[] permuteBySort(int length) {
	int[] a = new int[length];
	for (int i = 0; i < length; i++) {
		a[i] = i + 1;
	}
	Random random = new Random(System.currentTimeMillis());
	int[] p = new int[length];
	for (int i = 0; i < p.length; i++) {
		// 注意这里强制类型转换很可能会丢失数据
		p[i] = random.nextInt((int) Math.pow(length, 3)) + 1;
	}
	// 冒泡排序法
	for (int i = 0; i < p.length; i++) {
		for (int j = 0; j < p.length - 1 - i; j++) {
			if (p[j] > p[j + 1]) {
				int temp = p[j];
				p[j] = p[j + 1];
				p[j + 1] = temp;
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
	}
	return a;
}

/**
 * 生成随机数组(方法2)
 * 
 * @param length
 *            数组规模
 * @return
 */
private static int[] randomizeInPlace(int length) {
	int[] a = new int[length];
	for (int i = 0; i < length; i++) {
		a[i] = i + 1;
	}
	Random random = new Random(System.currentTimeMillis());
	for (int i = 0; i < a.length; i++) {
		int swapIndex = random.nextInt(a.length - i) + i;
		int temp = a[i];
		a[i] = a[swapIndex];
		a[swapIndex] = temp;
	}
	return a;
}

/**
 * 打印数组
 * 
 * @param a
 */
private static void printArray(int[] a) {
	for (int i : a) {
		System.out.print(i + "");
	}
	System.out.println();
}
posted @ 2015-09-14 18:56  学数学的程序猿  阅读(1847)  评论(0编辑  收藏  举报