Sort 1 triệu số thực 32 bit mà giới hạn bộ nhớ là 3MB

Làm thế nào để sort một mảng mà bộ nhộ chương trình cung cấp quá ít không đủ để đọc hết mảng.

Đây là ví dụ:

Cách giải quyết của mình.

Vì mình thấy 3mb chỉ lưu được khoảng hơn 770k phần tử float nên mình chọn 600k làm phần tử làm mốc để xử lý nhau sau:

Với trường hợp n (số phần tử của chương trình) < 600k… mình quicksort bình thường vì vẫn thõa mãn 3mb

Với trường họp n > 600k: thì mình đọc n/2 phần tử đầu và sort và ghi vào file1
mình đọc tiếp n - n/2 phần tử tiếp theo sort và ghi vào file2
(làm như vậy vì khi n > 3*1024*1024/4 thì mình không thể scanf được)
Cụ thể như ở đây:

#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>
 
 
typedef long long ll;
 
//[Codeforces Round #]
 
using namespace std;
 
float a[600000],b1, b2;
int n, m;
FILE *out1, *out2, *in1, *in2;
// phare 1
void process(){
 
	int i, j;
 
	out1 = fopen("1.tmp", "w");
	out2 = fopen("2.tmp", "w");
 
//	scanf("%d\n", &n);
	m = n/2;
	n -= m;
	for(i=1; i<=n; i++) scanf("%f", &a[i]);
	sort(a+1, a+n+1);
	for(i=1; i<=n; i++) fprintf(out1, "%.2f ", a[i]);
 
 
 
	for(i=1; i<=m; i++) scanf("%f", &a[i]);
	sort(a+1, a+m+1);
	for(i=1; i<=m; i++) fprintf(out2, "%.2f ", a[i]);
 
	fclose(out1);
	fclose(out2);
 
	in1 = fopen("1.tmp", "r");
	in2 = fopen("2.tmp", "r");
 
	i = 1;	j = 1;
	fscanf(in1, "%f", &b1);
	fscanf(in2, "%f", &b2);
	while(i <= n && j <= m){
		if (b1 < b2){
			i++;
			printf("%.2f ", b1);
			if (i<=n)
				fscanf(in1, "%f", &b1);
		}
		else{
			j++;
			printf("%.2f ", b2);
			if (j<=m)
				fscanf(in2, "%f", &b2);
		}
	}
	while(i++ <= n){
		printf("%.2f ", b1);
		fscanf(in1, "%f", &b1);
	}
	while(j++ <= m){
		printf("%.2f ", b2);
		fscanf(in2, "%f", &b2);
	}
}
 
// phare 2
void swap_(float *a, float *b) {
    float tmp = *a;
    *a = *b;
    *b = tmp;
}
 
int partition_(int left, int right) {
    float pivot = a[right];
    int i = left - 1;
 
    for(int j = left; j <= right - 1; j ++) {
        if(a[j] > pivot) {
            i++;
            swap_(&a[j],&a[i]);
        }
    }
    swap_(&a[i+1],&a[right]);
    return (i+1);
}
 
void quickSort(int left, int right)
{
    if (left < right)
    {
        int pivot_index = partition_(left, right);
        quickSort(left, pivot_index - 1);
        quickSort(pivot_index + 1, right);
    }
}
void input( int n) {
    for(int i = 0; i < n ; i ++) {
        scanf("%f", &a[i]);
    }
}
void output(int n) {
    for(int i = n - 1; i >= 0 ; i--) {
        printf("%.2f ", a[i]);
    }
}
 
 
int main(){
    scanf("%d",&n);
    if( n > 600000) {
        	process();
    } else {
        input(n);
        quickSort(0,n-1);
        output(n);
    }
	return 0;
}

Nhưng vấn đề của mình gặp phải tiếp theo là TLE.

Mọi người xem và giúp mình tìm cách khắc phục với.

2 Likes

Hi BK Anonymous.

  1. Vấn đề có lẽ nằm ở phần đọc ghi file của bạn. Thây vì đọc ghi theo từng biến scan, Thì thử đọc ghi theo từng khối một.
  2. Theo mình trước sẽ xắp xếp quickSort trên các đoạn 500k phần còn lại dùng cho bộ đệm để tăng tốc đọc ghi. Sau đó ghép các đoạn 250k (2 đoạn nhỏ và một đoạn lớn kem thêm bộ đệm).
3 Likes

Chắc phải desync iostream với stdin.

4 Likes

giới hạn thời gian là bao nhiêu, có thể ghi xuống dạng nhị phân chi có 4 byte thay vì ghi xuống dạng chuỗi có thể 6-7 byte gì đấy :V

6 Likes

nếu ghi từng float xuống thì lúc merge lên lại xài 1 dòng std::merge là được rồi @_@

merge(istream_iterator<float>(ifs1), istream_iterator<float>(),
      istream_iterator<float>(ifs2), istream_iterator<float>(),
      ostream_iterator<float>(cout, " "));

:V

4 Likes

Mình không dành về C++, cho mình hỏi sao lại lỗi begin với end vậy.

TLE là là gì vậy?
Cái mảng a[600000] trong code kia : 1 nếu bạn nhập vào n = 700000 nó sẽ bị tràn( line 28, 30 khi i > 600000) , 2 là nguyên cái mảng đó khi chạy chương trình lên nó sẽ được lưu trong stack, nguyên nó đã chiếm mất gần 2.4 MB rồi

Đầu tiên để khả thi bạn cần sửa lại input. Input là mảng a và số thực n thì chỉ có n là nhập từ bàn phím thôi, còn data nên lưu trong file “data.txt” . Vì nếu làm như hiện tại cho dù bạn có chạy được cái chương trình kia lên thì cũng không có sức đâu mà nhập 60-100 vạn số thực bằng tay để mà chạy hàm process.
Cần có ít nhất 2 file vì có 2 kịch bản, 1 là < 3MB ( file chứa khoảng 10 vạn số float) và 2 là > 3MB( file chứa khoảng 80 vạn số float). Data thì tự tạo ra.
Hoặc để dễ kiểm tra thuật toán hơn nữa bạn có thể thu nhỏ lại, memory limit 3KB, n = 1000 chẳng hạn, OK rồi hãng làm trường hợp 3mb. Yêu cầu time limit per test 1.5s là rất mơ hồ, vì CPU khác nhau thời gian xử lỹ sẽ khác nhau, ví dụ Core i5 7500 3.4Ghz sẽ khác hẳn 1 con core i3 U bèo bèo cho laptop.

Khi xong input rồi bạn có thể tiếp tục như đã làm ở bên trên :

  1. nên set size của mảng a = N/2. đọc từ file data.txt vào mảng a, sort rồi ghi vào file1, đọc phần tiếp theo của file data.txt vào mảng a, sort rồi ghi vào file2.
  2. Sau mỗi lần sort lưu lại MIN và MAX của mỗi file để từ đó xử lý phần data giao cắt giữa 2 file.
  3. Đọc lần lượt phần giao cắt của mỗi file vào mảng a, sort rồi cho vào file3. Nếu size của phần này lớn quá kích thước mảng a thì lại chia nhỏ tiếp.
  4. Lần lượt in ra kết quả

Nếu chưa quen các thao tác với file ( open, close, seek …) thì nên tạo dump code chạy được mấy cái món đó đã, đừng nhảy luôn vào code chương trình chính, nếu không rất khó tìm ra lỗi.

2 Likes
  1. Không tràn ban nha: Mình luôn thao tác trong mảng a[600000] phần tử cho dù đầu vào có bao nhiều phân tử ( lơn hơn 600k)

  2. Dĩ nhiên là có test cho mình dùng rồi, hơi đâu nhập nhiều phần từ. Còn mình tự test thì mình cũng tự tạo file mà test thử với 900k phần từ vẫn đúng

  3. TEL(Time limited exceeded) là không thõa mãn thời gian yêu cầu của test, chương trình của mình chạy đúng và giờ chỉ cần cải thiện thời gian thôi, mình nghĩ vấn đề không nằm ở bộ nhớ nữa

  4. variable global không nằm ở stack memory mà.

Bây giờ mình đang thử test với ghi theo block. không biết thời gian có tốt hơn không.

1 Like

Mình chưa hiểu cách diễn đặt ở ý thứ 2 của bạn lắm, bạn có thể nói diễn giải lại được không?

Cám ơn mọi người mình đã giải quyết được rồi nha,

Spoiler

Giải pháp là không cần phải ghi 2 cả chuỗi từ 1- n và chuôi n + 1 đến n+ m+1 mà chỉ cần lưu chuổi 1-n thôi.

3 Likes

Vậy là

Spoiler
  1. Nạp hết nửa đầu mảng lên mem
  2. Sắp xếp và lưu nửa đầu mảng
  3. Nạp hết nửa sau mảng
  4. Sắp xếp nửa sau mảng
  5. Trộn nửa sau với nửa đầu mảng vào file
4 Likes

Hi BK Anonymous.
Như bạn phân tích thì không xắp xếp được hết nên sẽ xắp xếp từng phần và sau đó ghép lại. Đầu tiên bạn đọc lên một ít sau đó xắp xếp rồi ghi xuống. Tốt nhất nên ghi file dạng nhị phân và đẩy cả mảng cấp phát động đó xuống một lần fwrite. Tương tự với phần còn lại. Sau đó khi trộn thì bạn dùng fread đọc một phần lên rồi ép kiểu mảng char về float luôn. Sau đó trộn dần như bạn trên nói. Tiếp đến khi ghi xuông thì dùng stringstream rồi ghi xuống theo lô luôn.

P/S Các bài tối ưu code thường cần bạn nắm khá vững về ngôn ngữ nên hãy dùng cái nào bạn dành nhất.

2 Likes

ra là chỉ cần ghi 1 nửa là đc :V :V

code ngắn gọn :V

Spoiler
#include <iostream>
#include <algorithm>
#include <iterator>
#include <fstream>
#include <iomanip>
using namespace std;

float a[500000];

int main()
{
    std::ios::sync_with_stdio(false);
    
    int n;
    cin >> n;
    
    // first half
    int size1 = n / 2;
    for (int k = 0; k < size1; cin >> a[k++]);
    sort(a, a + size1);
    // write to file
    ofstream ofs("a.txt");
    copy(a, a + size1, ostream_iterator<float>(ofs, " "));
    ofs.close();
    
    // second half
    int size2 = n - n / 2;
    for (int k = 0; k < size2; cin >> a[k++]);
    sort(a, a + size2);
    
    // merge
    ifstream ifs("a.txt"); // re-open first half
    cout << fixed << setprecision(2);
    merge(istream_iterator<float>(ifs), istream_iterator<float>(),
          a, a + size2, ostream_iterator<float>(cout, " "));
}

edit: thêm setprecision :V

3 Likes

cái #5 mình chưa hiểu lắm. Nhưng đến 4 thì đã có

n phần tử từ 1 - n được sắp xếp và ở trong file

m phần tử (index từ n + 1 đến n + m + 1) được sắp xếp và lưu ở mảng a (ban đầu)

Việc cần làm bây giờ là so sánh 2 mảng đã sắp và prinf giá trị nhỏ hơn đến khi hết index thôi

OK cám ơn bạn, nhưng gì bạn nói, thật sự mình chưa hiểu hết, nhất là phần read/write mình không rành.

Hi BK Anonymous.
Bạn có thể thử đoạn code sau.

Code

File văn bản.

#include <fstream>                                                                                       
#include <iostream>                                                                                      
#include <sstream>                                                                                       
#include <string>                                                                                        
                                                                                                         
using namespace std;                                                                                     
                                                                                                         
#define FILE_NAME "data.txt"                                                                             
#define COUNT 10000000                                                                                   
                                                                                                         
void create(string);                                                                                     
void read(string);                                                                                       
                                                                                                         
int main(int argc, char **argv) {                                                                        
    if(argc < 2) {                                                                                       
        create(FILE_NAME);                                                                               
    }                                                                                                    
    if(argc > 1) {                                                                                       
        read(argv[1]);                                                                                   
    }                                                                                                    
return 0;                                                                                                
}                                                                                                        
                                                                                                         
void create(string fileName) {                                                                           
    ofstream file;                                                                                       
    file.open(fileName);                                                                                 
    for(int index = 0; index < COUNT; index++) {                                                         
        file << index << " ";                                                                            
    }                                                                                                    
    file.close();                                                                                        
}                                                                                                        
                                                                                                         
void read(string fileName) {                                                                             
    ifstream file;                                                                                       
    file.open(fileName);                                                                                 
    int value;                                                                                           
    for(int index = 0; index < COUNT; index++) {                                                         
        file >> value;                                                                                   
    }                                                                                                    
    file.close();                                                                                        
}

File nhị phân.

#include <fstream>>
#include <iostream>                                                                                      
#include <sstream>                                                                                       
#include <string>                                                                                        
                                                                                                         
using namespace std;                                                                                     
                                                                                                         
#define FILE_NAME "data.bin"                                                                             
#define COUNT 10000000                                                                                   
                                                                                                         
void create(string);                                                                                     
void read(string);                                                                                       
                                                                                                         
int main(int argc, char **argv) {                                                                        
    if(argc < 2) {                                                                                       
        create(FILE_NAME);                                                                               
    }                                                                                                    
    if(argc > 1) {                                                                                       
        read(argv[1]);                                                                                   
    }                                                                                                    
return 0;                                                                                                
}                                                                                                        
                                                                                                         
void create(string fileName) {                                                                           
    int *buffer = new int[COUNT];                                                                        
    for(int index = 0; index < COUNT; index++) {                                                         
        buffer[index] = index;                                                                           
    }                                                                                                    
    ofstream file;                                                                                       
    file.open(fileName, ios::binary);                                                                    
    file.write((char *) buffer, sizeof(float) * COUNT);                                                  
    file.close();                                                                                        
    delete [] buffer;                                                                                    
}                                                                                                        
                                                                                                         
void read(string fileName) {                                                                             
    int *buffer = new int[COUNT];                                                                        
    ifstream file;                                                                                       
    file.open(fileName, ios::binary);                                                                    
    file.read((char *) buffer, sizeof(float) * COUNT);                                                   
    file.close();                                                                                        
    delete [] buffer;                                                                                    
} 
4 Likes

4 posts were merged into an existing topic: Topic lưu trữ các post off-topic - version 3

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?