Inter美国职位的面试题,师兄对c++不是很熟,让我帮忙写。看了题目写了后感觉这职位就是为我量身定做的呀T_T,但是人家PHD only。。。
题目是给一个高纬矩阵,维数和每一维的长度都未知,但是每一维的长度都是2^x,即2^d1, 2^d2,..., 2^dn,让对这各矩阵downsample,也就是数据降维。每次降维就是把原矩阵降成 2^(d1-1), 2^(d2-1),..., 2^(dn-1)维,降次就可以得到一个小矩阵,直到降到某一维长度1,输出每一次降维得到的矩阵。
降维后矩阵的值由原矩阵中出现次数最多的数字代替,每次降维都必须由原始矩阵得到新值,而不能用中间降维得到的矩阵得到新值。
例如:
00000000
10101010
11001100
11101110
00001111
10101111
11101100
11001101
yields the 1-downsampled image
0000
1010
0011
1010
the 2-downsampled image
00
01
and the 3-downsampled image
1
最后一次得1得原因是原始矩阵中1出现最多,而不是根据2-downsampled里的值来输出0
如果你认为这就完了,那么你就错了。。毕竟这是招PHD的,薪水还好高好高。。
题目要求用多线程实现,用BOOST库,还要分析时间空间利用与线程数,矩阵大小和矩阵中值的种类的关系。
下面是题目的一下要求:
2. EFFICIENCY
Your code should use the fastest possible parallel algorithm. Answer the following questions about computational complexity.
Let N be the number of pixels in the original image, n the number of unique pixel values in the original image, and M the number of parallel threads.
(1) How does the execution time of the fastest parallel algorithm scale with N, n, andM?
(2) How does memory usage scale with N, n, and M?
3. STYLE
The style of your code will be evaluated, including aspects such as
• organization into functions
• object-oriented design
• memory management
下面是我的解法了:由于矩阵的维数和各维的长度都未知,我用了类似c语言的一维矩阵模拟高维矩阵,手动计算下表。用多线程将矩阵分割,然后每个线程处理一块。用一个Index(map< pair<level, blockIndex>, <value, times> >)数据结构存放线程结果。最后汇总一下得到最终结果。
上代码!!!!真是高端面试题呀。。
#include <iostream> #include <algorithm> #include <string> #include <fstream> #include <vector> #include <map> #include <boost/lexical_cast.hpp> #include <boost/thread/locks.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp> #include <boost/bind.hpp> template<class T> class Matrix { public: Matrix():_d(0), _p(NULL){ } Matrix(int d, int *base): _d(d) { size_t num = 1; for(int i=0; i<d; ++i) { _base[i] = base[i]; num *= _base[i]; } _p = new T[num]; } Matrix(const Matrix<T> & m):_d(m._d) { for(int i=0; i<_d; ++i) { _base[i] = m._base[i]; } size_t s = m.size(); _p = new T[s]; for(int i=0; i<size(); ++i) { _p[i] = m._p[i]; } } Matrix<T> & operator=(const Matrix<T> & x) { if(this == &x) { return *this; } _d = x._d; for(int i=0; i<x._d; ++i) { _base[i] = x._base[i]; } if(_p) delete[] _p; size_t s = x.size(); _p = new T[s]; for(int i=0; i<s; ++i) { _p[i] = x._p[i]; } return *this; } size_t size() const{ if(!_d) return 0; size_t re = 1; for(int i=0; i<_d; ++i) { re *= _base[i]; } return re; } T & get(int *ind) const{ size_t pod = 1, index = 0; for(int i=_d-1; i>=0; --i) { index += ind[i] * pod; pod *= _base[i]; } return _p[index]; } void assign(int *ind, const T &t) { size_t pod = 1, index = 0; for(int i=_d-1; i>=0; --i) { index += ind[i] * pod; pod *= _base[i]; } _p[index] = t; } int getd() const { return _d; } int getBase(int i) const { return _base[i]; } int getMinBase() const{ return *std::min_element(_base, _base+_d); } void output() const { size_t s = size(); for(int i=0; i<s; ++i) { std::cout << _p[i] << " "; } std::cout << std::endl; } ~Matrix(){ delete[] _p; } private: T *_p; int _d, _base[64]; }; template<class T> class MatrixReader { public: Matrix<T> read(std::istream &in){ int d, ind[64], index[64]; in >> d; for(int i=0; i<d; ++i) { in >> ind[i]; } Matrix<T> re(d, ind); while(in) { for(int i=0; i<d; ++i) { in >> index[i]; } T value; in >> value; re.assign(index, value); } return re; } Matrix<T> read(const std::string &fileName) { std::ifstream in(fileName.c_str()); return read(in); } }; template<class T> class DownSample { public: typedef std::map< std::pair<int, int>, std::map<T, int> > Index; //map< std::pair<level, blockIndex>, std::map<num, times>> //block of the original matirx //baseIntever[i][0] -> start index of i'th dimension //baseIntever[i][1] -> length of i'th dimesion struct Arg { int baseIntever[64][2]; void output(int d) const{ std::cout << "[TEST]" << std::endl; for(int i=0; i<d; ++i) { std::cout << baseIntever[i][0] << " " << baseIntever[i][1] <<std::endl; } } }; DownSample(const Matrix<T>& m) :_m(m){ } void downSample(int threadNum = 1) { for(int i=0; i<32; ++i) { if((1<<i) > threadNum) { threadNum = (1<<(i-1)); break; } } int mn = _m.getMinBase(); int ind[64]; for(int i=0; i<_m.getd(); ++i) { ind[i] = _m.getBase(i); } for(int k=mn; k>1; k/=2) { for(int j=0; j<_m.getd(); ++j) { ind[j] /= 2; } _re.push_back(Matrix<T>(_m.getd(), ind)); } //ind[i] -> the i'th dimension is cut to ind[i] parts for(int i=0; i<_m.getd(); ++i) { ind[i] = 1; } for(int i=0; i<_m.getd(); ++i) { int num = _m.getBase(i); if(threadNum <= num) { ind[i] = threadNum; break; } else { ind[i] = num; threadNum /= ind[i]; } } int threadInUse = 1; for(int i=0; i<_m.getd(); ++i) { threadInUse *= ind[i]; } std::cout << "[TEST] minbase = " << mn << " threadInUse = " << threadInUse<< std::endl; std::vector<Arg> args; Arg x; dfsGetArg(ind, args, 0, x); /* std::cout << "[TEST] ARGS" << std::endl; for(int i=0; i<args.size(); ++i) { args[i].output(_m.getd()); } */ boost::thread_group group; for (size_t i = 0; i < threadInUse; ++i) { group.create_thread(boost::bind((&DownSample::callBack), this, args[i], i)); } group.join_all(); int p[64]; for(typename Index::iterator it = _index.begin(); it != _index.end(); ++it) { int lev = (it->first).first; int block = (it->first).second; int factor = 1; //decode "block" to dimension indexes for(int i=1; i<_m.getd(); ++i) { factor *= _m.getBase(i) / (1<<(lev+1)); } for(int i=0; i<_m.getd(); ++i) { p[i] = block / factor; block %= factor; if(i+1 < _m.getd()) factor /= _m.getBase(i+1) / (1<<(lev+1)); } T val = 0, times = 0; for(typename std::map<T, int>::iterator jt = it->second.begin(); jt != it->second.end(); ++jt) { if( jt->second > times) { times = jt->second; val = jt->first; } } /* std::cout << "[TEST] " << "level = " << lev; std::cout << " index = "; for(int i=0; i<_m.getd(); ++i) { std::cout << "[" << p[i] << "]"; } std::cout << " value = " << val << std::endl; */ _re[lev].assign(p, val); } } //full permutition to get every block of the original matrix void dfsGetArg(const int *ind, std::vector<Arg> &args, int now, Arg &th) { if(now >= _m.getd()) { args.push_back(th); return ; } for(int i=0; i<ind[now]; ++i) { th.baseIntever[now][0] = i * (_m.getBase(now) / ind[now]); th.baseIntever[now][1] = (_m.getBase(now) / ind[now]); dfsGetArg(ind, args, now+1, th); } } void callBack(const Arg & x, int threadLabel) { std::cout << "[INFO] start " << threadLabel << " thread" << std::endl; int level = _re.size(); //map< std::pair<level, blockIndex>, std::map<num, times>> int offset[64]; dfsGetIndex(x, level, 0, offset); return ; } //full permutition to get every element from Arg x'block void dfsGetIndex(const Arg & x, int level, int now, int *offset) { if(now >= _m.getd()) { int p[64]; //index of every element in this block for(int i=0; i<_m.getd(); ++i) { p[i] = offset[i] + x.baseIntever[i][0]; } T value = _m.get(p); for(int i=0; i<level; ++i) { int blockIndex = 0; int factor = 1; //incode index p[] to i'level matrix for(int j=_m.getd()-1; j>=0; --j) { blockIndex += ( p[j] / (1<<(i+1)) ) * factor; factor *= (_m.getBase(j) / (1<<(i+1)) ); } boost::unique_lock< boost::mutex > lock(_mtx); ++_index[ std::make_pair(i, blockIndex) ][value]; } return ; } for(int i=0; i<x.baseIntever[now][1]; ++i) { offset[now] = i; dfsGetIndex(x, level, now+1, offset); } } void outputResult() const { std::cout << "[INFO] result one line a matrix" << std::endl; for(int i=0; i<_re.size(); ++i) { _re[i].output(); } } private: const Matrix<T> &_m; std::vector< Matrix<T> > _re; Index _index; boost::mutex _mtx; }; int main(int argc, char* argv[]) { if( argc != 3 ) { std::cout << "Usage: " << argv[0] << " fileName threadNum" << std::endl; exit(0); } MatrixReader<int> r; Matrix<int> m = r.read(argv[1]); // m.output(); DownSample<int> down(m); down.downSample(boost::lexical_cast< int >(argv[2])); down.outputResult(); return 0; } /* compile command: g++ -g -std=c++11 Matrix.cpp -lboost_thread -lboost_system -lpthread -o a.out */ /* example of input file 2 -> dimension numbers 4 8 -> base of every dimension 0 0 1 0 1 1 0 2 1 0 3 1 0 4 1 0 5 1 0 6 1 0 7 1 1 0 1 1 1 2 1 2 1 1 3 2 1 4 1 1 5 2 1 6 1 1 7 2 2 0 1 2 1 1 2 2 2 2 3 2 2 4 2 2 5 2 2 6 2 2 7 2 3 0 1 3 1 2 3 2 2 3 3 2 3 4 2 3 5 2 3 6 2 3 7 2 */