[2026] C++ Algorithm Generate | 생성 알고리즘 가이드

[2026] C++ Algorithm Generate | 생성 알고리즘 가이드

이 글의 핵심

C++ fill, generate, iota로 범위를 채우고 연속 값을 만드는 법. 람다·함수 객체와 함께 쓰는 생성 알고리즘 활용을 다룹니다.

들어가며

STL 생성 알고리즘은 컨테이너를 값으로 채우거나 함수로 생성하는 기능을 제공합니다. fill, generate, iota 등을 활용하면 초기화 코드를 간결하고 효율적으로 작성할 수 있습니다.

코딩 테스트 준비하며 깨달은 것

알고리즘 문제를 풀다 보면 “이게 실무에 무슨 도움이 될까?” 하는 의문이 들 때가 있습니다. 저도 그랬습니다. 하지만 실제 프로젝트에서 성능 문제에 부딪히면, 알고리즘 지식이 얼마나 중요한지 깨닫게 됩니다. 예를 들어, 사용자 검색 기능이 느려서 고민하다가 해시 테이블을 적용하니 응답 시간이 10초에서 0.1초로 줄어든 경험이 있습니다. 코딩 테스트는 단순히 취업을 위한 관문이 아니라, 문제 해결 능력을 키우는 훈련장입니다. 처음엔 브루트 포스로 풀다가, 시간 복잡도를 개선하는 과정에서 “아, 이렇게 생각하면 되는구나” 하는 깨달음을 얻을 때의 쾌감은 말로 표현하기 어렵습니다. 이 글에서는 단순히 정답 코드만 제시하는 게 아니라, 문제를 어떻게 접근하고 최적화하는지 사고 과정을 함께 공유하겠습니다.

1. std::fill

기본 사용

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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;
}

함수 시그니처: 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

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

기본 사용

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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;
}

함수 시그니처: 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

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

기본 사용

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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;
}

다양한 타입

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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: 난수 생성

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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)

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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: 구조체 초기화

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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 생성기

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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)
함수 시그니처:
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 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: 크기 미지정

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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: 캡처 방식

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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: 난수 생성

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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: 성능

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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. 실전 예제: 테스트 데이터 생성기

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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;
}

정리

핵심 요약

  1. fill: 고정 값으로 채우기 (빠름)
  2. fill_n: N개를 고정 값으로
  3. generate: 함수로 생성 (유연함)
  4. generate_n: N개를 함수로 생성
  5. iota: 순차 증가 값 생성 (<numeric>)

생성 알고리즘 선택 가이드

상황권장 알고리즘이유
고정 값fill최적화, 빠름
순차 값iota간결함
난수generate + <random>품질 높음
복잡한 로직generate + 람다유연함
상태 유지generate + 참조 캡처상태 공유

실전 팁

성능:

  • 고정 값은 fill 사용 (최적화됨)
  • generate는 함수 호출 오버헤드 있음
  • 대량 데이터는 병렬 알고리즘 고려 (std::execution::par) 가독성:
  • 간단한 순차 값은 iota
  • 복잡한 로직은 함수 객체로 분리
  • 람다는 간결하게 유지 주의사항:
  • 컨테이너 크기 미리 지정
  • 상태 유지는 참조 캡처
  • 난수는 C++11 <random> 사용

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3