Member 14925633 Ответов: 6

Снизить трудоемкость


хорошо: число n называется хорошим, если сумма его собственных делителей больше n. Пример сумма собственных делителей 12 равна 1 + 2 + 3 + 4 + 6 = 16, а это значит, что 12-хорошее число.Учитывая n, найдите сумму всех хороших чисел, меньших или равных n

Что я уже пробовал:

int getResult(int n)
    {
    int a = 0;
    for (int i = 1; i <= sqrt(n); i++)
        {
        if (n % i == 0)
            {
            if (n / i == i)
                {
                a = a + i;
                }
            else
                {
                a = a + i;
                a = a + (n / i);
                }
            }
        }
    a = a - n;
    return a;

    }
int main()
    {
    int n;
    cin > > n;
    int final_sum = 0;
    for (int i = 1;
    i <= n; i++)
        {
        int sum = 0;
        sum = getResult(i);
        if (sum > i)
            final_sum += i;

        }
    cout < < final_sum;
    return (0);
    }

6 Ответов

Рейтинг:
38

Patrice T

Цитата:
Снизить трудоемкость

Сначала вам нужно понять, где ваш код тратит время и почему.
Психолог поможет вам: Профилирование (компьютерное программирование) - Википедия[^]
Вам нужно понять причину, по которой существует какой-то код, и подумать о других способах добраться до решения.
Пример: "a = a - n;" существует только как следствие 1 как делитель, но 1 всегда является делителем:
int getResult(int n)
    {
    int a = 0;
    int a = 1;
    for (int i = 1; i <= sqrt(n); i++)
    for (int i = 2; i <= sqrt(n); i++)
        {
        if (n % i == 0)
            {
            if (n / i == i)
                {
                a = a + i;
                }
            else
                {
                a = a + i;
                a = a + (n / i);
                }
            }
        }
    a = a - n;
    return a;
    }


Member 14925633

Братан ошибка снова идет

Patrice T

Какая ошибка ?

Member 14925633

Ошибка временной привязки

Patrice T

Это означает, что ваш код все еще слишком сложен, вам нужно больше оптимизации.

Рейтинг:
2

Stefan_Lang

Прежде всего, есть ряд проблем с вашим вопросом. Вы могли бы избежать большинства из них, просто прочитав и следуя советам, предложенным при вводе вашего вопроса здесь! Не делая этого, вы усложняете задачу для тех, кто готов помочь, и, как следствие, вероятность того, что кто-то ответит полезным ответом, значительно меньше.

Кроме того, сделать это проще для людей, которые пытаются Вам помочь, - это вопрос уважения. Если вы просто сбросите свой код здесь, это будет неуважительно по отношению к тем, кто должен вам помочь! Пожалуйста, приложите больше усилий в следующий раз, когда будете задавать вопрос.

Во-вторых, когда вы публикуете вопрос, добавьте его в свой код! Мы не можем читать ваши мысли, не видим ни вашего экрана, ни оригинального описания задачи, над которой вы работаете! Опишите результат, который вы получаете, и результат, который вы ожидаете, а также любые сообщения об ошибках, которые вы видите. Любая часть этой информации, которую вы опустите, просто оставит нас в неведении и, скорее всего, приведет к предложениям, которые вам не помогут.

Third, when you write code, do yourself and everyone else a favor and use names that are self-explanatory! "getResult" or "a" aren't useful at all! Use names that offer an insight about what they are used for, e. g. "sumOfDivisors". In case of "a", "sum_of_divisors" would also be a fitting name, but since that is exactly what the function does, you could simply name it "result", implying that this is the variable to store the sum of divisors. You did use "sum" and "final_sum" in your main function, but in this case the prefix "final" doesn't help in clarifying the difference between the two, as you are summing totally different numbers! "sum" should be "sum_of_divisors" and "final_sum" should be "sum_of_good_numbers" - that would instantly clarify what you are storing and calculating here!

Наконец, для производительности попробуйте следующие вещи:

1. Научиться использовать профилировщик. Вы, конечно, можете попытаться сделать обоснованное предположение, какие части вашего кода занимают больше всего времени, но даже очень опытные программисты, как правило, испытывают сюрпризы, когда фактический профилировщик говорит им, где искать. Основная причина этого заключается в том, что современные компиляторы обычно гораздо лучше распознают и оптимизируют неэффективные сегменты кода, чем сами программисты, а узкие места производительности, которые остаются после оптимизации компилятора, гораздо труднее обнаружить.

2. Первое, на что нужно обратить внимание, когда у вас возникают проблемы с производительностью, - это избыточный код, а это означает, в первую очередь, код внутри циклов, поскольку это код, который выполняется повторно. Проверьте, содержит ли код цикла какие-либо операторы, которые могут быть перемещены за пределы цикла. Вычисление их один раз, перед началом цикла избавляет вас от ненужных повторений одних и тех же вычислений. Обратите внимание, что любые вычисления, выполняемые в операторе for (или условия в циклах do и while), являются частью этого кода цикла!

Пример:
- ваше сравнение i<=sqrt(n) выполняется для каждой итерации. Вы можете переместить вычисление, вызванное в этом сравнении, из цикла.
- вы можете вычислить n/i только один раз и сохранить его во вспомогательной переменной или настроить свой код, чтобы вам не нужно было пересчитывать его несколько раз.

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

Например, когда вы делаете сравнение, включающее такие вещи, как квадратные корни или деления, попробуйте переформулировать их таким образом, чтобы они использовали вместо этого умножения. например, вместо i<=sqrt(n) вы могли бы написать i*i<=n. Конечно, в этом случае, если вы переместите вычисление sqrt за пределы цикла, это будет всего один sqrt, но если вы реформируете сравнение с помощью i*i, то у вас будет много умножений, которые в этом случае не могут быть улучшением! Так что будьте осторожны, где использовать эту идею.

4. вызовы функций занимают значительно больше времени, чем простые операции, такие как * или /. Когда у вас есть вызов функции внутри цикла, проверьте, помогает ли объявление его встроенным.

Пример: ваша основная функция вызывает getResult() в цикле. Попробуйте объявить getResult() как встроенный.

5. когда у вас есть цикл, подумайте о способах повторного использования результатов предыдущих итераций, чтобы помочь вычислить результаты для последующих итераций.

Пример: для каждого четного числа n=2*k делители k также являются делителями n. Предупреждение: это не тривиально, чтобы найти все дополнительные делители, и это требует хранения делителей предыдущих чисел - это может потребовать большого объема памяти, и просто поддерживать эту память может стоить больше производительности, чем вы могли бы получить. Однако, если вы подумаете в соответствии с первоначальной идеей, вы можете придумать совершенно другой алгоритм:

Вместо того чтобы находить все делители всех чисел до n, постройте все "хорошие" числа n, комбинируя делители! Для этой идеи полезно было бы услышать о "Геделевская нумерация[^]" В основном это кодировка, которая представляет данное число n его простыми множителями. например, число 12 имеет код Геделя {"2","1"}, потому что 12=2^2*3^1, и 15 имеет код Геделя {"0","1","1"} потому что 15=2^0*3^1*5^1.

Хорошая вещь о представлениях Геделя заключается в том, что вычисление суммы делителей намного проще. Плохо то, что, хотя вы можете перечислять (или упорядочивать) числа, используя их коды Геделя, этот порядок не соответствует естественному порядку фактических чисел. Поэтому это немного сложно, чтобы перечислить "все числа до n"

Я не буду описывать (довольно сложные) шаги по реализации этого решения, так как не думаю, что автор этой задачи действительно стремился к такому решению. Вместо этого я собираюсь предложить.

6. Используйте распараллеливание для повышения производительности. Как и 5, это может быть не то, к чему стремился автор задачи. Но если он это сделал, просто используйте несколько потоков для вызова getResult() для разных номеров в вашем основном цикле.


Maciej Los

5ed!

Stefan_Lang

Спасибо.

Рейтинг:
2

CPallini

Обычно фокус заключается в запоминании, понимаете Мемуаризация - Википедия[^].


Patrice T

Привет,
В чем же интерес мемуаризации в данном случае ?
это 1-метровый поиск делителей чисел.

CPallini

Запись делителей.

Stefan_Lang

Хотя я и предложил хранить делители (и повторно использовать их для кратных этому числу), далеко не очевидно, как найти дополнительные делители, не включив случайно дубликаты. На самом деле, я не уверен, что это сильно поможет, но это потребует некоторого тестирования.

ИМХО, что тест намеренно использует компилятор с выключенной оптимизацией, чтобы измерить способность программиста находить незначительные оптимизации, такие как избегание повторяющихся вычислений.

Рейтинг:
1

OriginalGriff

Во-первых, сделайте отступ в своем коде!
Я отредактировал ваш вопрос, чтобы добавить отступ, но вы всегда должны убедиться, что ваш код правильно отступлен, используя ваш предпочтительный стиль: Whitespiths, K&R, даже 1TB, если это необходимо!Это делает ваш код намного проще для чтения и понимания.

Во-вторых, когда вы задаете вопрос, скажите нам, что ваш код делает то, чего вы не ожидали, или не делает то, что вы сделали. Скажите использовать любые сообщения об ошибках и где они происходят. Расскажите нам, что вы сделали, чтобы заставить его сделать это. Расскажите нам, что вы пытались исправить. Просто сбрасывая свой код на нас и эффективно говоря "это не работает", вы никому не поможете!

И наконец: заставить ваш код работать-это часть задачи: любой шимпанзе может разбить код и заставить его компилироваться - но это только начало! Компиляция не означает, что ваш код верен! :смеяться:
Подумайте о процессе разработки как о написании электронного письма: успешная компиляция означает, что вы написали электронное письмо на правильном языке - например, на английском, а не на немецком, - а не то, что письмо содержало сообщение, которое вы хотели отправить.

Итак, теперь вы входите во вторую стадию разработки (на самом деле это четвертая или пятая, но вы перейдете к более ранним стадиям позже): тестирование и отладка.

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

Input   Expected output    Actual output
  1            2                 1
  2            4                 4
  3            6                 9
  4            8                16
Тогда совершенно очевидно, что проблема заключается в бите, который удваивает его - он не прибавляет себя к себе или умножает его на 2, он умножает его на себя и возвращает квадрат входного сигнала.
Таким образом, вы можете посмотреть на код, и очевидно, что он находится где-то здесь:
int Double(int value)
   {
   return value * value;
   }

Как только у вас появится идея, что может пойти не так, начните использовать отладчик, чтобы выяснить, почему. Поместите точку останова в первую строку метода и запустите приложение. Когда он достигнет точки останова, отладчик остановится и передаст управление вам. Теперь вы можете запускать свой код построчно (так называемый "одноступенчатый") и просматривать (или даже изменять) содержимое переменных по мере необходимости (черт возьми, вы даже можете изменить код и повторить попытку, если вам это нужно).
Подумайте о том, что должна делать каждая строка кода перед ее выполнением, и сравните это с тем, что она действительно делала, когда вы использовали кнопку "Step over" для выполнения каждой строки по очереди. Он сделал то, что вы ожидали? Если да, то переходите к следующей строке.
Если нет, то почему? Чем это отличается?
Надеюсь, это поможет вам определить, в какой части этого кода есть проблема и в чем она заключается.
Это навык, и его стоит развивать, поскольку он помогает вам как в реальном мире, так и в развитии. И, как и все навыки, он только улучшается при использовании!


Рейтинг:
0

Greg Utas

Я вижу, что вы в основном перепостили этот вопрос из здесь[^].

Если это все еще тайм-аут, имейте в виду, что sqrt может быть, это довольно дорогостоящая процедура вызова. Можете ли вы придумать более эффективный способ завершить цикл?


Рейтинг:
0

Rick York

Я не совсем понимаю, что делал ваш код. Эту последовательность можно упростить.

if (n % i == 0)
    {
    if (n / i == i)
        {
        a = a + i;
        }
    else
        {
        a = a + i;
        a = a + (n / i);
        }
    }
Для начала "а = а + 1" повторяется дважды. Что еще более важно, почему a увеличивается на (n/1), когда n/i не равно i? Почему вообще существует этот код? Когда i является делителем n, вы просто хотите увеличить сумму, а затем перейти к следующему значению. Этот код должен выглядеть так :
if( n % i == 0 )
{
    a += i;
}
Почему он должен делать что-то еще?

Другое дело, что цикл for не является правильным. Поскольку 6-это делитель 12, ваш цикл пропустит его проверку. Я думаю, что условное выражение цикла должно быть i <= n / 2;. Эти две вещи должны дать вашему коду лучшие результаты.


Patrice T

Боюсь, вам придется внимательно перечитать кодекс.
В "А = А + я" " я " - это буква, а не цифра. И то же самое с кодом в вашем решении.
Эта "(н / я == я)" является верным, когда " я " - это квадратный корень из 'Н'.
"Поскольку 6 является делителем 12", когда Фактор 2 найден, "a = a + (n / i);" заботится о факторе 6