算法作业之概率算法&近似算法(中科大黄刘生总裁版)

Ex.1 若将y ← \leftarrow uniform(0,1)改为y ← \leftarrow x,则上述的算法估计的值是什么?

答:此时x和y的取值相等,都位于连接(0,0)和(1,1)的线段上。此时四分之一圆和边长为1的正方形,根据Monte Carlo,面积比变为1: 2 \sqrt2 2 。根据下列公式:

π ∗ 1 2 4 ∗ 1 2 = 1 2 \frac{\pi*1^{2}}{4*1^{2}}=\frac{1}{\sqrt{2}} 412π12=2 1

解得 π = 2 2 \pi=2\sqrt2 π=22 .

import random

n = 100000
cnt = 0
for i in range(n):
    x = random.random()
    y = x
    if x**2 + y**2 <= 1:
        cnt += 1

pi = 4*(cnt/n)
print(pi)
# n = 10, pi = 2.4
# n = 10万, pi = 2.8536
# n = 1000万, pi = 2.8296

Ex.2 在机器上用 4 ∫ 0 1 1 − x 2 d x 4\int_{0}^{1}\sqrt{1-x^{2}}dx 4011x2 dx,估计 π \pi π值,给出不同的n值及精度。

答:第一象限框定一个(0,0)到(1,1)的正方形,随机在该区域采样n个点,若某点坐标在曲线 y = 1 − x 2 y=\sqrt{1-x^{2}} y=1x2 下方,则count加一,最终 c o u n t / n count/n count/n ∫ 0 1 1 − x 2 d x \int_{0}^{1}\sqrt{1-x^{2}}dx 011x2 dx的面积与 1 ∗ 1 1*1 11正方形面积的比值.

import random
import math

def f(x):
    return math.sqrt(1-x**2)

n = 100000
cnt = 0
for i in range(n):
    x = random.random()
    y = random.random()

    if y < f(x):
        cnt += 1

S = cnt/n
pi = 4*S
print(pi)
# n = 10, pi = 2.8
# n = 1万, pi = 3.16
# n = 100万, pi = 3.1428
# n = 1亿, pi = 3.1417

Ex.3 设a,b, c和d是实数,且 a ≤ b , c ≤ d , f : [ a , b ] → [ c , d ] a\le b,c\le d, f:[a,b]\rightarrow [c,d] ab,cd,f:[a,b][c,d]是一个连续函数,写一概率算法计算积分 ∫ a b f ( x ) d x \int_{a}^{b}f(x)dx abf(x)dx.

答:设函数 f ( x ) = s i n x f(x)=sinx f(x)=sinx a = 0 , b = π , c = 0 , d = 1 a=0, b=\pi, c=0, d=1 a=0,b=π,c=0,d=1,理论上根据牛顿莱布尼茨公式:

∫ 0 π s i n x d x = [ − c o s ] 0 π = 2 \int_{0}^{\pi}sinxdx=[-cos]_{0}^{\pi}=2 0πsinxdx=[cos]0π=2

import random
import math

def f(x):
    return math.sin(x)

n = 1000000
cnt = 0

a = 0
b = math.pi
c = 0
d = 1

for i in range(n):
    x = random.random() * math.pi
    y = random.random()
    if y <= f(x):
        cnt += 1

S = math.pi*(cnt/n)
print(S)
# n = 10, S = 2.8274
# n = 1万, S = 2.0008
# n = 100万, S = 1.9988
# n = 1亿, S = 2.0002

可见当n值越大,计算出的面积与理论值越接近,但计算时间越长。

Ex.4 (p36)用上述算法,估计整数子集1-n的大小,并分析n对估计值的影响.

答:这种计算集合的势算法需要n足够大,且需要运行多次求平均后的结果才能接近于集合的势,下面代码分别是基于不同的n进行实验,每一个n对应运行20次求平均.

import numpy as np
import random
import math

n = 10000000
arr = np.arange(n)
tmp = []
flag = True
cnt = 0

while flag:
    r = random.randint(0, n-1)
    if r in tmp:
        break
    else:
        tmp.append(r)
        cnt += 1

result = 2*cnt**2/math.pi
print(result)

# n = 10, result = 14.53
# n = 10000, result = 7841.2
# n = 1000000, result = 1270204.1
# n = 100000000, result = 91275198.9

当n越大时,概率算法计算后的结果越接近实际。当然对于每个n来说,实验次数越多,其平均值也越接近于真实的n,这个消融实验本题没做要求。

Ex.5 (p54)分析dlogRH的工作原理,指出该算法相应的u和v.

答:Sherwood算法主要的思路是消除掉「某个样例造成最坏复杂度」的情况,包括以下三个步骤:

(1)预处理:通过u(x,r)将原实例x转化为随机实例y,r为一个随机数;

(2)采用确定算法对随机实例y进行求解,得到一个解;

(3)后处理:将上一步得到的解通过v(r, s)变换为原实例的解;

所以dlogRH算法先选择一个随机数r,先求模幂b,c ← \leftarrow ba mod p这一步相当于预处理并求出c,随后对这个随机变量c按照确定算法进行求解,得到结果y;最后return那一步即后处理,解出x。

u = ba mod p

v = (y-r) mod (p-1)

Ex.6(p67)写一Sherwood算法C,与算法A,B,D比较,给出实验结果。

直接上代码:

import numpy as np
import random
import math

class Solution:
    def __init__(self, n):
        self.arr = np.arange(n)
        self.ptr = np.empty_like(self.arr)
        self.pre = np.empty_like(self.arr)

        self.ptr[:-1] = self.arr[1:]
        self.ptr[-1] = self.arr[0]
        self.pre[1:] = self.arr[:-1]
        self.pre[0] = self.arr[-1]

        self.shuffle(n)
        self.head = 0
        for i in range(n):
            if self.arr[i] == 0:
                self.head = i
        self.cnt = 0


    def shuffle(self, n):       # shuffle the array and keep the order of ptr
        for i in range(n):
            # print(self.arr)
            # print(self.ptr)
            # print('++++++++++++++++++++')
            r = random.randint(i, n-1)
            self.arr[i], self.arr[r] = self.arr[r], self.arr[i]
            tmp_r = [self.ptr[r], self.pre[r]]
            tmp_i = [self.ptr[i], self.pre[i]]
            if i == r:
                continue
            elif r == self.ptr[i]:          # swap the order of index i and r (adjacent)
                self.ptr[self.pre[i]] = r
                self.pre[self.ptr[r]] = i
                self.ptr[i] = tmp_r[0]
                self.ptr[r] = i
                self.pre[i] = r
                self.pre[r] = tmp_i[1]
            elif i == self.ptr[r]:          # swap the order of index i and r (adjacent)
                self.ptr[self.pre[r]] = i
                self.pre[self.ptr[i]] = r
                self.ptr[i] = r
                self.ptr[r] = tmp_i[0]
                self.pre[i] = tmp_r[1]
                self.pre[r] = i
            else:                           # general situation
                self.pre[tmp_i[0]] = r
                self.ptr[tmp_i[1]] = r
                self.pre[tmp_r[0]] = i
                self.ptr[tmp_r[1]] = i
                self.pre[i] = tmp_r[1]
                self.ptr[i] = tmp_r[0]
                self.pre[r] = tmp_i[1]
                self.ptr[r] = tmp_i[0]


    def funcA(self, x, head):
        idx = head
        # cnt = 0
        while x > self.arr[idx]:
            idx = self.ptr[idx]
            self.cnt += 1
        return idx


    def funcB(self, x):
        n = len(self.arr)
        sqrtn = math.floor(math.sqrt(n)) + 1
        max_idx, max_val = self.head, 0
        for i in range(sqrtn):
            self.cnt += 1
            y = self.arr[i]
            if y > max_val and x > y:
                max_idx, max_val = i, y

        return self.funcA(x, max_idx)


    def funcC(self, x):
        n = len(self.arr)
        sqrtn = math.floor(math.sqrt(n)) + 1
        max_idx, max_val = self.head, 0
        for i in range(sqrtn):
            r = random.randint(0, n-1)
            self.cnt += 1
            y = self.arr[r]
            if y > max_val and x > y:
                max_idx, max_val = r, y

        return self.funcA(x, max_idx)


    def funcD(self, x):
        n = len(self.arr)
        r = random.randint(0, n-1)
        val = self.arr[r]
        # print(r, val)
        self.cnt += 1
        if x > val:
            return self.funcA(x, self.ptr[r])
        elif x < val:
            return self.funcA(x, self.head)
        else:
            return r



s = Solution(100000)
print(s.arr)
print(s.ptr)
x = 50000
# print(s.funcA(x, s.head))     # cnt == 50000 constant
# print(s.funcD(x))             # cnt = 35933 on avg (10 times)
# print(s.funcB(x))             # cnt = 654.2 on avg (10 times)
# print(s.funcC(x))             # cnt = 720 on avg (10 times)
# print(s.cnt)

实验结果:实验中令数组长度n=100000,数组为一个1-100000的整数序列,经过shuffle进行打乱,但ptr的顺序保持一致。实验中用四种算法搜寻x=50000这个元素,得到的平均查找次数如下:

A算法(确定算法),复杂度O(n),查找次数为常数50000;

B算法(确定算法),复杂度O( n \sqrt n n ),平均查找次数654.2(10次实验);

C算法(概率算法),复杂度O( n \sqrt n n ),平均查找次数720(10次实验);

D算法(概率算法),复杂度O(n),查找次数为常数35933(10次实验)。

可见当n的数量级具有一定规模时,O( n \sqrt n n )算法比O(n)算法效率高得太多。

======================================================

Ex.7(p77)证明:当放置(k+1)th皇后时,若有多个位置是开放的,则算法QueensLV选中其中任意位置的概率相等.

proof:对于最后一个(nb)可以放的皇后来说,其被选中的概率为1/nb;往前递推,前一个被选中的概率为1/(nb-1),但是其后续还有被替换掉的可能性,这个概率为(nb-1)/nb,因此倒数第二个成立元素最终得以保留的概率为1/(nb-1)*(nb-1)/nb=1/nb;反向一直推到第一个成立的元素,其被选中的概率为1,被替换掉的概率为 1 / 2 ∗ 2 / 3 ∗ . . . ∗ ( n b − 1 ) / n b = 1 / n b 1/2*2/3*...*(nb-1)/nb=1/nb 1/22/3...(nb1)/nb=1/nb。因此所有的open位置的概率相等,都等于1/nb。

Ex.8(p83)写一算法,求n=12-20时最优的StepVegas值.

time为100次实验每次都找到解的总时间,probability为100次实验得到的success的频率。

N=12(100次实验)

StepVegas time(ms) probability(%)
1 186 100
2 139 100
3 90 100
4 83 85
5 54 49
6 78 27
7 119 9
8 178 9
9 228 6
10 257 4
11 232 3
12 271 4

N=12时,由实验结果,当StepVegas=4 时时间和概率效果较好;

N=13(100次实验)

StepVegas time(ms) probability(%)
1 247 100
2 215 100
3 134 98
4 88 88
5 78 54
6 87 25
7 114 11
8 139 7
9 212 3
10 268 3
11 391 2
12 325 3
13 328 1

N=13时,由实验结果,当StepVegas=4 时时间和概率效果较好;

N=14(100次实验)

StepVegas time(ms) probability(%)
1 908 100
2 520 100
3 253 97
4 179 94
5 113 92
6 98 59
7 86 32
8 125 16
9 194 10
10 239 6
11 486 6
12 441 5
13 512 3
14 551 4

N=14时,由实验结果,当StepVegas=4,5 时时间和概率效果较好;

N=15(100次实验)

StepVegas time(ms) probability(%)
1 940 100
2 549 100
3 381 100
4 247 100
5 209 97
6 133 87
7 110 57
8 114 41
9 168 12
10 232 5
11 319 4
12 457 6
13 478 2
14 532 2
15 608 4

N=15时,由实验结果,当StepVegas=5,6 时时间和概率效果较好;

N=16(100次实验)

StepVegas time(ms) probability(%)
1 3993 100
2 1528 100
3 956 100
4 402 100
5 326 100
6 194 95
7 126 71
8 108 44
9 132 27
10 184 12
11 251 12
12 372 4
13 632 4
14 604 2
15 648 2
16 614 5

N=16时,由实验结果,当StepVegas=6,7 时时间和概率效果较好;

N=17(100次实验)

StepVegas time(ms) probability(%)
1 4441 100
2 1732 100
3 1316 100
4 704 100
5 484 100
6 295 98
7 228 94
8 128 77
9 146 44
10 155 18
11 243 12
12 415 4
13 511 6
14 659 2
15 810 1
16 882 1
17 704 2

N=17时,由实验结果,当StepVegas=7,8 时时间和概率效果较好;

N=18(100次实验)

StepVegas time(ms) probability(%)
1 14691 100
2 5718 100
3 3171 100
4 1497 100
5 814 100
6 435 100
7 358 97
8 198 82
9 151 65
10 166 31
11 192 19
12 304 7
13 439 5
14 577 8
15 732 5
16 852 3
17 929 4
18 877 1

N=18时,由实验结果,当StepVegas=7,8 时时间和概率效果较好;

N=19(100次实验)

StepVegas time(ms) probability(%)
1 9290 100
2 5183 100
3 3675 100
4 2893 100
5 1468 100
6 998 100
7 412 99
8 336 99
9 251 81
10 169 54
11 187 35
12 231 16
13 383 10
14 494 9
15 796 5
16 1213 7
17 1204 3
18 1369 4
19 1095 2

N=19时,由实验结果,当StepVegas=8,9 时时间和概率效果较好;

N=20(100次实验)

StepVegas time(ms) probability(%)
1 78801 100
2 27941 100
3 8371 100
4 5370 100
5 2573 100
6 998 100
7 694 99
8 562 98
9 268 96
10 247 75
11 202 50
12 235 34
13 281 16
14 453 7
15 583 3
16 866 7
17 1172 6
18 1128 5
19 1543 1
20 1501 2

N=20时,由实验结果,当StepVegas=9 时时间和概率效果较好;

综上所述,对于所有的N来说,最好的StepVegas的值位于[N/3,N/2]之间。

Ex.7(p77)证明:当放置(k+1)th皇后时,若有多个位置是开放的,则算法QueensLV选中其中任意位置的概率相等.

proof:对于最后一个(nb)可以放的皇后来说,其被选中的概率为1/nb;往前递推,前一个被选中的概率为1/(nb-1),但是其后续还有被替换掉的可能性,这个概率为(nb-1)/nb,因此倒数第二个成立元素最终得以保留的概率为1/(nb-1)*(nb-1)/nb=1/nb;反向一直推到第一个成立的元素,其被选中的概率为1,被替换掉的概率为 1 / 2 ∗ 2 / 3 ∗ . . . ∗ ( n b − 1 ) / n b = 1 / n b 1/2*2/3*...*(nb-1)/nb=1/nb 1/22/3...(nb1)/nb=1/nb。因此所有的open位置的概率相等,都等于1/nb。

Ex.8(p83)写一算法,求n=12-20时最优的StepVegas值.

代码如下:

import time
import random


class Solution:
    def __init__(self, n, sv):
        self.n = n
        self.k = 0
        self.diag45 = []
        self.diag135 = []
        self.col = []
        self.cnt = 0
        self.stepVegas = sv
        self.nb = 0
        self.flag = False
        self.flag2 = False


    def isOk(self, i):
        if i not in self.col and i-self.k+1 not in self.diag45 and i+self.k+1 not in self.diag135:
            return True
        else:
            return False


    def QueenDFS(self):
        if self.flag is True:
            return
        if self.k >= self.n:
            # print(self.col)
            self.flag = True
            # exit(1)
            return
        for i in range(self.n):
            self.cnt += 1
            if self.isOk(i):
                self.col.append(i)
                self.diag45.append(i-self.k+1)
                self.diag135.append(i+self.k+1)
                self.k += 1
                self.QueenDFS()
                self.col.pop()
                self.diag45.pop()
                self.diag135.pop()
                self.k -= 1


    def QueenLV(self):
        if self.k >= self.n:
            print(self.col)
            exit(1)
        self.nb = 0
        j = -1
        for i in range(self.n):
            if self.isOk(i):
                self.nb += 1
                r = random.randint(1, self.nb)
                if r == 1:
                    j = i

        if self.nb > 0:
            self.col.append(j)
            self.diag45.append(j-self.k+1)
            self.diag135.append(j+self.k+1)
            self.k += 1
            self.QueenLV()
            self.col.pop()
            self.diag45.pop()
            self.diag135.pop()
            self.k -= 1

        elif self.nb == 0:
            print('error')
            exit(1)


    def QueenComb(self):
        if self.flag2 is True:
            return
        if self.k >= self.stepVegas:
            self.QueenDFS()
            return
            # print(self.col)
            # exit(1)
        # print(self.k)
        self.nb = 0
        j = -1
        for i in range(self.n):
            if self.isOk(i):
                self.nb += 1
                r = random.randint(1, self.nb)
                if r == 1:
                    j = i

        if self.nb > 0:
            self.col.append(j)
            self.diag45.append(j-self.k+1)
            self.diag135.append(j+self.k+1)
            self.k += 1
            self.QueenComb()
            self.col.pop()
            self.diag45.pop()
            self.diag135.pop()
            self.k -= 1

        elif self.nb == 0:
            # print('error')
            self.flag2 = True
            # exit(1)


n = 17

for sv in range(1, n + 1):
    t1 = time.time()
    cnt = 0
    while cnt < 100:
        a = Solution(n, sv)
        a.QueenComb()
        if a.flag is True:
            cnt += 1
    t2 = time.time()
    # print(t2 - t1)

    cnt = 0
    for i in range(100):
        a = Solution(n, sv)
        a.QueenComb()
        if a.flag is True:
            cnt += 1
    print('StepVegas={}: time:{} cnt={}'.format(sv, t2-t1, cnt))

time为100次实验每次都找到解的总时间,probability为100次实验得到的success的频率。

N=12(100次实验)

StepVegas time(ms) probability(%)
1 186 100
2 139 100
3 90 100
4 83 85
5 54 49
6 78 27
7 119 9
8 178 9
9 228 6
10 257 4
11 232 3
12 271 4

N=12时,由实验结果,当StepVegas=4 时时间和概率效果较好;

N=13(100次实验)

StepVegas time(ms) probability(%)
1 247 100
2 215 100
3 134 98
4 88 88
5 78 54
6 87 25
7 114 11
8 139 7
9 212 3
10 268 3
11 391 2
12 325 3
13 328 1

N=13时,由实验结果,当StepVegas=4 时时间和概率效果较好;

N=14(100次实验)

StepVegas time(ms) probability(%)
1 908 100
2 520 100
3 253 97
4 179 94
5 113 92
6 98 59
7 86 32
8 125 16
9 194 10
10 239 6
11 486 6
12 441 5
13 512 3
14 551 4

N=14时,由实验结果,当StepVegas=4,5 时时间和概率效果较好;

N=15(100次实验)

StepVegas time(ms) probability(%)
1 940 100
2 549 100
3 381 100
4 247 100
5 209 97
6 133 87
7 110 57
8 114 41
9 168 12
10 232 5
11 319 4
12 457 6
13 478 2
14 532 2
15 608 4

N=15时,由实验结果,当StepVegas=5,6 时时间和概率效果较好;

N=16(100次实验)

StepVegas time(ms) probability(%)
1 3993 100
2 1528 100
3 956 100
4 402 100
5 326 100
6 194 95
7 126 71
8 108 44
9 132 27
10 184 12
11 251 12
12 372 4
13 632 4
14 604 2
15 648 2
16 614 5

N=16时,由实验结果,当StepVegas=6,7 时时间和概率效果较好;

N=17(100次实验)

StepVegas time(ms) probability(%)
1 4441 100
2 1732 100
3 1316 100
4 704 100
5 484 100
6 295 98
7 228 94
8 128 77
9 146 44
10 155 18
11 243 12
12 415 4
13 511 6
14 659 2
15 810 1
16 882 1
17 704 2

N=17时,由实验结果,当StepVegas=7,8 时时间和概率效果较好;

N=18(100次实验)

StepVegas time(ms) probability(%)
1 14691 100
2 5718 100
3 3171 100
4 1497 100
5 814 100
6 435 100
7 358 97
8 198 82
9 151 65
10 166 31
11 192 19
12 304 7
13 439 5
14 577 8
15 732 5
16 852 3
17 929 4
18 877 1

N=18时,由实验结果,当StepVegas=7,8 时时间和概率效果较好;

N=19(100次实验)

StepVegas time(ms) probability(%)
1 9290 100
2 5183 100
3 3675 100
4 2893 100
5 1468 100
6 998 100
7 412 99
8 336 99
9 251 81
10 169 54
11 187 35
12 231 16
13 383 10
14 494 9
15 796 5
16 1213 7
17 1204 3
18 1369 4
19 1095 2

N=19时,由实验结果,当StepVegas=8,9 时时间和概率效果较好;

N=20(100次实验)

StepVegas time(ms) probability(%)
1 78801 100
2 27941 100
3 8371 100
4 5370 100
5 2573 100
6 998 100
7 694 99
8 562 98
9 268 96
10 247 75
11 202 50
12 235 34
13 281 16
14 453 7
15 583 3
16 866 7
17 1172 6
18 1128 5
19 1543 1
20 1501 2

N=20时,由实验结果,当StepVegas=9 时时间和概率效果较好;

综上所述,对于所有的N来说,最好的StepVegas的值位于[N/3,N/2]之间。

Ex1. 与确定性算法相比较,并给出100-10000以内错误的比例。

代码:

import math
import random

class Certain:
    def __init__(self):
        self.res = []
        self.n = 10000


    def isPrime(self, x):
        s = 2
        e = int(math.sqrt(x))
        for i in range(s, e+1):
            if int(x) % int(i) == 0:
                return False

        return True


    def printPrimes(self):
        # self.res.append(2)
        i = 101
        while i < 10000:
            if self.isPrime(i):
                self.res.append(i)
            i += 2

        print(self.res)


class Uncertain:
    def __init__(self):
        self.res = []
        self.n = 10000


    def Btest(self, a, n):
        s, t = 0, n-1
        while t % 2 == 1:
            s += 1
            t /= 2

        x = a**t % n
        if x == 1 or x == n-1:
            return True
        for i in range(1, s):
            x = x ** 2 % n
            if x == n-1:
                return True

        return False


    def MillRob(self, n):
        a = random.randint(2, n-2)
        return self.Btest(a, n)


    def repeatMillRab(self, n, k):
        for i in range(1, k+1):
            if self.MillRob(n) is False:
                return False

        return True


    def printPrimes(self):
        self.res.append(2)
        self.res.append(3)
        i = 101
        while i < 10000:
            k = int(math.log(i, 2))
            if self.repeatMillRab(i, k):
                self.res.append(i)
            i += 2

        print(self.res)


a = Certain()
a.printPrimes()
print(len(a.res))

b = Uncertain()
b.printPrimes()
print(len(b.res))

答案:确定性算法是1204个,Monte Carlo算法得出的结果基本落在1204-1206之间(进行了30次实验)。

Ex2. 给出与图的独立集问题相关的判定问题的形式化描述,并证明它是NP完全的(提示:根据团问题进行规约)。

答:独立集问题:给定一个图G=(V,E),假设V‘表示图G的一个子集,对于I中的任意两个不同的顶点u,v,都不存在(u,v)属于E,则集合V‘是G的一个独立集。判定问题即给定V的一个子集V’作为证书,验证该子集之间任意两个顶点u,v,都不存在(u,v)属于E,如是则返回yes,否则返回no。

NPC证明:

(1)先证明独立集问题属于NP问题:根据上述形式化表示,可给定一个独立集V‘作为图G的证书,可以在多项式时间内验证:对于V’中的任意两点u,v,都不存在(u,v)属于E。

(2)再证明 Clique ≤ p \le_{p} pIndependent-Set:设G‘=(V,E’)表示图G的补图,从团V‘中任取不同的两点u,v,都存在(u,v)属于E‘,即(u,v)不属于E,因此V‘属于图G的独立集。所以团问题可以规约到独立集问题。

综和(1)和(2),独立集问题属于NPC问题。

Ex3. 证明G中的最大团size为 α \alpha α等价于Gm中的最大团size为m α \alpha α.

证明:(1)充分性:根据幂图的特征,可知若图G的最大团size为 α \alpha α,则幂图Gm中必然存在一个大小为m α \alpha α的最大团。此时需要考虑的是是否有大于m α \alpha α的团存在。假设幂图Gm中存在大于m α \alpha α的团,此时必然会有某一个子图G贡献至少 α \alpha α+1的结点用于构建团,显然矛盾。因此充分性证毕;

(2)必要性:若Gm的最大团size为m α \alpha α,若每一个子图G贡献的结点数(最大团)大于 α \alpha α,则幂图Gm的最大团size必然要大于m α \alpha α;若每一个子图G的最大团size小于 α \alpha α,则幂图Gm的最大团size必然要小于m α \alpha α,因此图G的最大团size只能在等于 α \alpha α时,Gm中的最大团size为m α \alpha α

Ex4. 当最优调度在任何机器上至多包含两个作业时,LPT也是最优的。

答:设n=2m,若n<2m,则令 J n + 1 , . . . , J 2 m J_{n+1},...,J_{2m} Jn+1,...,J2m的时间均设为0,并将其加入I,设 P 1 ≥ P 2 ≥ . . . ≥ P 2 m P_{1}\ge P_{2}\ge ... \ge P_{2m} P1P2...P2m。设最优调度使得每台机器恰有两个作业: J i 和 J j J_{i}和J_{j} JiJj,则必有i ≤ \le m, j > \gt >m,

否则若有最优调度O有i,j ≤ \le m,则一定有某台机器上有 J s J_{s} Js J t J_{t} Jt,使得s,t>m。因为 P i , P j ≥ P s , P t P_{i},P_{j}\ge P_{s},P_{t} Pi,PjPs,Pt,交换 P j P_{j} Pj P t P_{t} Pt,则 P i + P t ≤ P s + P j P_{i}+P_{t}\le P_{s}+P_{j} Pi+PtPs+Pj,且 P i + P t ≤ P i + P j P_{i}+P_{t}\le P_{i}+P_{j} Pi+PtPi+Pj,所以交换后的调度O‘的最迟完成时间只可能减少,故O并不是最优调度,矛盾。

若有某调度算法O,有i,j > \gt >m,交换 J s J_{s} Js J t J_{t} Jt,使得s,t ≤ \le m,此时 P i , P j ≤ P s , P t P_{i},P_{j}\le P_{s},P_{t} Pi,PjPs,Pt,交换 P j P_{j} Pj P t P_{t} Pt,则有 P i + P t ≤ P s + P t P_{i}+P_{t}\le P_{s}+P_{t} Pi+PtPs+Pt,且 P j + P s ≤ P s + P t P_{j}+P_{s}\le P_{s}+P_{t} Pj+PsPs+Pt,所以交换后的调度O‘的最迟完成时间只可能减少,故O并不是最优调度,矛盾。

因此必有最优调度使 J 1 , . . . , J m J_{1},...,J_{m} J1,...,Jm分别分配到 M 1 , . . . , M m M_{1},...,M_{m} M1,...,Mm上,当将 J n + 1 , . . . , J 2 m J_{n+1},...,J_{2m} Jn+1,...,J2m分配到M台机器上时,LPT是将长时间的作业分配到轻负载上,必于该最优调度结果相同。

即: A ( I ) O P T ( I ) ≤ 4 3 − 1 3 m \frac{A(I)}{OPT(I)}\le \frac{4}{3}-\frac{1}{3m} OPT(I)A(I)343m1

对于紧确界的证明如下:现假设每个作业的时间( P 1 , P 2 , . . . , P 2 m + 1 P_{1},P_{2},...,P_{2m+1} P1,P2,...,P2m+1)=(2m-1,2m-1,2m-2,2m-2,…,m+1,m+1,m,m,m),即 P k = 2 m − [ k + 1 2 ] , k = 1 , . . . , 2 m , P 2 m + 1 = m P_{k}=2m-[\frac{k+1}{2}], k=1,...,2m ,P_{2m+1}=m Pk=2m[2k+1],k=1,...,2m,P2m+1=m,所以OPT(I)=3m(对应下图1),A(I)最坏的情况为前2m个作业在m个不同机器上同时完成,此时A(I)=4m-1(对应下图2,图中的n均为上述公式的m),此时可证明上确界的紧致性。

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/jackzhang11/article/details/109479996