Проблема толи лыжи не едут.. то ли..

Nag_X
5/29/2006, 10:25:04 PM
Господа программисты, такая проблема... пишу контрольную по С++
Задача звучит так: Найти сумму всех чётных делителей натурального числа. Ну вроде как разобрался составил функцию:
Написал консольку на C++ Bilder 5...
И тут началось кино и немцы в хате...
Ну ладно думаю масдайность глюкавая мне мозг парит... нет на другом компе таже лажа, я раньше дела с С++ не имел, прошу помочь...
Вот две функции как бы для одного и того же конечного результата, где я не прав или укажите как надо:
int sumdel(int n)
№1
{ int s,i=2 ;
while(i>n)
{if(n%i==0) s+=i;
++i;
}
return s;
}
№2
int sumdel(int n)
{ int s=n,i ;
for( i=2; i>n;) i+=2;
if(n%i==0) s+=i;
return s;
}
DELETED
5/29/2006, 10:57:56 PM
Вот тебе корректные варианты:

CODE
int sumdel(int n)
{
 int s = 0, i = 2;
 while(n >= i)
 {
   if(!(n % i))
     s += i;
   i += 2;
 }
 return s;
}

int sumdel1(int n)
{
 int s = n, i;
 for(i = 2; n > i; i += 2)
 {
   if(!(n % i))
     s += i;
 }
 return s;
}


Багов было просто немерено. Начиная от неверного сравнения и т.д.

А вот этот вариант мне больше всего нравится:
CODE
int sumdel2(int n)
{
 int sum = n;
 for(int i = 2; n > i; i += 2)
   sum += n % i ? 0 : i;
 return sum;
}
tetro
5/29/2006, 11:07:55 PM
За что люблю коллегу, так за лаконичнисть формулировок: немногое, но много...

А что будет если n нечетно?
DELETED
5/30/2006, 12:01:48 AM
(tetro @ 29.05.2006 - время: 19:07)А что будет если n нечетно?
Точно :) в последних двух вариантах есть глюк, который я не исправил.
Правильно будет так:

CODE
int sumdel1(int n)
{
int s = 0, i;
for(i = 2; n >= i; i += 2)
{
  if(!(n % i))
    s += i;
}
return s;
}

int sumdel2(int n)
{
int sum = 0;
for(int i = 2; n >= i; i += 2)
  sum += n % i ? 0 : i;
return sum;
}


Специально нечетный варианты никак не обрабатываются, мой принцип: никогда не оптимизируй заранее.
Nag_X
5/30/2006, 4:36:23 AM
Спасибо большое. Как я понял, двигался в правильном направлении..))) Ток заплутал в трёх соснах... Ещё раз спасибо....
tetro
5/30/2006, 3:33:27 PM
Глюк увидел, поэтому и спросил... просто не было времени чинить, позвали на заседание ...
GrAnd
5/30/2006, 9:35:11 PM
Ну перво-наперво бросается в глаза, что i пробегает значения от 2 до n, т.е. задача имеет временную сложность порядка O(n). Сразу возникает желание (если n четно), заставить i пробегать значения до n/2, а n прибавить потом. Но это на порядок сложности не повлияет никак, разве что уменьшит время работы в 2 раза.

Вопрос такой: А нельзя ли создать алгоритм с временной сложностью порядка O(ln(n)) или O(sqrt(n))?
Кажется, что-то такое возможно.
Ведь самый большой делитель должен быть n. учтя это мы можем откинуть сразу половину из возможных кандидатов и проверить, является ли число n/2 целым. Оно является, если n четно. Прибавив n/2 к n можно отбросить все i свыше n/3 и проверить, является ли n/3 целым (делится ли n на 3) и т.д. Таким образом отбрасываем сразу значительную часть от кандидатов на делители. Правда, с каждым последующим шагом, отбрасываемая часть уменьшается и станет меньше 1. Но и это не очень страшно, т.к. можно затормозиться, когда количество кандидатов станет равно корню из n. остальные можно получить делением n на уже найденные. Только, если число n является квадратом и найден делитель i, являющийся корнем квадратным, то прибавлять n/i не надо - оно рано i.

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

Очень мешает то, что нужно находить только четные делители. Поэтому, если найден некий четный делитель i, то нужно не только найти делитель n/i, но и проверить на четность оба делителя (i и n/i) и прибавить только четные.
Избавиться от этого поможет тот факт, что сумма четных делителей числа равна удвоенной сумме ВСЕХ делителей половины этого числа. Тогда можно будет идти не сверху, а снизу, проверяя делители, начиная с 1, до корня из половины n).
Таким образом, алгоритм примерно такой:

1. Если n<=0 или нечетно, то возврат 0.
2. Получаем целые числа n2=n/2; nr=целая часть(корень_квадратный(n-1))+1; s=0.
3. Для i от 1 до nr-1 делать проверку: если n2 делится на i, то s=s+i+n/i.
4. Если n=nr*nr (n - квадрат) , то s=s+nr.
5. Вернуть s+s.

Прошу прощения за псевдокод. Последний раз на C я писал лет так 10 назад или чуть поменьше. Поэтому специально избегал синтаксиса C, ибо мог и ошибиться.

Маленькая тонкость. Т.к. при извлечении корня квадратного возможны ошибки, в результате которого целая часть окажется меньше на 1, то мною применен следующий алгоритм вычисления nr, основанный на том, что сумма первых нечетный чисел равна квадрату:

1. n1=0; nr=0;
2. Пока n1 < n2 делай
2.1. n1=n1+nr+nr+1;
2.2. nr=nr+1;

Как ни странно, это работает ...

Временная сложность данного алгоритма O(sqrt(n)). Алгоритм с временной сложностью O(ln(n)) придумать не удалось. Может быть кто-то сумеет.
DELETED
5/30/2006, 10:19:24 PM
А не напоминает ли вам это все факторизацию, у которой текущая стоимость sqrt(a)/2? Если тут кто напишет на ln(a) - чур я его импрессарио.. :)
tetro
5/30/2006, 11:31:51 PM
Естественно, можно конечно через решето Эрастофена... т.е. разложить на делители и посмотреть потом собрав:
Т.е. двигаемся в двух направлениях: строим послед. простых чисел и параллельно пытаемся на них поделить, как с двух сторон сойдемся так нам и будет счастье wink.gif
Не помню какова сложность решета (тут точно, что не более sqrt(a)/2), но насчет лога, не уверен...
Nag_X
5/31/2006, 2:45:35 AM
И тут я понял...как мало я знаю... wacko.gif
DELETED
5/31/2006, 3:02:18 AM
(GrAnd @ 30.05.2006 - время: 17:35) Временная сложность данного алгоритма O(sqrt(n)). Алгоритм с временной сложностью O(ln(n)) придумать не удалось. Может быть кто-то сумеет.

+1

Но! Основная заповедь разработчика: "Keep It Simple, Stupid".
DELETED
5/31/2006, 3:59:04 AM
(простите за assa wink.gif)

(tetro @ 30.05.2006 - время: 19:31)Естественно, можно конечно через решето Эрастофена...
А есть трехлетней давности полиномиальный алгоритм факторизации со стоимостью (уж простите, я по старинке) O(ln^(6+e)n). Ну куда нам тут с титанами бороться.

(GregZ @ 30.05.2006 - время: 23:02)Но! Основная заповедь разработчика: "Keep It Simple, Stupid".
Вы просто неправильно понимаете эту фразу. :)
GrAnd
5/31/2006, 1:42:32 PM
(Nag_X @ 30.05.2006 - время: 23:45) И тут я понял...как мало я знаю... wacko.gif
Да ладно, какие проблемы ...
Лет так 20-25 назад, когда я еще понятия не имел об анализе производительности алгоритмов, я искренне считал пузырьковую сортировку эффективным алгоритмом.

Если тебя заинтересуют эффективные алгоритмы и методы их исследований, то попытайся найти книгу
"Н.Вирт (Да-да, тот самый дедушка Вирт). Алгоритмы и структуры данных. М.: Мир, 1989 г."
Или хотя бы
"Н.Вирт. Алгоритмы + структуры данных = программы. М.: Мир, 1985 г."
Там все на пальцАх расписывается - различные маленькие хитрости, способные значительно улучшить производительность программ. А так же азы расчета производительности. Только примеры приведены на языках Modula-2 и Pascal (что почти одно и то же).
Для более глубокого изучения
"Д.Кнут. Искусство программирования. 1-3 т.т. М.: Вильямс, 2000 г."
Но это уже для профи. БГ говорил, что того, кто осилит этот трехтомник, он сразу берет к себе на работу.

(GregZ)Но! Основная заповедь разработчика: "Keep It Simple, Stupid".
Впервые про KISS-принцип я прочитал в книге
"Ван Тассел. Стиль, разработка, эффективность, отладка и испытание программ. Д.: Мир, 1985 г."
Но там же был приведен и другой принцип:
"Оптимизацию любой программы можно проводить бесконечно" :) ...

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

(JeyLo)А есть трехлетней давности полиномиальный алгоритм факторизации со стоимостью (уж простите, я по старинке) O(ln^(6+e)n). Ну куда нам тут с титанами бороться.
Извини, не понял в обозначениях. Это что за крышечка? Показатель степени? Т.е. (ln(n))^(6+e). Или наоборот - основание логарифма?
tetro
5/31/2006, 2:09:52 PM
Да показатель степени ...

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

Это к стати примерно идея (на детальную имплементацию с места в карьер не уверен что рискну), которая крутилась у меня в голове по дороге домой.


Сложность решета где-то О(sqrt(x)*ln(x)), а возраст примерно как сумма возрастов народу в этой ветке с весьма толстым фактором... wink.gif

По поводу книг, то на сегодня лучшим считается в этой области (как одна книга):
Introduction to Algorithms, Second Edition by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein (с первого издания есть русский перевод).
Не все в ней совремменно, выбор тем для примеров иногда странен, например черно-красные деревья вместо АВЛей и нет рандомальных скип-листов (из того что я помню), но еще раз если одна книга, то скорее она.