Giải bài tập sinh dữ liệu mẫu không ngẫu nhiên và có ràng buộc

Đối với chúng tôi, bài toán Generating Realistic and Non-Random Sample Data luôn là 1 trong những bài toán rất thực tế và mong muốn tìm ra cách giải quyết tốt nhất. Tùy vào kinh nghiệm, tư duy, sáng tạo mà người thực hiện có thể đưa ra các cách giải riêng. Hiện tại, tôi đang rất quan tâm đến thước đo hiệu suất implement chứ không phải là performance. Sau đây là 1 bài tập tôi thường hay cho các bạn thực tập sinh rèn luyện

 

Cho mô hình, có N con bò gồm 1 trong 3 loại Bò sữa, Bò đỏ, Bò vàng cung cấp cho M cơ sở chế biến trực thuộc P Tổng công ty với thiết kế quan hệ như sau:

clip_image002

Yêu cầu tạo được cơ sở dữ liệu với dữ liệu mẫu được sinh ra đảm bảo được các số N, M, P nhập vào và thỏa gần đúng bảng thống kê sau:

% số con bò trong mỗi loại cung cấp cho các Tổng công ty

TCT1

TCT2

TCTP

Bò sữa

10%

10%

5%

Bò đỏ

20%

50%

20%

Bò vàng

90%

80%

80%

Sau khi sinh xong data, thống kê thực tế ra lại bảng tương tự để đối chiếu:

 

Bài làm:

1.    Phân tích hướng tiếp cận:

Tạo N con bò. Tỉ lệ mỗi loại không quy định nên tạm chia đều: N/3 mỗi loại. Trường hợp N không chia hết cho 3 thì lấy phần nguyên, nhóm cuối sẽ có ít hoặc nhiều bò hơn 2 nhóm còn lại

Tạo P tổng công ty.

Tạo M cơ sở. Tỉ lệ thuộc tổng công ty không quy định nên tạm chia đều: 1 tổng công ty có M/P cơ sở

Với 1 giá trị trong bảng thống kê X sẽ có ((N/3) * X) con bò loại đó được cung cấp cho tổng công ty tương ứng, không quan tâm cơ sở chế biến nào.

Ví dụ cụ thể cho dễ hình dung:

N=3000, mỗi loại 1000 con

P=5 giả sử là TCT1, TCT2,… TCT5

M=2 vậy là mỗi tcty có 2 cơ sở chế biến, cứ tạm gọi là A và B: cs1A, cs1B, cs2A, cs2B,… cs5A, cs5B

Giả sử có 10% bò sữa được cung cấp cho TCT1. Tức là nói 1 cách khác có 100 con bò sữa cung cấp cho Tổng công ty 1, có thể tất cả đều cung cấp cho cs1A hoặc cs1B hoặc là vừa cs1A, cs1B

Vì không quan tâm cơ sở chế biến nào nên có thể tự áp đặt là luôn cung cấp cho cơ sở đầu tiên của mỗi Tổng công ty.

2.    Hiện thực

Do yêu cầu và kì vọng không rõ ràng nên khi thực hiện, chúng ta có thể chọn kĩ thuật và công nghệ mình cảm thấy phù hợp nhất (thường chọn cái nào quen nhất)

Flow suy nghĩ thường gặp sẽ là:

Đầu vào và đầu ra của chương trình thì giờ đọc ghi với excel hay là database, xml, nosql…

Nên dùng Entityframework, hay là ADO, nHibernate, Dapper, JDBC, LINQ, RESTful,… chi tiết code sẽ như thế nào… search google, stackoverflow xem thế nào….làm thử. Bug thì config, cài lại môi trường…

Không nên! Suy nghĩ vậy rất dễ gây chết người nếu không có kinh nghiệm xử lý lỗi(troubleshoot).

Thay vì vậy hãy tập trung vào hiện thực cho bằng được cái tiếp cận ban đầu nghĩ ra. Hãy coi input output là những thành phần cơ bản nhất của chương trình: Biến, Mảng. Hãy tận dụng những lệnh khởi tạo, xuất lên màn hình như khi bắt đầu với lập trình.

Đôi khi sinh viên năm 1, năm 2 nhiều khi có lợi thế hơn là biết phải làm gì trước vì chỉ có thể làm cái đó trước. Khi càng học thêm khóa mì ăn liền này, tuyệt chiêu kia, thậm chí khi trở thành những dev công nghiệp rất dễ bị ảnh hưởng bởi những suy nghĩ công nghệ hoặc suy nghĩ search google cho ra giải pháp và chép code để xong mà lười phân tích và tự code

Sau đây là code của 1 em sinh viên năm 2 sau khi học xong kĩ thuật lập trình C++ là đủ để hiện thực và test xem ý tưởng ban đầu có khả thi hay không

#include<iostream>
#include<string.h>

#define NAME_SIZE 32
#define NUMBER_BUFFER_SIZE 32
#define COW_TYPE_COUNT 3
#define MAX_P 32
using namespace std;

//Khai báo cấu trúc
enum CowType{
	Milk,
	Red,
	Yellow
};

struct Cow{
	int id;
	char name[NAME_SIZE];
	CowType cowType;
}* _cows;

struct Company{
	int id;
	char name[NAME_SIZE];
}* _companies;

struct Facilty{
	int id;
	char name[NAME_SIZE];
	int companyId;
}* _facilities;

struct Provide{
	int facilityId;
	int cowId;
}* _provides;


//Khởi tạo
int N = -34;
int M = 10;
int P = 5;
float X[COW_TYPE_COUNT][MAX_P] = {
	{ 0.1, 0.1, 0.1, 0.1, 0.05 },
	{ 0.2, 0.5, 0.5, 0.3, 0.05 },
	{ 0.9, 0.8, 0.6, 0.6, 0.08 }
};

int _providesCount;

//Xử lý
void process()
{
	char buffer[NUMBER_BUFFER_SIZE];

	//Tạo N con bò. Tỉ lệ mỗi loại không quy định nên tạm chia đều: N/3 mỗi loại. 
	//Trường hợp N không chia hết cho 3 thì lấy phần nguyên, nhóm cuối sẽ có ít hoặc nhiều bò hơn 2 nhóm còn lại
	_cows = new Cow[N];
	int numberOfCowsEach = N / COW_TYPE_COUNT;
	int remainCow = N % COW_TYPE_COUNT;
	int idx = 0;
	for (int i = 0; i < COW_TYPE_COUNT; i++)
	{
		for (int j = 0; j < numberOfCowsEach; j++)
		{
			_cows[idx].id = idx + 1;
			switch (i)
			{
			case 0:
				_cows[idx].cowType = CowType::Milk;
				sprintf(buffer, "Mil %d", j + 1);
				break;
			case 1:
				_cows[idx].cowType = CowType::Red;
				sprintf(buffer, "Red %d", j + 1);
				break;
			case 2:
				_cows[idx].cowType = CowType::Yellow;
				sprintf(buffer, "Yel %d", j + 1);
				break;
			}
			strcpy(_cows[idx].name, buffer);
			idx++;
		}
	}
	for (int j = 0; j < remainCow; j++)
	{
		_cows[idx].id = idx + 1;
		_cows[idx].cowType = CowType::Yellow;
		sprintf(buffer, "Yel %d", numberOfCowsEach + j + 1);
		strcpy(_cows[idx].name, buffer);
		idx++;
	}

	//Tạo P tổng công ty.
	//và tạo M cơ sở.Tỉ lệ thuộc tổng công ty không quy định nên tạm chia đều : 1 tổng công ty có M / P cơ sở (lấy nguyên tương tự con bò)
	_companies = new Company[P];
	_facilities = new Facilty[M];
	int numberOfFacsEach = M / P;
	int remainFacs = M % P;
	idx = 0;
	for (int i = 0; i < P; i++)
	{
		_companies[i].id = i + 1;
		sprintf(buffer, "Com %d", i + 1);
		strcpy(_companies[i].name, buffer);
		for (int j = 0; j < numberOfFacsEach; j++)
		{
			_facilities[idx].companyId = _companies[i].id;
			_facilities[idx].id = idx;
			sprintf(buffer, "Fac %d", j + 1);
			strcpy(_facilities[idx].name, buffer);
			idx++;
		}
	}
	for (int j = 0; j < remainFacs; j++)
	{
		_facilities[idx].id = idx + 1;
		_facilities[idx].companyId = _companies[P-1].id;
		sprintf(buffer, "Fac %d", numberOfFacsEach + j + 1);
		strcpy(_facilities[idx].name, buffer);
		idx++;
	}

	//Với 1 giá trị trong bảng thống kê X sẽ có((N / 3) * X) con bò loại đó được cung cấp cho tổng công ty tương ứng, 
	//không quan tâm cơ sở chế biến nào.
	_provides = new Provide[N * M];//giả sử 100%
	idx = 0;
	for (int i = 0; i < P; i++)
	{
		//Trường hợp 2 loại bò đầu
		for (int j = 0; j < COW_TYPE_COUNT - 1; j++)
		{
			int numberOfProvidedCows = numberOfCowsEach * X[j][i];
			for (int k = 0; k < numberOfProvidedCows; k++)
			{
				_provides[idx].facilityId = _facilities[numberOfFacsEach*i].id;
				_provides[idx].cowId = _cows[numberOfCowsEach*j + k].id;
				idx++;
			}
		}
		//Trường hợp sau
		int numberOfProvidedCows = (numberOfCowsEach + remainCow) * X[COW_TYPE_COUNT - 1][i];
		for (int k = 0; k < numberOfProvidedCows; k++)
		{
			_provides[idx].facilityId = _facilities[numberOfFacsEach*i].id;
			_provides[idx].cowId = _cows[numberOfCowsEach*(COW_TYPE_COUNT - 1) + k].id;
			idx++;
		}
	}
	_providesCount = idx;
}

void output()
{
	for (int i = 0; i < N; i++)
	{
		cout << _cows[i].id << "\t" 
			<< _cows[i].name << "\t" 
			<< _cows[i].cowType << endl;
	}
	for (int i = 0; i < P; i++)
	{
		cout << _companies[i].id << "\t" 
			<< _companies[i].name << endl;
	}
	for (int i = 0; i < M; i++)
	{
		cout << _facilities[i].id << "\t" 
			<< _facilities[i].name << "\t" 
			<< _facilities[i].companyId << endl;
	}
	for (int i = 0; i < _providesCount; i++)
	{
		cout << _provides[i].cowId << "\t"
			<< _provides[i].facilityId << endl;
	}

}

int main()
{
	process();
	output();

	delete[] _cows;
	delete[] _companies;
	delete[] _facilities;
	delete[] _provides;
	return 0;
}

 

Sau đó với 1 chút kiến thức về SQL trong môn nhập môn Cơ sở dữ liệu em ấy có thể tự nghiên cứu tiếp cách đọc ghi vào cơ sở dữ liệu quan hệ. Nhưng cài đặt và cấu hình thư viện để truy xuất được database không dễ đối với người chưa có kinh nghiệm. Vậy cách hay ở đây để tránh research lan man là đọc từ file text và xuất script SQL ra file text và bỏ vào MySQL chạy.

void inputFromFile()
{
	ifstream fin("E:\\code\\Cow\\Debug\\input.txt");
	//format
	//N M P
	//X11 X12 X13 ... 
	//X21 X22 X23 ...
	//X31 X32 X33 ...
	fin >> N >> M >> P;
	for (int i = 0; i < COW_TYPE_COUNT; i++)
	{
		for (int j = 0; j < P; j++)
		{
			fin >> X[i][j];
		}
	}
	fin.close();
}

void outputToFile()
{
	ofstream fout("E:\\code\\Cow\\Debug\\ouput.sql");
	for (int i = 0; i < N; i++)
	{
		fout << "insert into cows values("
			<< _cows[i].id << ","
			<< "'" << _cows[i].name << "',"
			<< _cows[i].cowType << ");" << endl;
	}
	for (int i = 0; i < P; i++)
	{
		fout << "insert into companies values("
			<< _companies[i].id << ","
			<< "'" << _companies[i].name << "');" << endl;
	}
	for (int i = 0; i < M; i++)
	{
		fout << "insert into facilities values("
			<< _facilities[i].id << ","
			<< "'" << _facilities[i].name << "',"
			<< _facilities[i].companyId << ");" << endl;
	}
	for (int i = 0; i < _providesCount; i++)
	{
		fout << "insert into provides values("
			<< _provides[i].cowId << ","
			<< _provides[i].facilityId << ");" << endl;
	}
	fout.close();
}

int main()
{
	inputFromFile();
	process();
	output();
	outputToFile();

	delete[] _cows;
	delete[] _companies;
	delete[] _facilities;
	delete[] _provides;
	return 0;
}

2016-04-26 13_07_09-input.txt - Notepad
2016-04-26 13_07_28-ouput.sql - Notepad

 

Sở dĩ chọn MySQL vì có thể tận dụng câu lệnh SQL query ra dạng bảng và dựa vào Pivot table của Excel để thể hiện dạng bảng:

SELECT A.cowType, A.coId, A.coName, A.cowCount/B.cowCount rate
FROM (SELECT cw.cowType as cowType, co.id as coId, co.name as coName, count(*) as cowCount
FROM Provides pr, Cows cw, Facilities fa, Companies co
WHERE pr.cowId = cw.id and pr.facilityId = fa.id and fa.companyId = co.id
GROUP By cw.cowType, co.id)A
,
(SELECT cowType, count(*) as cowCount
FROM Cows
GROUP BY cowType)B
WHERE A.cowType = B.cowType

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s