разделение чисел на три фактора

Цель этого кода — использовать его для эффективного вычисления некоторой теоретико-числовой функции для чисел от 1 до 1000000, но я не уверен, будет ли он действительно полезен. Основная цель заключалась в том, чтобы запрограммировать кусок кода C ++, потому что у меня почти нет опыта в этом языке. Так что было бы неплохо, если бы вы могли дать мне обратную связь не по алгоритму, а по его реализации на C ++. Есть также некоторые вещи, которые мне непонятны: следует ли избегать дублирования блока с оператором case? Следует ли избегать использования глобальных переменных? Уместно ли использование массива или мне следует использовать какой-либо тип данных контейнера?

/*
if a positive integer number n has a prime divisor p>=sqrt(n), then n=p*q and p and q are uniquely defined and (p,q)=1 and q<=sqrt(n).
More general: if n is a positive integer number then there is a prime p such that n=(p^e)*q1*q2, such that (p,q1)=(p,q2)=(q1,q2)=1 and q1<=sqrt(n), q2<=sqrt(n). p,e,q1,q2 are not uniquely define.
this program calculates and stores such factors q1,q2,p^e, for every n between sqrt(limit) and limit and prints some sample output
*/
#include <iostream>
#include <cmath>
#include <cassert> 
const int limit=1000000;
int sqrtlimit;

int primefactor[limit]={0};

int smallfactor1[limit]={0};
int primepowerfactor[limit]={0};
int smallfactor2[limit]={0};


void init_sieve(void){
    /* 
    initializes the array primefactor
    primefactor[n]==n if n is a prime or n==1 or n==0, otherwise
    primefactor[n] is largest prime factor of n that 
    is smaller than sqrtkimit
    */
    sqrtlimit=int(std::pow(limit,.5))+1;
    for (int n=0;n<limit;n++){
        primefactor[n]=n;
    }
    for (int n=2;n<=sqrtlimit;n++){
        if (primefactor[n]==n){
            for (int i=n;i<limit;i+=n){
                primefactor[i]=n;
            }
        }
    }
}

void split_factors(void){
    /* 
    uses the initialized array primefactor
    sets the arrays smallfactor1, smalfactor2, primepowerfactor
    splits a number in three, pairwise relativly prime factors
    where the first and the third are smaller than sqrtlimit and the  second is a prime power
    */
    enum State {
        continue_first_factor,
        in_second_factor,
        continue_third_factor
        };
    for (int n=sqrtlimit; n<limit; n++){
        int current_prod=1;
        int quotient=n;
        int last_prime=0;
        int prime_exponent=0;
        int first_factor=1;
        int second_factor=1;
        int third_factor=1;
        int prime_power=1;
        int p;
        State state=continue_first_factor;
        while(primefactor[quotient]>1){
            p=primefactor[quotient];
            quotient/=p;
            /*
            if last prime power was found, 
            put it to first factor
            and restart calculating the next prime power
            */
            if (p!=last_prime){
                switch (state) {
                    case continue_first_factor:
                        first_factor*=prime_power;
                        break;
                    case in_second_factor:
                        second_factor=prime_power;
                        current_prod=1;
                        state=continue_third_factor;
                        break;
                    case continue_third_factor:
                        third_factor*=prime_power;
                        break;
                    default:
                        assert(0);
                        break;
                }
                last_prime=p;
                prime_power=p;
            }
            else {
                prime_power*=p;
            }
            if (state==continue_first_factor){
                current_prod*=p;
                if (current_prod>sqrtlimit){
                    state=in_second_factor;
                }
            }
            /* process the last prime power */
            if (quotient==1){
                switch (state) {
                    case continue_first_factor:
                        first_factor*=prime_power;
                        break;
                    case in_second_factor:
                        second_factor=prime_power;
                        current_prod=1;
                        state=continue_third_factor;
                        break;
                    case continue_third_factor:
                        third_factor*=prime_power;
                        break;
                    default:
                        assert(0);
                        break;
                }
            }
        }
        smallfactor1[n]=first_factor;
        primepowerfactor[n]=second_factor;
        smallfactor2[n]=third_factor;
    }
}

int main() {
    init_sieve();
    split_factors();  
    // some sample output
    std::cout<<"limit "<<limit<<std::endl;
    std::cout<<"sqrtlimit "<<sqrtlimit<<std::endl;
    std::cout<<std::endl;
    std::cout<<"print some prime factorization"<<std::endl;
    for (int n=limit-20;n<limit; n++){
        int m=n;
        std::cout << m << " = " ;
        bool first=true;
        while (m>1){
            if (first){
                first=false;
            }
            else {
                std::cout << " * ";
            }
            std::cout <<primefactor[m];
            m=m/primefactor[m];
        }
        std::cout <<std::endl;
    }
    std::cout<<std::endl<<"smallfactor * primepower * smallfactor"<<std::endl;
    std::cout<<"tthe second factor is a prime power"<<std::endl;
    std::cout<<"t'!' at the end prime power exponent > 1"<<std::endl;

    for (int n=limit-20;n<limit; n++){
        std::cout<<n<<" = "<<smallfactor1[n]<<" * "<<primepowerfactor[n]<<" * "<<smallfactor2[n];
        if (primefactor[primepowerfactor[n]]!=primepowerfactor[n]){
            std::cout<<" !";
        }
        std::cout<<std::endl;
    }
}

1 ответ
1

Вопросов

Вы просили дать обзор C ++, а не алгоритма, так что я сделаю это. Но сначала я отвечу на ваши вопросы.

Следует ли избегать дублирования блока с оператором case?

Да.

Если это не В самом деле банально — мы говорим как две строчки, может быть three, max, и даже тогда, только если эти строки не являются сложными или «умными» — вы никогда не должны стесняться разбивать повторяющиеся фрагменты кода на отдельные функции.

Фактически, забудьте о «повторении»; Каждый раз, когда у вас есть часть алгоритма, которая кажется несколько замкнутой, вам следует подумать о ее разделении. Но это, очевидно, особенно верно для повторяющихся блоков.

Есть много на то есть веские причины:

  • Упрощение: Он делает любой код, в котором повторяется блок, короче и проще, и это обычно является преимуществом для понимания и обслуживания.
  • Возможность повторного использования: Если вы использовали его дважды, есть большая вероятность, что вы сможете использовать его трижды, и в конечном итоге вы сэкономите время.
  • Модуляризация: Когда вы разбиваете кусок кода на отдельную функцию, вы можете рассматривать функцию как отдельный блок. Можете протестировать отдельно. Вы можете оптимизировать его отдельно. Вы можете работать с ним, чтобы улучшить его — и, соответственно, все, что его использует — не беспокоясь о том, чтобы сломать какой-либо код, который его использует (конечно, при условии, что вы правильно его тестируете… что вам следует).

И с любым современным компилятором, вероятно, будет мало или совсем бесплатно разбить код на отдельную функцию … особенно если вы сделаете это тело функции видимым везде (что произойдет по умолчанию, если вы сделаете его constexpr функция, которую вам действительно стоит попробовать).

Следует ли избегать использования глобальных переменных?

Да.

Сейчас находятся ситуации, когда глобальные переменные (или что-то подобное, например функция, возвращающая ссылку на статическую переменную) допустимы. Но это крайние ситуации: вы должны сделать все, что в ваших силах, чтобы избежать глобальных переменных, и Только используйте их как последнее средство, когда каждый другой вариант хуже.

Сделав переменные глобальными, вы создали четыре основные проблемы:

  1. Вы сделали свой алгоритм менее эффективным. По своей природе глобальные переменные должен быть размещенным где-нибудь в памяти. (Обычно! Технически компиляторы могут обмануть и избежать их фактического выделения. На практике это очень сильно маловероятно.) Это означает, что компилятор больше не имеет возможности исключать их, если он обнаруживает трюк там, где они ему не нужны. Мало того, они, скорее всего, размещены где-то в памяти, нет близко к тому месту, где вы работаете, а это значит, что в кеше не будет жарко.
  2. Вы сделали свой алгоритм функционально невозможным для использования в параллельном коде. Это будущее, и все параллельна в наши дни. В наши дни вы, вероятно, не сможете получить универсальный потребительский ЦП с менее чем 4 ядрами даже на дешевой машине. (Черт, мой маршрутизатор имеет 4 ядра!) И это даже не считая материала GPGPU, который торгуется сотнями или тысячи Конечно.
  3. Вы усложнили тестирование своего алгоритма. Глобальные переменные сложно высмеять, и почти невозможно изолировать, что делает тестирование королевской болью. Вам действительно следует серьезно отнестись к написанию тестируемого кода; фактически, вы должны сделать это своим главным приоритетом. Любой код, который я вижу без тестов, считаю мусорным и никуда его не допущу возле любые серьезные проекты, над которыми я работаю.
  4. Вы сделали свой алгоритм ошибочным. Даже если вы гениальный программист, который пишет только идеальный код и никогда не вносит ошибок … этот код ненадежен. Это потому, что даже если ваш код идеален, кто-то другой может возиться с этими глобальными переменными и полностью испортить результаты вашего алгоритма. И вы, вероятно, никогда не узнаете почему: это одна из тех ужасных ошибок, о которых вы слышали — такого рода, когда код работает отлично, кроме как на компьютере Боба, и только тогда, по вторникам, и только если Leafs будут играть в эту ночь.

Мы не говорим «избегайте глобальных переменных», потому что мы придурки, которые любят придумывать правила и навязывать их программистам, чтобы чувствовать себя лучше. Глобальные переменные действительно, В самом деле Плохо. Они неэффективны, непроверяемы и опасны. Да иногда ситуации, в которых они в порядке (например, std::cout; это глобальная переменная (ну, технически, в пространстве имен, но достаточно близко)). Но вы должны использовать их только тогда, когда действительно, В самом деле должен.

Уместно ли использование массива или мне следует использовать какой-либо тип данных контейнера?

Этот ответ менее ясен, чем другие, но я бы сказал, что вам следует использовать std::vector вместо.

Во-первых, если вы должен использовать массивы, я бы рекомендовал использовать std::array а не массивы C. std::array имеет функции безопасности, которые делают его менее опасным, чем массив C, а также некоторые действительно приятные улучшения удобства использования.

Однако ключевая проблема с вашим использованием заключается в том, что это действительно огромные массивы. Брать primefactor Например. Оно имеет миллион элементов. И каждый элемент — это int, который, вероятно, составит 4 байта. Так primefactor только занимает 4 МБ (примерно 3,8 МБ). Что ж, в Windows, которую я проверял в последний раз (и, по общему признанию, я не проверяю материалы Windows так часто), размер стека по умолчанию, по крайней мере для потоков, составляет всего 1 МБ. primefactor удары прямо мимо этого … и у вас есть четыре массивы такие большие. Существует приличная вероятность того, что даже если эта программа компилируется, она выйдет из строя или будет работать нестабильно на некоторых платформах из-за переполнения стека.

Для таких больших наборов данных следует использовать динамическое размещение. Таким образом, если памяти просто не хватает, вы, по крайней мере, получите предсказуемое поведение, а не сбой. (Теоретически. На практике платформа может чрезмерно фиксировать, а затем начать убивать процессы, и ваша программа все равно может вылететь из строя. Но, по крайней мере, вы пытались.)

Поэтому я бы отказался от массивов для этих огромных наборов данных и предложил бы использовать std::vector вместо. На практике ни одна строка вашего кода не должна измениться (yanno, кроме этих 4 объявлений массивов). тем не мение… Как только вы начнете использовать современные инструменты C ++, такие как std::array или же std::vector, ты мог сделать несколько улучшений. Я вернусь к этому в обзоре кода.

Код ревью

#include <cassert>
const int limit=1000000;

Вы должны оставить пробел между заголовками и фактическим кодом. Пробел ничего не стоит, но делает огромный разница для удобочитаемости.

const int limit=1000000;

const здесь неправильное слово; ты хочешь constexpr. const означает «это значение не изменится»; constexpr означает «это значение является постоянным». Обратите внимание, что это нет тоже самое. const переменные можно установить в любое время, даже во время выполнения. В const просто означает, что после он установлен, он не изменится. Но это по-прежнему переменная, а не константа.

constexpr (применительно к переменным) означает, что значение является истинной константой.

Кроме того, я бы рекомендовал оставить немного места вокруг =. Опять же, пробелы ничего не стоят, но значительно упрощают чтение.

Вы также можете рассмотреть возможность использования разделителей цифр, чтобы сделать большие числа более читаемыми:

constexpr auto limit = 1'000'000;

auto необязательно, но, честно говоря, когда я пишу современный C ++, все является auto В эти дни. Конкретные типы обычно не имеют значения, в отличие от интерфейсов.)

const int limit=1000000;
int sqrtlimit;

int primefactor[limit]={0};

int smallfactor1[limit]={0};
int primepowerfactor[limit]={0};
int smallfactor2[limit]={0};

Поскольку все это глобальные переменные, все они инициализированы нулем. Это означает, что вам технически не нужно писать ={0}… Но это не больно.

Однако тот факт, что все они инициализированы нулями, на самом деле не очень хорошо … потому что это просто потраченные впустую циклы, поскольку первое, что вы делаете, когда программа действительно начинает работать, — это действительно инициализировать их с их реальными значениями.

Эта потраченная впустую инициализация не будет проблемой, если вы не используете глобальные переменные… что я собираюсь предложить.

void init_sieve(void){

Ладно, во-первых, мы не пишем void в пустых списках параметров. Это древний практика из C. Это не относится к C ++ и просто бесполезно загромождает вещи.

Что еще более важно, когда у вас есть функция «init» … это запах кода. В C ++ инициализация и очистка обычно выполняются автоматически… в конструкторах и деструкторах. Они делают инициализацию невозможной забыть, и они заставляют очистку происходить в любых обстоятельствах. И они обычно не берутся за дело; вам даже не нужно о них думать, потому что они просто работают автоматически.

Вот как примерно может выглядеть класс для вашего алгоритма:

class your_algo
{
    enum class factor_split_state
    {
        continue_first_factor,
        in_second_factor,
        continue_third_factor
    };

    static constexpr int _default_limit = 1'000'000;
    // better make sure a million can fit in an int!
    //
    // something like:
    //      static_assert(static_cast<long>(std::numeric_limits<int>::max()) >= 1'000'000L);
    //
    // or you could replace all the "int"s with "value_type",
    // and set "value_type" to "int" or "long", depending
    // on what fits:
    //      using value_type = std::conditional_t<
    //          (static_cast<long>(std::numeric_limits<int>::max()) >= 1'000'000L),
    //          int,
    //          long>;
    //
    //      static constexpr auto _default_limit = value_type(1'000'000L);
    //
    //      value_type limit = _default_limit;
    //      value_type sqrtlimit;
    //      std::vector<value_type> primefactor;
    //      // ... and so on...

public:
    your_algo() :
        your_algo(_default_limit)
    {}

    explicit your_algo(int lim) :
        limit{lim},
        sqrtlimit{int(std::pow(lim, 0.5)) + 1},
        primefactor(lim),
        smallfactor1(lim),
        primepowerfactor(lim),
        smallfactor2(lim)
    {
        // i've just duplicated your algorithm exactly, because you didn't
        // want that critiqued

        init_sieve();
        split_factors();
    }

    // i've just left all the variables public, because i don't understand
    // what you actually *need* to be public
    //
    // this is *not* good practice; you should decide what interface you
    // actually need
    int limit = _default_limit;
    int sqrtlimit;
    std::vector<int> primefactor;
    std::vector<int> smallfactor1;
    std::vector<int> primepowerfactor;
    std::vector<int> smallfactor2;

private:
    constexpr auto init_sieve() -> void
    {
        // for (int n=0;n<limit;n++){
        //     primefactor[n]=n;
        // }
        std::iota(primefactor.begin(), primefactor.end(), 0);

        for (auto n = 2; n <= sqrtlimit; ++n)
        {
            if (primefactor[n] == n)
            {
                for (auto i = n; i < limit; i += n)
                    primefactor[i] = n;
            }
        }
    }

    constexpr auto split_factors() -> void
    {
        // might want to make sure that:
        //      limit == primefactor.size()
        //      limit == smallfactor1.size()
        //      limit == primepowerfactor.size()
        //      limit == smallfactor2.size()

        for (auto n = sqrtlimit; n < limit; ++n)
        {
            auto current_prod = 1;
            auto quotient = n;
            auto last_prime = 0;
            auto prime_exponent = 0;
            auto first_factor = 1;
            auto second_factor = 1;
            auto third_factor = 1;
            auto prime_power = 1;
            auto state = factor_split_state::continue_first_factor;

            while (primefactor[quotient] > 1)
            {
                auto p = primefactor[quotient];
                quotient /= p;

                if (p != last_prime)
                {
                    std::tie(state, first_factor, second_factor, third_factor) =
                        _factor_calculator_thingy(state, prime_power, first_factor, second_factor, third_factor);

                    if (state == factor_split_state::in_second_factor)
                        current_prod = 1;

                    last_prime = p;
                    prime_power = p;
                }
                else {
                    prime_power *= p;
                }

                if (state == factor_split_state::continue_first_factor)
                {
                    current_prod *= p;
                    if (current_prod > sqrtlimit)
                        state = factor_split_state::in_second_factor;
                }

                /* process the last prime power */
                if (quotient == 1)
                {
                    std::tie(state, first_factor, second_factor, third_factor) =
                        _factor_calculator_thingy(state, prime_power, first_factor, second_factor, third_factor);

                    if (state == factor_split_state::in_second_factor)
                        current_prod = 1;
                }
            }

            smallfactor1[n] = first_factor;
            primepowerfactor[n] = second_factor;
            smallfactor2[n] = third_factor;
        }
    }

    // obviously this needs a better name
    static constexpr auto _factor_calculator_thingy(
        factor_split_state state,
        int prime_power,
        int first_factor,
        int second_factor,
        int third_factor
    ) noexcept
        -> std::tuple<factor_split_state, int, int, int>
    {
        switch (state)
        {
        case continue_first_factor:
            return {state, first_factor * prime_power, second_factor, third_factor};
        case in_second_factor:
            return {factor_split_state::continue_third_factor, first_factor, second_factor * prime_power, third_factor};
        case continue_third_factor:
            return {state, first_factor, second_factor, third_factor * prime_power};
        }
    }
};

Теперь твой main() может выглядеть так:

auto main() -> int
{
    auto algo = your_algo{};

    std::cout << "limit " << algo.limit << 'n';
    std::cout << "sqrtlimit " << algo.sqrtlimit << 'n';
    std::cout << 'n';

    std::cout << "print some prime factorizationn";
    for (auto n = algo.limit - 20; n < algo.limit; ++n)
    {
        auto m = n;
        std::cout << m << " = " ;

        auto first = true;
        while (m > 1)
        {
            if (not std::exchange(first, false))
                std::cout << " * ";

            std::cout << algo.primefactor[m];
            m = m / algo.primefactor[m];
        }

        std::cout << 'n';
    }

    std::cout << "nsmallfactor * primepower * smallfactorn"
                 "tthe second factor is a prime powern"
                 "t'!' at the end prime power exponent > 1n";

    for (auto n = algo.limit - 20; n < algo.limit; ++n)
    {
        std::cout << n << " = " << algo.smallfactor1[n] << " * "
            << algo.primepowerfactor[n] << " * " << algo.smallfactor2[n];

        if (algo.primefactor[algo.primepowerfactor[n]] != algo.primepowerfactor[n])
            std::cout << " !";

        std::cout << 'n';
    }
}

Хорошо, вернемся к обзору.

sqrtlimit=int(std::pow(limit,.5))+1;

Я не вижу логики использования std::pow(limit, 0.5) скорее, чем std::sqrt(limit). Полагаю, у вас есть причина?

enum State {
    continue_first_factor,
    in_second_factor,
    continue_third_factor
    };

Вы должны использовать современные enum class а не старая школа enum. Это немного более многословно, но много безопаснее.

        int current_prod=1;
        int quotient=n;
        int last_prime=0;
        int prime_exponent=0;
        int first_factor=1;
        int second_factor=1;
        int third_factor=1;
        int prime_power=1;

Когда я вижу длинный список подобных переменных, это действительно подсказывает мне, что вы могли бы использовать еще несколько типов. Например, тип факторов:

struct factors_t
{
    int first = 1;
    int second = 1;
    int third = 1;
};

Затем приведенный выше список немного сокращается до:

        auto current_prod = 1;
        auto quotient = n;
        auto last_prime = 0;
        auto prime_exponent = 0;
        auto factors = factors_t{};
        auto prime_power = 1;

Это улучшение. Это также упрощает вспомогательную функцию:

    static constexpr auto _factor_calculator_thingy(factor_split_state state, int prime_power, factors_t factors) noexcept
        -> std::tuple<factor_split_state, factors_t>
    {
        switch (state)
        {
        case continue_first_factor:
            return {state, {factors.first * prime_power, factors.second, factors.third}};
        case in_second_factor:
            return {factor_split_state::continue_third_factor, {factors.first * prime_power, factors.second, factors.third}};
        case continue_third_factor:
            return {state, {factors.first, factors.second, factors.third * prime_power}};
        }
    }

Возможно, вы также можете объединить некоторые другие переменные в типы.

        int p;

Теперь это определенно не принадлежит.

Это немедленно вызвало бы у меня «нет» при проверке кода, потому что это неинициализированная переменная. Плохо.

Но «исправлять» это путем добавления инициализатора — неправильный ход, потому что правда в том, что этой переменной здесь не должно быть вообще. Он должен существовать только внутри цикла, поэтому его следует создавать, когда это необходимо:

        int third_factor=1;
        int prime_power=1;

        State state=continue_first_factor;
        while(primefactor[quotient]>1){
            auto p = primefactor[quotient]; // <-- this is where it needs to be created
            quotient/=p;

Все остальные переменные должны сохранять свои значения во время итераций цикла, поэтому их нужно создавать вне цикла.

switch (state) {
    case continue_first_factor:
        first_factor*=prime_power;
        break;
    case in_second_factor:
        second_factor=prime_power;
        current_prod=1;
        state=continue_third_factor;
        break;
    case continue_third_factor:
        third_factor*=prime_power;
        break;
    default:
        assert(0);
        break;
}

Добавление варианта по умолчанию, возможно, является излишней защитой. Все современные компиляторы, с которыми я знаком, автоматически обнаруживают, что переключатель является исчерпывающим, потому что вы учли каждое значение параметра State enum.

Итак, все, что осталось, это main(). Самое большое преступление, которое происходит в main() использует std::endl. Не делай этого. Да, я знаю, что его используют повсюду учебные пособия; ну, я говорю, что они все отстой. С использованием std::endl почти всегда ошибается. (Или в по крайней мере, бессмысленно расточительно.) Фактически, каждое использование std::endl в вашем коде не так.

Если вам нужна новая строка, просто используйте n.

    bool first=true;
    while (m>1){
        if (first){
            first=false;
        }
        else {
            std::cout << " * ";
        }
        // ... [snip] ...
    }

Есть небольшой трюк с шаблоном, который вы использовали выше. Ключ std::exchange(). std::exchange() принимает переменную в качестве первого аргумента и значение в качестве второго: std::exchange(variable, value). Что он делает:

  1. установить значение variable к value; а также
  2. вернуть Старый значение variable.

И делает все это максимально эффективно.

Итак, что вы делаете:

    auto first = true;
    while (m > 1)
    {
        if (not std::exchange(first, false))
            std::cout << " * ";

        // ... [snip] ...
    }

В первый раз exchange() звонки first к false, и возвращает true (что является старым значением first). С not true является false, то cout заявление никогда не срабатывает.

Во второй раз и каждый раз после этого exchange() наборы first к false (бессмысленно, потому что first уже false, но это не имеет значения на любом современном оборудовании) и возвращает false (что было старым значением first). not false является true, Итак cout распечатки выписки.

Это всего лишь один из тех паттернов, которые вам нужно знать; например, чтобы красиво распечатать набор значений в векторе:

auto const vec = std::vector{1, 2, 3, 4};

std::ranges::for_each(vec, [first=true](auto&& v) mutable
    {
        if (not std::exchange(first, false))
            std::cout << ", ";
        std::cout << v;
    });

// prints:
//      1, 2, 3, 4

Теперь давайте очистим большой блок печати:

    std::cout<<std::endl<<"smallfactor * primepower * smallfactor"<<std::endl;
    std::cout<<"tthe second factor is a prime power"<<std::endl;
    std::cout<<"t'!' at the end prime power exponent > 1"<<std::endl;

Сначала удаляем все std::endls (и разнесите токены для удобства чтения):

    std::cout << 'n' << "smallfactor * primepower * smallfactor" << 'n';
    std::cout << "tthe second factor is a prime power" << 'n';
    std::cout << "t'!' at the end prime power exponent > 1" << 'n';

Далее мы объединяем все 'n's в строки:

    std::cout << "nsmallfactor * primepower * smallfactorn";
    std::cout << "tthe second factor is a prime powern";
    std::cout << "t'!' at the end prime power exponent > 1n";

Далее вместо 3 cout заявления, объединяем их в одно:

    std::cout << "nsmallfactor * primepower * smallfactorn"
        << "tthe second factor is a prime powern"
        << "t'!' at the end prime power exponent > 1n";

Затем мы воспользуемся тем фактом, что C ++ автоматически объединит смежные строковые литералы и превратит эти 3 operator<<() звонит в 1:

    std::cout << "nsmallfactor * primepower * smallfactorn"
        "tthe second factor is a prime powern"
        "t'!' at the end prime power exponent > 1n";

Мы могли бы оставить все как есть, что уже будет значительно быстрее, чем исходный код, но есть еще один вариант, который вы могли бы рассмотреть: необработанные строки:

    std::cout << R"(
smallfactor * primepower * smallfactor
    the second factor is a prime power
    '!' at the end prime power exponent > 1
)";

Это все, что я могу порекомендовать без полного пересмотра алгоритма и структуры кода. Отправной точкой для этого было бы использование класса, как я продемонстрировал выше. После этого, возможно, следующим шагом будет рассмотрение разделения частей процесса на независимые функции или типы — в частности, вероятно, нет необходимости работать с 4 массивами. Затем, возможно, в долгосрочной перспективе, посмотрите на возможности распараллеливания.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *