C++ Algorithm Generate | "생성 알고리즘" 가이드
이 글의 핵심
C++ Algorithm Generate에 대한 실전 가이드입니다.
들어가며
STL 생성 알고리즘은 컨테이너를 값으로 채우거나 함수로 생성하는 기능을 제공합니다. fill, generate, iota 등을 활용하면 초기화 코드를 간결하고 효율적으로 작성할 수 있습니다.
1. std::fill
기본 사용
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v(10);
// 모두 42로 채우기
std::fill(v.begin(), v.end(), 42);
for (int x : v) {
std::cout << x << " "; // 42 42 42 42 42 42 42 42 42 42
}
std::cout << std::endl;
return 0;
}
std::fill_n
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v(10, 0); // 모두 0으로 초기화
// 처음 5개만 1로
std::fill_n(v.begin(), 5, 1);
for (int x : v) {
std::cout << x << " "; // 1 1 1 1 1 0 0 0 0 0
}
std::cout << std::endl;
return 0;
}
함수 시그니처:
template<class ForwardIt, class T>
void fill(ForwardIt first, ForwardIt last, const T& value);
template<class OutputIt, class Size, class T>
OutputIt fill_n(OutputIt first, Size count, const T& value);
2. std::generate
기본 사용
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v(10);
int counter = 0;
// 함수로 생성
std::generate(v.begin(), v.end(), [&counter]() {
return counter++;
});
for (int x : v) {
std::cout << x << " "; // 0 1 2 3 4 5 6 7 8 9
}
std::cout << std::endl;
return 0;
}
std::generate_n
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v;
// back_inserter로 자동 확장
std::generate_n(std::back_inserter(v), 5, {
return rand() % 100;
});
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
함수 시그니처:
template<class ForwardIt, class Generator>
void generate(ForwardIt first, ForwardIt last, Generator g);
template<class OutputIt, class Size, class Generator>
OutputIt generate_n(OutputIt first, Size count, Generator g);
3. std::iota
기본 사용
#include <numeric>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v(10);
// 1부터 순차 생성
std::iota(v.begin(), v.end(), 1);
for (int x : v) {
std::cout << x << " "; // 1 2 3 4 5 6 7 8 9 10
}
std::cout << std::endl;
return 0;
}
다양한 타입
#include <numeric>
#include <vector>
#include <iostream>
int main() {
// double로 순차 생성
std::vector<double> v(5);
std::iota(v.begin(), v.end(), 1.5);
// 1.5, 2.5, 3.5, 4.5, 5.5
// char로 순차 생성
std::vector<char> chars(5);
std::iota(chars.begin(), chars.end(), 'A');
// 'A', 'B', 'C', 'D', 'E'
for (double d : v) {
std::cout << d << " ";
}
std::cout << std::endl;
for (char c : chars) {
std::cout << c << " ";
}
std::cout << std::endl;
return 0;
}
4. 실전 예제
예제 1: 난수 생성
#include <algorithm>
#include <vector>
#include <random>
#include <iostream>
int main() {
std::vector<int> v(10);
// 난수 생성기 설정
std::mt19937 gen{std::random_device{}()};
std::uniform_int_distribution<> dist{1, 100};
// 난수로 채우기
std::generate(v.begin(), v.end(), [&]() {
return dist(gen);
});
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
예제 2: 함수 객체 (Functor)
#include <algorithm>
#include <vector>
#include <iostream>
class Counter {
int count;
int step;
public:
Counter(int start = 0, int step = 1)
: count(start), step(step) {}
int operator()() {
int result = count;
count += step;
return result;
}
};
int main() {
std::vector<int> v(10);
// 0, 2, 4, 6, ...
std::generate(v.begin(), v.end(), Counter{0, 2});
for (int x : v) {
std::cout << x << " "; // 0 2 4 6 8 10 12 14 16 18
}
std::cout << std::endl;
return 0;
}
예제 3: 구조체 초기화
#include <algorithm>
#include <vector>
#include <iostream>
struct Point {
int x, y;
};
int main() {
std::vector<Point> points(5);
int id = 0;
std::generate(points.begin(), points.end(), [&id]() {
return Point{id, id * 10};
id++;
});
for (const auto& p : points) {
std::cout << "(" << p.x << ", " << p.y << ") ";
}
std::cout << std::endl;
return 0;
}
예제 4: ID 생성기
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
class IDGenerator {
std::string prefix;
int counter;
public:
IDGenerator(const std::string& prefix)
: prefix(prefix), counter(1) {}
std::string operator()() {
return prefix + std::to_string(counter++);
}
};
int main() {
std::vector<std::string> ids(5);
std::generate(ids.begin(), ids.end(), IDGenerator{"USER_"});
for (const auto& id : ids) {
std::cout << id << std::endl;
}
// USER_1
// USER_2
// USER_3
// USER_4
// USER_5
return 0;
}
5. 생성 알고리즘 비교
| 알고리즘 | 헤더 | 용도 | 시간 복잡도 |
|---|---|---|---|
fill | <algorithm> | 고정 값으로 채우기 | O(N) |
fill_n | <algorithm> | N개를 고정 값으로 | O(N) |
generate | <algorithm> | 함수로 생성 | O(N) |
generate_n | <algorithm> | N개를 함수로 생성 | O(N) |
iota | <numeric> | 순차 증가 값 생성 | O(N) |
함수 시그니처:
// fill
template<class ForwardIt, class T>
void fill(ForwardIt first, ForwardIt last, const T& value);
// generate
template<class ForwardIt, class Generator>
void generate(ForwardIt first, ForwardIt last, Generator g);
// iota
template<class ForwardIt, class T>
void iota(ForwardIt first, ForwardIt last, T value);
6. 자주 발생하는 문제
문제 1: 크기 미지정
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v; // 크기 0
// ❌ 크기가 0이면 아무 일도 안 함
std::fill(v.begin(), v.end(), 42);
std::cout << v.size() << std::endl; // 0
// ✅ 크기 지정
v.resize(10);
std::fill(v.begin(), v.end(), 42);
std::cout << v.size() << std::endl; // 10
// ✅ 또는 생성자에서 크기 지정
std::vector<int> v2(10);
std::fill(v2.begin(), v2.end(), 42);
return 0;
}
해결책: 컨테이너 크기를 미리 지정하거나 back_inserter를 사용하세요.
문제 2: 캡처 방식
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
int counter = 0;
std::vector<int> v(10);
// ❌ 값 캡처 (복사본 수정)
std::generate(v.begin(), v.end(), [=]() mutable {
return counter++; // 복사본만 증가
});
std::cout << "counter: " << counter << std::endl; // 0 (변경 안됨)
// ✅ 참조 캡처
counter = 0;
std::generate(v.begin(), v.end(), [&counter]() {
return counter++; // 원본 증가
});
std::cout << "counter: " << counter << std::endl; // 10
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
해결책: 상태를 유지하려면 참조 캡처([&])를 사용하세요.
문제 3: 난수 생성
#include <algorithm>
#include <vector>
#include <random>
#include <iostream>
int main() {
std::vector<int> v(10);
// ❌ rand() (C 스타일, 품질 낮음)
std::generate(v.begin(), v.end(), {
return rand() % 100;
});
// ✅ C++11 random (품질 높음)
std::mt19937 gen{std::random_device{}()};
std::uniform_int_distribution<> dist{0, 99};
std::generate(v.begin(), v.end(), [&]() {
return dist(gen);
});
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
해결책: C++11 <random> 라이브러리를 사용하세요.
문제 4: 성능
#include <algorithm>
#include <vector>
#include <chrono>
#include <iostream>
int main() {
std::vector<int> v(1000000);
// fill: 최적화됨 (빠름)
auto start1 = std::chrono::high_resolution_clock::now();
std::fill(v.begin(), v.end(), 0);
auto end1 = std::chrono::high_resolution_clock::now();
// generate: 함수 호출 오버헤드 (느림)
auto start2 = std::chrono::high_resolution_clock::now();
std::generate(v.begin(), v.end(), { return 0; });
auto end2 = std::chrono::high_resolution_clock::now();
auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(end1 - start1).count();
auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>(end2 - start2).count();
std::cout << "fill: " << duration1 << " μs" << std::endl;
std::cout << "generate: " << duration2 << " μs" << std::endl;
return 0;
}
해결책: 고정 값은 fill을, 동적 생성은 generate를 사용하세요.
7. 실전 예제: 테스트 데이터 생성기
#include <algorithm>
#include <numeric>
#include <vector>
#include <random>
#include <iostream>
#include <string>
class TestDataGenerator {
std::mt19937 gen;
public:
TestDataGenerator() : gen(std::random_device{}()) {}
// 난수 배열 생성
std::vector<int> randomInts(size_t count, int min, int max) {
std::vector<int> result(count);
std::uniform_int_distribution<> dist{min, max};
std::generate(result.begin(), result.end(), [&]() {
return dist(gen);
});
return result;
}
// 순차 배열 생성
std::vector<int> sequence(size_t count, int start = 0) {
std::vector<int> result(count);
std::iota(result.begin(), result.end(), start);
return result;
}
// 고정 값 배열 생성
std::vector<int> constant(size_t count, int value) {
std::vector<int> result(count);
std::fill(result.begin(), result.end(), value);
return result;
}
// 패턴 배열 생성
std::vector<int> pattern(size_t count, const std::vector<int>& pattern) {
std::vector<int> result(count);
size_t patternSize = pattern.size();
std::generate(result.begin(), result.end(), [&, i = 0]() mutable {
return pattern[i++ % patternSize];
});
return result;
}
// 사용자 정의 생성
template<typename Generator>
std::vector<int> custom(size_t count, Generator gen) {
std::vector<int> result(count);
std::generate(result.begin(), result.end(), gen);
return result;
}
};
int main() {
TestDataGenerator tdg;
// 난수 10개 (1~100)
auto random = tdg.randomInts(10, 1, 100);
std::cout << "난수: ";
for (int x : random) std::cout << x << " ";
std::cout << std::endl;
// 순차 10개 (0부터)
auto seq = tdg.sequence(10);
std::cout << "순차: ";
for (int x : seq) std::cout << x << " ";
std::cout << std::endl;
// 고정 값 10개
auto constant = tdg.constant(10, 42);
std::cout << "고정: ";
for (int x : constant) std::cout << x << " ";
std::cout << std::endl;
// 패턴 반복 (1, 2, 3, 1, 2, 3, ...)
auto patt = tdg.pattern(10, {1, 2, 3});
std::cout << "패턴: ";
for (int x : patt) std::cout << x << " ";
std::cout << std::endl;
// 사용자 정의 (제곱수)
auto custom = tdg.custom(5, [i = 0]() mutable {
return i * i++;
});
std::cout << "제곱: ";
for (int x : custom) std::cout << x << " ";
std::cout << std::endl;
return 0;
}
정리
핵심 요약
- fill: 고정 값으로 채우기 (빠름)
- fill_n: N개를 고정 값으로
- generate: 함수로 생성 (유연함)
- generate_n: N개를 함수로 생성
- iota: 순차 증가 값 생성 (
<numeric>)
생성 알고리즘 선택 가이드
| 상황 | 권장 알고리즘 | 이유 |
|---|---|---|
| 고정 값 | fill | 최적화, 빠름 |
| 순차 값 | iota | 간결함 |
| 난수 | generate + <random> | 품질 높음 |
| 복잡한 로직 | generate + 람다 | 유연함 |
| 상태 유지 | generate + 참조 캡처 | 상태 공유 |
실전 팁
성능:
- 고정 값은
fill사용 (최적화됨) generate는 함수 호출 오버헤드 있음- 대량 데이터는 병렬 알고리즘 고려 (
std::execution::par)
가독성:
- 간단한 순차 값은
iota - 복잡한 로직은 함수 객체로 분리
- 람다는 간결하게 유지
주의사항:
- 컨테이너 크기 미리 지정
- 상태 유지는 참조 캡처
- 난수는 C++11
<random>사용
다음 단계
- C++ Algorithm Reverse
- C++ Algorithm Count
- C++ Algorithm Transform
관련 글
- C++ Algorithm Copy |