雇用问题

情景:

猎头(代理)公司帮你物色办公助理候选人,面试一个候选人支付代理公司 1K。

下面的HIRE-ASSISTANT过程假设应聘办公助理的候选人编号为1到n。假设你能在面试完应聘者i后,决定应聘者i是否是你目前见过的最佳人选。初始化时,创建一个虚拟的应聘者,编号为0,他比其他所有应聘者都差。

def HireAssistant(a):
    best = 0
    count = 0
    for i in a:
        if i > best:
            best = i
            count += 1
    return count

概率分析:

对每一个候选人分配一个“名次”。概率分析的本质:需已知或假定输入的概率分布,依据概率分布来求期望,期望值即是平均 hire 的人数。

我们不知道输入的分布,也不可能为输入的分布进行可计算的建模,在算法中通过对输入进行随机化处理,从而为输入强加一种分布。

猎头公司预先提供 n 个候选人列表,每天,我们随机选取一人进行面试,无须担心候选人是否被随机提供,我们通过随机运算预处理可以控制候选人的随机顺序。

算法随机化:由随机数产生器生成数值,RANDOM实际上由一个确定的算法〔伪随机产生器〕产生,其结果表面上看上去像是随机数。

指示随机变量:

E[X]=E[\sum_{i=1}^{n}X_{i}]=\sum_{i=1}^{n}E[X_{i}]=\sum_{i=1}^{n}\frac{1}{i}=lnn

尽管我们面试了n个人,但平均起来,实际上大约只雇用他们之中的lnn个人。用下面的引理来总结这个结果:假设应聘者以随机次序出现,算法HIRE-ASSISTANT总的雇用费用平均情形下为O(c_{h}lnn)

随机算法:

给定输入,则雇用的人数确定,雇用的人数〔资源消费〕依赖于输入。

随机化过程在算法中,而不是在输入中体现,算法的运行时间与输入无关,算法的最坏运算时间不取决于特定的输入,只有当随机数产生器产生很不幸运的数时,算法的运算时间最坏。

对于雇用问题,代码中唯一需要改变的是随机地变换应聘者序列。

def RandomizedHireAssistant(a):
    import random
    random.shuffle(a)
    best = 0
    count = 0
    for i in a:
        if i > best:
            best = i
            count += 1
    return count

对输入矩阵进行随机置换后,情况同HIRE-ASSISTANT相同。

随机排列数组:

1.PERMUTE-BY-SORTING

设数组 A = <1, 2, ... , n>,为每个元素 A[i] 分配一个随机数 P[i] 作为其优先权,然后依据这些优先权对数组 A 进行排序。

import random


def QuickSort(a, p):  # 以p为排序优先级排序a
    b = [[], []]
    b[0] = a
    b[1] = p
    if len(b[1]) >= 2:
        mid_p = b[1][int(len(b[1])/2)]
        mid_a = b[0][int(len(b[0])/2)]
        left_p, right_p = [], []
        left_a, right_a = [], []
        b[1].remove(mid_p)
        b[0].remove(mid_a)
        for i in range(len(b[1])):
            if b[1][i] >= mid_p:
                right_p.append(b[1][i])
                right_a.append(b[0][i])
            else:
                left_p.append(b[1][i])
                left_a.append(b[0][i])
        b[0] = QuickSort(left_a, left_p)[0] + [mid_a] + QuickSort(right_a, right_p)[0]
        b[1] = QuickSort(left_a, left_p)[1] + [mid_p] + QuickSort(right_a, right_p)[1]
        return b
    else:
        return [a, p]


def PermuteBySorting(a):
    n = len(a)
    P = [0]*n
    for i in range(len(a)):
        P[i] = random.random() * (n**3)
    return QuickSort(a, P)[0]

2.RANDOMIZE-IN-PLACE

import random


def RandomizeInPlace(a):  # # 随机排序数组
    n = len(a)
    for i in range(n):
        r = random.randint(i, n-1)
        a[i], a[r] = a[r], a[i]
    return a

第 i 次迭代后不再改变 A[i],仅需更小范围的随机数产生器,不需要辅助空间。

主程序:

雇用问题的代码变为:

from PermuteBySorting import PermuteBySorting  # 随机排序数组
from RandomizeInPlace import RandomizeInPlace

def RandomizedHireAssistant(a):
    # PermuteBySorting(a)
    RandomizeInPlace(a)
    best = 0
    count = 0
    for i in a:
        if i > best:
            best = i
            count += 1
    return count

主程序为:

import numpy as np
import math
import time
# from HireAssistant import HireAssistant
from RandomizedHireAssistant import RandomizedHireAssistant

n = 100
a = np.random.randint(1, 10*n, n)  # 构造一个范围在1-10*n,长度为n的整型数组

before = time.time()
s = 0
r = 100000  # 循环r次计算平均雇佣次数
for i in range(r):
    s += RandomizedHireAssistant(list(a))
after = time.time()

print(n, "个应聘者的平均雇佣次数:", round(s/r, 3))  # 平均雇佣次数
print("ln", n, "的理论值:", round(math.log(n, math.e), 3))  # 理论值lnn
print("运行时间:", round(after-before, 3), "s")

结果如下:

100 个应聘者的平均雇佣次数: 5.187
ln 100 的理论值: 4.605
运行时间: 222.125 s

雇用两次的概率:

练习5.2-2 在HIRE-ASSISTANT中,假设应聘者以随机顺序出现,你正好雇用两次的概率是多少?

设事件E为第一个位置实力为i,P(E) = 1/n,n>i>0,完全随机

设事件F为在E的条件下,不需要考虑排名低于i的应聘者,在所有的排名高于i的应聘者中,正好第一个是最好的应聘者,P(F | E) = 1/(n-i)

则P{雇佣两次} = P(F∩(E1∪E2∪...∪En-1) = (F∩E1)∪(F∩E2)∪...∪(F∩En-1)) = ∑P(F | E)*P(E) (i=1 to n-1)
假设一共有十个应聘者,则雇用两次的概率为:P=\frac{1}{10}*(\frac{1}{9}+\frac{1}{8}+...+1)\approx 0.28289

编写程序验证:

import numpy as np
import time
from RandomizedHire2Assistants import RandomizedHire2Assistants

n = 10
a = np.random.permutation(n)  # 构造一个范围在1-10,长度为n的整型数组

before = time.time()
s = 0
r = 100000  # 循环r次计算平均雇佣次数
for i in range(r):
    s += RandomizedHire2Assistants(list(a))
after = time.time()

p = 0
for i in range(1, n):
    p += 1/i
p /= n

print(n, "个应聘者仅雇用两次的频率:", s/r)  # 雇用两次的频率
print("仅雇用两次的概率理论值p:", p)  # 理论值
print("运行时间:", round(after-before, 3), "s")

其中RandomizedHire2Assistants函数计算列表a是否满足雇用两次,若满足则返回1,不满足返回0:

from RandomizeInPlace import RandomizeInPlace


def RandomizedHire2Assistants(a):
    RandomizeInPlace(a)
    best = a[0]
    count = 0
    if best == len(a)-1:
        return 0
    else:
        for i in range(1, len(a)):
            if a[i] > best:
                count += 1
            if a[i] == len(a)-1:
                if count == 1:
                    return 1
                else:
                    return 0

结果如下:

10 个应聘者仅雇用两次的频率: 0.28342
仅雇用两次的概率理论值p: 0.28289682539682537
运行时间: 2.805 s

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值