20200917-3 白名单

此作业的要求参见作业要求 20200917-3 白名单

本次作业的全部代码可以在codinghttps://e.coding.net/bluestudio/whitelist/whitelist.git , git@e.coding.net:bluestudio/whitelist/whitelist.git。

the git url of this project: https://e.coding.net/bluestudio/whitelist/whitelist.git or git@e.coding.net:bluestudio/whitelist/whitelist.git

WHY BAD CODING!!!! F**K Service.

该作业在截至时间后发生了题目描述修改,请自行关注

白名单 SPEC

老杨因为留作业太多被学生投诉下岗了,去面试,刚好你是公司的面试官。出了道题,题目要求如下(别忙着答题,现在是背景,作业的要求还在后面)。

1) 程序名brute,按照下面的要求读入两个文件。

文件 whitelist,包含1列整数10个,随机生成(要求老杨自己想办法),通过命令行参数指出文件名。

文件 q,包含1列整数1000个,随机生成(也要求老杨自己想办法),通过控制台读入。

2) 在文件q中查找所有不在whitelist中的整数,定向输出到一个文件中。

(读到此处,你见老杨面露困惑,出于多年,不,一年,不,半年的师生情谊,你补充道,“杨老师,您就当这是从交易记录q中查找不符合白名单whitelist的非法交易。”老杨感激地点点头。)

3) 写一份如何部署运行代码的readme。

老杨写成如下代码:

//brute.cpp
#include <fstream>
#include <iostream>
#include <cstring>
using namespace std;

const int w_1m = 1000000;
int w[w_1m];

bool is_match(int t, int w[], int w_length)
{
	for (int i = 0; i < w_length; i++)
	{
		if (t != w[i])
		{
			return true;
		}
	}
	return false;
}

// brute -w whitelist < T
int main(int argc, char* argv[])
{
	if (argc != 3 || strcmp(argv[1], "-w"))
	{
		return 1;
	}

	// init w
	////    for(int i=0;i<w_1m)
	////    {
	////        w[i]=-1; //填充非法数据
	////    }
	ifstream infile;
	infile.open(argv[2]);
	int i = 0;
	cout << argv[2] << endl;
	while (infile >> w[i++])
	{
	}
	int w_length = i - 1;
	cout << w_length << endl;
	// check t
	int t = 0;
	while (cin >> t)
	{
		if (is_match(t, w, w_length))
		{
			cout << t << endl;
		}
	}
}

由于数据老杨也得自己想办法,所以老杨又写了两段代码生成数据,代码如下:

//create.cpp
#include <iostream>
#include <stdlib.h>
#include <time.h>

using namespace std;

int main(int argc, char* argv[])
{
	srand((unsigned)time(NULL));
	for (int i = 0; i < 10; i++)
	{
		cout << rand() << "\n";
	}
	cout << endl;

	return 0;
}

readme文档如下:

readme.md

安装vs;
配置环境变量;
编译create.cpp文件;
执行“create >whitelist”生成文件whitelist;
将create.cpp中的for循环中的“10”改为1000;
重新编译create.cpp文件;
执行“create >q”生成文件q;
编译brute.cpp文件;
执行“brute -w q < whitelist > output”

老杨有多年的指导学生的经验,所以会使用多种语言编程。既然是面试,就想着多多展示自己,所以老杨又用C#解了这道题,代码如下:

//foo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace foo
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime beforDT = System.DateTime.Now;
            if(args.Count() < 1)
                return;
            string path = args[0];
            string[] sm = File.ReadAllLines(path);
            int[] p = new int[sm.Length];
            //Console.WriteLine(sm.Length);
            int[] array = new int[1000000];
            for (int i = 0; i < 1000000; i++)
            {
                array[i] = Convert.ToInt32(Console.ReadLine());
            }
            for(int i = 1;i < sm.Length; ++i)
            {
                int temp = Convert.ToInt32(sm[i]);
                if(find(temp, array) == -1)
                    Console.WriteLine(temp); 
            }
            DateTime afterDT = System.DateTime.Now;  
            TimeSpan ts = afterDT.Subtract(beforDT);  
            Console.WriteLine("DateTime: {0}ms.", ts.TotalMilliseconds);
        }

        static int find(int key, int[] array)
        {
            for(int j = 0; j < 10; j++)
            {
                if(key == array[j])
                    return key;
            }
            return -1;
        }
    }
}

readme文档如下:

readme.md

安装vs;
编译create.cpp文件;
执行“create >whitelist”生成文件whitelist;
将create.cpp中的for循环中的“10”改为1000;
重新编译create.cpp文件;
执行“create >q”生成文件q;
编译.cs文件;
执行“foo q <whitelist >output”。

你读了一遍老杨的readme.md文件,看了看老杨生成数据的代码,建议到:“为什么不用命令行参数决定生成的数据量的大小呢?”你说完这句话之后老杨有点儿疑惑,你赶紧补充说:“你把for循环中的10替换成1000有点儿麻烦啊,何不把它定义成一个变量呢?在控制台进行输入。”老杨恍然大悟,马上修改了create.cpp和readme.md。

作业0(5分)

Q1: 修改create.cpp文件,改成由命令行参数确定生成的数据的数据量。修改readme.md的对应部分。(要求贴出修改之后的代码和read.md。)

A:

修改后的代码如下:

// add注释文本后的代码段(主观意义上)为修改的代码
//create.cpp
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <string> // add

using namespace std;

int main(int argc, char* argv[])
{
        // add
	// default counter of random number,
	// with default value 10 (create 10 numbers)
	int counter = 10;

        // add
	// try to get counter form argument
	if (3 == argc && 0 == strcmp("-n", argv[1])) {
		try {
			counter = std::stoi(argv[2]);
		}
		catch (const char* msg) {
			cerr << msg << endl;
		}
	}

	// create random number
	srand((unsigned)time(NULL));
	for (int i = 0; i < counter; i++)
	{
		cout << rand() << "\n";
	}
	cout << endl;

	return 0;
}

修改后的read.md如下:

readme.md

安装vs;
配置环境变量;
编译create.cpp文件;
执行“create >whitelist”生成文件whitelist,默认包含10个随机数;
执行“create -n N >whitelist”生成文件whitelist,包含N个随机数;
编译brute.cpp文件;
执行“brute -w whitelist < q > output”

运行截图如下:

fig 1: create程序默认参数运行

默认参数

fig 2: create程序指定参数为15运行

给定生成个数

你看了一下代码,又说道:“老杨,你这结果倒是能对……但是”。你觉得代码的执行效率会比较低。但是你想引导他独立完成修改,你说:“我认为你应该profile一下你的代码,找到代码最慢的地方。”

profile?还好老杨看过《构建之法》,那本书中提到过效能分析。不过老杨不明白为啥要进行效能分析,但毕竟是在面试也不好把太多疑义说出来。所以只好照做。

作业1(10分)

Q2: 对上面两段老杨写的代码任选其一进行profile,观察现象(要求有截图记录)。

A:

profile结果如下图所示。可以发现,该程序本身运行的时间并不多,外部代码占用较多运行时间。程序自身的代码中,输入数据部分占用较多时间。但由于程序运行时间过短(main function: 1unit),并不能准确反应占用情况。

PS:

原始代码的is_match()有逻辑错误,修改为:

bool is_match(int t, int w[], int w_length)
{
	// search every number in array w, test t not same as anyone in array w.
	// if have one same as t, return false and exit function;
	// if not found any one same as t until last, return true and exit function.
	for (int i = 0; i < w_length; i++)
	{
		if (t == w[i])
		{
			return false;
		}
	}
	return true;
}

fig 3: profile-1 结果

profile-1 结果

你是一个好人,为了让老杨知道为什么要对代码进行profile,于是你在原来的题目的基础上做出了修改,修改之后的题要求如下:

1) 读入两个文件,一个用控制台,一个用命令行参数指出文件名。

文件 biggerwhitelist,包含1列整数1M个,随机生成(要求老杨自己想办法),通过命令行参数指出文件名。

文件 biggerq,包含1列整数10M个,随机生成(也要求老杨自己想办法),通过控制台读入。

2) 在文件biggerq中查找所有不在biggerwhitelist中的整数,重定向输出到一个文件中。

3) 写一份如何部署运行代码的readme。

老杨看了一下,发现只是数据量变大了,代码不用变。于是换了数据又运行了一遍自己的代码,发现跑了很久(大概10分钟)还没结果。由于是在面试,老杨急坏了。这个时候作为面试官的你知道目的已经达成了,于是告诉老杨:“你看,知道为啥让你profile了吧,你还是再profile一次吧。”

PS:

命令行操作如下:

# 创建1M随机数文件biggerwhitelist
.\create.exe -n 1000000 > biggerwhitelist

# 创建10M随机数文件biggerwhitelist
.\create.exe -n 10000000 > biggerq

作业2(10分)

Q3: 以biggerwhitelist和biggerq作为输入,对作业1中选择的代码再次进行profile,找到代码执行最“慢”的地方,截图为证并文字说明。

A:

  1. 修改项目命令行属性,进行profile,属性设置如下图所示。

fig 4: profile 2 项目命令行属性

项目命令行属性

  1. 配置profile

fig 5: profile 2 配置

profile - 2 配置

  1. 测试结果

fig 6: profile 2测试结果1

profile - 2 结果

fig 7: profile 2测试结果2

  1. 分析

is_match()是性能的瓶颈所在,分析得知该程序需要大量的进行查找操作,但is_match()的实现算法为暴力穷举,效率低下,考虑使用更高效的查找算法,如二分查找。

老杨再次profile之后发现了代码最慢的地方在哪儿。这时候你说:“既然找到了最慢的地方,那就开始对你的代码进行优化吧。”对于这次引导的结果,你很得意。

作业3(10分)

Q4: 根据作业2找到的最慢的地方,优化作业1中你选择的代码,在保证输出结果正确的前提下,减少老杨程序运行的时间。(优化后的代码需要你提交到git上,作为教师的判断依据。优化后的程序的名字应该是better.cpp或者better.cs。)

A:

修改is_match()函数,使用二分算法,修改后的代码如下所示。同时,本次作业的全部代码可以在coding找到。注意,本代码在Visual Studio 2019编写、编译以及测试,但版本控制中只包含源码文件,请自行配置解决方案项目

bool is_match(int t, int w[], int w_length)
{
	// binary search
	int left = 0, right = w_length - 1, mid;
	while (left <= right) {
		mid = (left + right) / 2;

		if (t == w[mid]) {
			return false;
		}

		if (t > w[mid]) {
			left = mid + 1;
		}
		else {
			right = mid - 1;
		}
	}

	return true;
}

老杨在优化了代码之后,发现果然代码运行“快”了很多,很是得意。这时候你想“好人”做到底,顺水推舟一把。你说:“对优化后的代码再profile一下吧。”老杨与你意见一致。

作业4(5分)

Q5: 对作业3优化后的代码进行profile,结果与作业2的结果做对比。画表格并文字说明。

A:

对修改is_match()更新算法后,对程序重新进行profile,测试结果如下图所示。可以看出,程序运行总体用时大大减少,对程序内部函数的统计数据进一步分析,可以看出is_match()函数不在是性能瓶颈,其运行时间远远低于输入输出。更新前后的profile对比如下表所示。

table 1: brute(old)于better(new) CPU占用对比表

function burter
Total CPU[unit, %]
better
Total CPU[unit, %]
burter
Self CPU[unit, %]
better
Self CPU[unit, %]
main 1100115(97.42%) 227214(99.50%) 567(0.05) 384(0.17%)
is_match 1009162(91.6%) 2027(1.33%) 1006970(91.0%) 2846(1.25%)
  1. 配置项目命令行参数

fig 8: profile 3 项目命令行配置

profile - 3 配置

  1. profile配置

fig 9: profile 2测试

profile  - 2 测试开始

  1. 测试结果

fig 10: profile 2 结果

profile - 2 测试结果

最后,老杨发现了他原本代码的不足,并且对于你的引导表达了谢意。

但是,你还是犹豫要不要录用老杨。因为你觉得老杨的文档(readme),注释和代码风格有很大的问题,并且给老杨指了出来。

做业5(5分)

Q6: 你觉得老杨的文档(readme),注释和代码风格又哪些问题,该如何改进?

A:

  1. 函数未写注释。函数应当注释其作用,参数,返回值以及简要的逻辑和算法说明;

  2. 关键语句或语句块应当注释逻辑和算法说明;

  3. readme没有说明程序作用,没有对编译环境、过程和参数做足够说明,也没有对程序的运行提供足够的文档说明。

Q7: 面试结束了,你和老杨握手,对他说出了面试的结果。你说的内容,不是今天的作业题,也许是若干年以后你想对当年教你的教师说的,也许是你希望未来的面试官对你说的。你想说的是什么呢?

A:

功能实现的不错,算法很好,代码可读性也高

posted @ 2020-09-22 18:38  WenqiangXie  阅读(166)  评论(2编辑  收藏  举报