1.1 让CPU占用率曲线听你指挥

题意:写程序来控制任务管理器中CPU的占用率,使得占用率曲线达到某种样式。

任务管理器是通过定期对CPU的状态采样来获得占用率的,如果我们保证在采样间隔内占用率相同,那么曲线就会是一条直线。

由于CPU的工作状态是离散的,我们是无法做到真正的50%占用的这种状态,指令只有执行和不执行之分,没有执行一半的说法。我们只能努力使得在一个微分的时间片内,CPU有50%的时间工作,50%的时间空闲,这样从宏观上来看,的确是50%的占用。

书中第一个例子的缺点是依赖于具体的CPU,在一个时间片内,它使用Sleep来设定10ms的占空时间,使用CPU的频率来估计工作的时间。

第二个例子则好得多,使用Tick来对工作时间计时,脱离了对具体频率的依赖。

第三个例子是为了能在有其他进程干扰下正常工作,因为前两个例子并不能保证自己Sleep的时候,其他进程也不工作。不过这种适应实际上只满足了一半的情况,还是以50%占用的目标为例,其他进程占10%,它能补40%,但其他进程占60%的时候,程序不可能把占用率降下来,程序只能补,不能减。

第四个正弦曲线的例子实际上就是不断调整占空比来实现的,原理和第二个例子完全一样。

对于多核心的情况,涉及的无非是核心管理的问题,懂一些系统API就行,这个题目的关键还是微分时间片的思想。

1.2 中国象棋将帅问题

题意:懂中国象棋的人都知道,将和帅是不能碰面的,这道题就是要求编程打印出这种情况下,将和帅的所有可能的位置,只能使用一个变量。

打印出所有位置的确是个简单的问题,这道题的考点在于只使用一个变量。

本需要多个变量来保存状态,却只能使用一个变量,无非两种解答方案:1、把一个长变量拆成多个短变量用;2、编码变量降维。

变量存的是信息,以上两种方案归结起来都是把信息存到一个变量的空间里面去,前者使用空间来划分信息,后者利用逻辑关系来编码信息。

书中完整解法只给了一个,后面附带了两段代码,解法一和解法三无本质区别,都是将长变量划分成短变量来用,只是前者手工划分,后者利用了结构体划分,解法二是一种非常有趣的解法,将帅的位置编号分别有9个,恰好可以写成一个9×9的矩阵,矩阵里面的81个元素对应81种状态,状态值能够对应到矩阵中的位置,因此可以遍历所有将帅位置的组合。

解法二实际上是将二维状态线性化编码,本来81个二维向量,编码成了81个数字,然后能够解码对应到二维向量。其中,在解码时候使用了除法和模运算,传统上认为这是非高效的指令。

解法一手工划分太麻烦,还不如解法三直接。不过解法三也使用了模运算,可不可以不使用模运算呢?

分析下题目我们发现,将帅的位置是线性编码的,也就是说,为了判断将帅是不是面对面了,还得解码得出列位置,然后才能判断。那么,我们可以直接将将帅位置二维化编码,分别保存行列,即使这样编码,一个字节也足够了。这里我提高了问题维度,但减少了一次解码过程。

#include <stdio.h>

struct {
    unsigned char x1:2;
    unsigned char y1:2;
    unsigned char x2:2;
    unsigned char y2:2;
}i;

int main()
{
    for(i.x1 = 0; i.x1 < 3; i.x1++) {
        for(i.x2 = 0; i.x2 < 3; i.x2++) {
            if(i.x1 == i.x2) continue;
            for(i.y1 = 0; i.y1 < 3; i.y1++) {
                for(i.y2 = 0; i.y2 < 3; i.y2++) {
                    printf("A = (%d, %d), B = (%d, %d)\n", i.x1, i.y1, i.x2, i.y2);
                }
            }
        }
    }
    return 0;
}

从只使用一个变量的思考中,我发现这些有趣的规则:1、对于固定维度的问题,如果变量有限,既可以划分变量匹配问题维度,也可以编码变量降低维度;2、问题本身的维度也和其编码定义有关。

维度和编码的取舍,其实就是空间和时间的取舍。

posted on 2012-10-14 22:12  罗辑  阅读(245)  评论(0编辑  收藏  举报