Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Найти количество разложений числа, на простые слагаемые 
:(
    Опции темы
GoldMan
Дата 10.12.2002, 03:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 27.11.2002

Репутация: нет
Всего: нет



Найти количество разложений числа на простые слагаемые, например, для 11 ответ 6, т.к. 11=2+2+2+2+3;11=2+3+3+3;11=2+2+2+5;11=3+3+5;11=7+2+2;11=11.

Заранее спасибо.
PM MAIL   Вверх
Step
Дата 10.12.2002, 04:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5151
Регистрация: 26.9.2002
Где: дурдом.UA

Репутация: нет
Всего: 25



Цитата(GoldMan @ 09.12.2002, 19:39)
Найти количество разложений числа на простые слагаемые, например, для 11 ответ 6, т.к. 11=2+2+2+2+3;11=2+3+3+3;11=2+2+2+5;11=3+3+5;11=7+2+2;11=11.

Заранее спасибо.

Впоследнее время я слишком часто слушы такии вопросы, товарищи это вы на какой алимпиаде побывали.

Кстати тупым перебором не пробывали.... ну и что, что долго...


--------------------
- Дурак учится на своих ошибках, умный на чужих.
 - умные учатся у дураков
PM MAIL ICQ   Вверх
podval
Дата 10.12.2002, 04:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Где я? Кто я?
****


Профиль
Группа: Экс. модератор
Сообщений: 3094
Регистрация: 25.3.2002
Где: СПб

Репутация: 18
Всего: 62



На некоторых олимпиадах именно такие задачи. Решение состоит в том, чтобы написать алгоритм решения такой задачи. А если это число 11-значное? Какой тут тогда перебор?
Итак, задача - написать алгоритм.
PM WWW ICQ   Вверх
GoldMan
Дата 10.12.2002, 16:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 27.11.2002

Репутация: нет
Всего: нет



Ограничения:
число в пределах от 1 до 5000;
15 секунд на тест
Перебор успевает за 15 секунд число, не большее 120 :(((
PM MAIL   Вверх
MuToGeN
Дата 10.12.2002, 16:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лесник
****


Профиль
Группа: Модератор
Сообщений: 4379
Регистрация: 15.8.2002
Где: Москва

Репутация: нет
Всего: 32



Цитата(GoldMan @ 10.12.2002, 08:29)
Ограничения:
число в пределах от 1 до 5000;
15 секунд на тест
Перебор успевает за 15 секунд число, не большее 120 :(((

что значит 15 секунд? 15 секунд на 486DX2 или на AthlonXP 2.5GHz?


--------------------
Three pings for the token rings,
Five pings for the UNIX machines,
Hundred pings for the broken links,
One special ping to check them all
Through Simple Network Management Protocol!
PM MAIL ICQ   Вверх
MuToGeN
Дата 10.12.2002, 16:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лесник
****


Профиль
Группа: Модератор
Сообщений: 4379
Регистрация: 15.8.2002
Где: Москва

Репутация: нет
Всего: 32



хотя есть у меня смутная догадка как это сделать... скоро че-нить нарисую...


--------------------
Three pings for the token rings,
Five pings for the UNIX machines,
Hundred pings for the broken links,
One special ping to check them all
Through Simple Network Management Protocol!
PM MAIL ICQ   Вверх
podval
Дата 11.12.2002, 05:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Где я? Кто я?
****


Профиль
Группа: Экс. модератор
Сообщений: 3094
Регистрация: 25.3.2002
Где: СПб

Репутация: 18
Всего: 62



А почему среди слагаемых нет числа 1? Тогда 11 не шестью способами можно получить.
PM WWW ICQ   Вверх
Cashey
Дата 11.12.2002, 08:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бессмертный
****


Профиль
Группа: Завсегдатай
Сообщений: 3441
Регистрация: 13.11.2002
Где: в столице

Репутация: нет
Всего: 60



Цитата

А почему среди слагаемых нет числа 1?

Число 1 не является простым.


--------------------
библия учит любить ближнего, а камасутра обучает как именно
PM Jabber   Вверх
podval
Дата 11.12.2002, 18:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Где я? Кто я?
****


Профиль
Группа: Экс. модератор
Сообщений: 3094
Регистрация: 25.3.2002
Где: СПб

Репутация: 18
Всего: 62



В таком случае наша задача отличается от http://algolist.manual.ru/olimp/per_sol.php#a8 тем, что надо предварительно найти и записать массив простых чисел, меньших заданного, и работать только с этим массивом.
PM WWW ICQ   Вверх
GoldMan
Дата 11.12.2002, 19:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 27.11.2002

Репутация: нет
Всего: нет



2 MutoGen: комп 486DX, поэтому перебор для n=5000 не успевает.
PM MAIL   Вверх
MuToGeN
Дата 11.12.2002, 19:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лесник
****


Профиль
Группа: Модератор
Сообщений: 4379
Регистрация: 15.8.2002
Где: Москва

Репутация: нет
Всего: 32



Цитата
найти и записать массив простых чисел, меньших заданного, и работать только с этим массивом
такой вариант, правда, он не самый лучший:
Код
int c[5000];
int i,j,k=1;
c[0]=2;
bool p;
for(i=3;i<=5000;i++)
{
  p=true;
  for(j=0;j<k;j++)
    if(i%c[j]==0)
      p=false;
  if(p)
    c[k++]=i;
}
в массиве c имеем простые числа от 0 до 5000, кол-во простых чисел равно k. все что после k - неинициализированные элементы массива






--------------------
Three pings for the token rings,
Five pings for the UNIX machines,
Hundred pings for the broken links,
One special ping to check them all
Through Simple Network Management Protocol!
PM MAIL ICQ   Вверх
Chingachguk
Дата 15.12.2002, 13:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1232
Регистрация: 25.3.2002
Где: Москва

Репутация: 1
Всего: 18



Вот, вроде сделал. Адская рекурсия и прочий изврат ;)

Для полного решения надо выделить еще кучу памяти, что меня делать заломало, поэтому я посчитал (на P-120) тока до 300-т ;)

(Именно от количества памяти зависит комфортное быстродействие)


Код


const
  MaxValidNumber = 500;
var
  P:array[0..MaxValidNumber] of longint;
  P3:array[0..MaxValidNumber] of longint;
  P5:array[0..MaxValidNumber] of longint;
  P7:array[0..MaxValidNumber] of longint;
  P11:array[0..MaxValidNumber] of longint;
  P13:array[0..MaxValidNumber] of longint;
  P17:array[0..MaxValidNumber] of longint;
  P19:array[0..MaxValidNumber] of longint;
  P23:array[0..MaxValidNumber] of longint;
  Simples: array [1..(MaxValidNumber shr 2)] of word;
function Is_SimpleNumber(Num:word):boolean;
  var
    i: word;
  begin
    Is_SimpleNumber:=false;
    if ((Num=0) or (Num=1)) then exit;
    if Num>3 then
      begin
        if (Num and 1)=0 then exit;
        if (Num and 3)=0 then exit;
      end;
    Is_SimpleNumber:=true;
    for i:=3 to (Num-1) do
      if (Num mod i)=0 then
        begin
          Is_SimpleNumber:=false;
          exit;
        end;
  end;
{Число возможнных сумм простых чисел, равных Num}
function Sum_Number(Num:integer):longint;
  var
    result: longint;
    i,j,k: word;
    Simples: array [1..(MaxValidNumber shr 2)] of word;
    Coffs: array [1..(MaxValidNumber shr 2)] of word;
    MaxCoffs: array [1..(MaxValidNumber shr 2)] of word;
    SimplesNum: word;
    TestSum: integer;
    DeepOld: word;
    Deep: word;
    Flag: boolean;
    delta: word;
  label NextCoffs,CoffsDone;
  begin
    Sum_Number:=0;
    {Выполняем поиск всех простых чисел в интервале [0,Num]}
    {Таких чисел не более чем Num/4}
    SimplesNum:=0;
    for i:=0 to Num do
      if Is_SimpleNumber(i)=true then
        begin
          inc(SimplesNum);
          Simples[SimplesNum]:=i;
          MaxCoffs[SimplesNum]:=Num div Simples[SimplesNum];
          Coffs[SimplesNum]:=0;
        end;
    if SimplesNum=0 then exit;
    result:=0;
    {Получили число простых чисел в интервале в SimplesNum}
    {Выполняем переборы коэффициентов}
    Deep:=1;
    repeat
      TestSum:=0;
      for i:=1 to Deep do
        begin
          inc(TestSum,Coffs[i]*Simples[i]);
          if TestSum>Num then break;
        end;
      if TestSum=Num then inc(result);
      i:=1;
      while (i<=SimplesNum) do
        begin
          inc(Coffs[i]);
          if Coffs[i]>MaxCoffs[i] then
            begin
              Coffs[i]:=0;
              if i=Deep then
                begin
                  inc(Deep);
                  if Deep>SimplesNum then break;
                end;
              inc(i);
            end
          else
            break;
        end;
    until Deep>SimplesNum;
    Sum_Number:=result;
  end;
{Вычислить число разложений, используя таблицу P[] для Num-1,Num-2..}
function Sum_NumberBest(Num:integer; MaxSimple:word):longint;
  var
    result: longint;
    i: word;
    SimplesNum: word;
    delta: word;
  begin
    Sum_NumberBest:=0;
    SimplesNum:=0;
    if MaxSimple>0 then SimplesNum:=MaxSimple
    else
      begin
        i:=1;
        while (Simples[i]<=Num) do
          begin
            if ((MaxSimple>0) and (Simples[i]>MaxSimple)) then break;
            inc(SimplesNum);
            inc(i);
          end;
      end;
    if SimplesNum=0 then exit;
    result:=0;
    {if Is_SimpleNumber(Num)=true then inc(result);}
    {Получили число простых чисел в интервале в SimplesNum}
    {Выполняем переборы коэффициентов}
    {writeln('processing ',Num);}
    for i:=SimplesNum downto 1 do
      begin
        {writeln('Current simple:',Simples[i]);}
        {Разница между заданным числом и старшим текущим простым}
        delta:=Num-Simples[i];
        {+Число разложений через простые числа, не большие текущего простого}
        {Используем для этого таблицу P}
        if delta<=Simples[i] then inc(result,P[delta]) else
          begin
            if Simples[i]>23 then inc(result,Sum_NumberBest(delta,i));
            if Simples[i]=23 then
              begin
                if P23[delta]>0 then inc(result,P23[delta])
                  else inc(result,Sum_NumberBest(delta,i));
              end;
            if Simples[i]=19 then
              begin
                if P19[delta]>0 then inc(result,P19[delta])
                  else inc(result,Sum_NumberBest(delta,i));
              end;
            if Simples[i]=17 then
              begin
                if P17[delta]>0 then inc(result,P17[delta])
                  else inc(result,Sum_NumberBest(delta,i));
              end;
            if Simples[i]=13 then
              begin
                if P13[delta]>0 then inc(result,P13[delta])
                  else inc(result,Sum_NumberBest(delta,i));
              end;
            if Simples[i]=11 then
              begin
                if P11[delta]>0 then inc(result,P11[delta])
                  else inc(result,Sum_NumberBest(delta,i));
              end;
            if Simples[i]=7 then
              begin
                if P7[delta]>0 then inc(result,P7[delta])
                  else inc(result,Sum_NumberBest(delta,i));
              end;
            if Simples[i]=5 then
              begin
                if P5[delta]>0 then inc(result,P5[delta])
                  else inc(result,Sum_NumberBest(delta,i));
              end;
            if Simples[i]=3 then
              begin
                if P3[delta]>0 then inc(result,P3[delta])
                  else inc(result,Sum_NumberBest(delta,i));
              end;
            if Simples[i]=2 then if (delta and 1)=0 then inc(result);
          end;
      end;
    if ((MaxSimple>0) and (Simples[MaxSimple]=3)) then P3[Num]:=result;
    if ((MaxSimple>0) and (Simples[MaxSimple]=5)) then P5[Num]:=result;
    if ((MaxSimple>0) and (Simples[MaxSimple]=7)) then P7[Num]:=result;
    if ((MaxSimple>0) and (Simples[MaxSimple]=11)) then P11[Num]:=result;
    if ((MaxSimple>0) and (Simples[MaxSimple]=13)) then P13[Num]:=result;
    if ((MaxSimple>0) and (Simples[MaxSimple]=17)) then P17[Num]:=result;
    if ((MaxSimple>0) and (Simples[MaxSimple]=19)) then P19[Num]:=result;
    if ((MaxSimple>0) and (Simples[MaxSimple]=21)) then P23[Num]:=result;
    Sum_NumberBest:=result;
  end;
var
  i,j: word;
begin
  {Test for IsSimpleNumber(..) function}
  j:=0;
  for i:=0 to MaxValidNumber do
    begin
      P3[i]:=0;
      P5[i]:=0;
      P7[i]:=0;
      P11[i]:=0;
      P13[i]:=0;
      P17[i]:=0;
      P19[i]:=0;
      P23[i]:=0;
      if Is_SimpleNumber(i)=true then
        begin
          inc(j);
          Simples[j]:=i;
          writeln('Number ',Simples[j],' is simple');
        end;
    end;
  writeln('Total: ',j);
  writeln;
  {Вычисляем число разложений чисел до 5, запоминаем их}
  P[0]:=1;
  for i:=1 to 5 do
    begin
      P[i]:=Sum_Number(i);
      writeln('Number of sum for ',i,' is ',P[i]);
    end;
  for i:=6 to 300 do
    begin
      P[i]:=Sum_NumberBest(i,0);
      writeln('Number of sum for ',i,' is ',P[i]);
    end;
  i:=20;
  writeln('Number of sum for ',i,' is ',Sum_Number(i));
end.




--------------------
I don't like the drugs (but the drugs like me). M.Manson.
PM MAIL ICQ   Вверх
MuToGeN
Дата 15.12.2002, 13:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лесник
****


Профиль
Группа: Модератор
Сообщений: 4379
Регистрация: 15.8.2002
Где: Москва

Репутация: нет
Всего: 32



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


--------------------
Three pings for the token rings,
Five pings for the UNIX machines,
Hundred pings for the broken links,
One special ping to check them all
Through Simple Network Management Protocol!
PM MAIL ICQ   Вверх
Chingachguk
Дата 15.12.2002, 17:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1232
Регистрация: 25.3.2002
Где: Москва

Репутация: 1
Всего: 18



Цитата

MuToGeN Дата сообщения: 15.12.2002,05:19
кстати если известно, что число будет не больше 5000, то вроде как божно просчитать (с помощью другой программы) все простые числа до 5000 и вставить их в программу как константу, чтобы не просчитывать заново.


Проверка ЛЮБОГО числа на простоту тут по времени <<< времени подсчета числа сумм. Хотя я и этим не побрезговал ;)


--------------------
I don't like the drugs (but the drugs like me). M.Manson.
PM MAIL ICQ   Вверх
BlowFish
Дата 17.12.2002, 18:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 97
Регистрация: 29.3.2002
Где: Санкт-Петербург

Репутация: нет
Всего: нет



To Chingachguk: так сколько по времени у тебя прога считает? например числа 200, 300..? Ты укладываешься в 15 сек?
PM MAIL   Вверх
Chingachguk
Дата 17.12.2002, 18:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1232
Регистрация: 25.3.2002
Где: Москва

Репутация: 1
Всего: 18



Цитата

BlowFish Дата сообщения: 17.12.2002,10:03
To Chingachguk: так сколько по времени у тебя прога считает? например числа 200, 300..? Ты укладываешься в 15 сек?


Моя прога на turbo pascal 7.0, компьютер был P-120. Числа до 300 считает примерно ~5-6 секунд легко.

Если выделять больше памяти на хранение количеств разложения чисел на  суммs только через простые числа, не большие 23, 29, 31 ... и т.д., то можно добиться и большего. Просто требуется выделение памяти. (Смотри мои массивы:

P3:array[0..MaxValidNumber] of longint;
P5:array[0..MaxValidNumber] of longint;
...
P23:array[0..MaxValidNumber] of longint

и их заполнение в процедуре Sum_NumberBest)


--------------------
I don't like the drugs (but the drugs like me). M.Manson.
PM MAIL ICQ   Вверх
Alex101
Дата 5.1.2003, 04:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник Клуба
Сообщений: 891
Регистрация: 8.4.2002
Где: Москва

Репутация: 1
Всего: 10



Когда проверяете, является ли число простым, то надо в качестве делителей числа N перебирать до sqrt(N) - уже на этом время сэкономите.
2 ChinqachqukИнтересный у тебя алгоритм, но слишком громоздкий.


--------------------
С уважением, А. Фролов.
PM MAIL ICQ   Вверх
Alex101
Дата 5.1.2003, 04:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник Клуба
Сообщений: 891
Регистрация: 8.4.2002
Где: Москва

Репутация: 1
Всего: 10



Интересная задачка!
Сейчас что-то влом вызовы отлавливать, завтра-послезавтра точно решу.


--------------------
С уважением, А. Фролов.
PM MAIL ICQ   Вверх
Chingachguk
Дата 8.1.2003, 18:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1232
Регистрация: 25.3.2002
Где: Москва

Репутация: 1
Всего: 18



Цитата

Alex101 Дата сообщения: 04.1.2003,20:14
Когда проверяете, является ли число простым, то надо в качестве делителей числа N перебирать до sqrt(N) - уже на этом время сэкономите.
2 ChinqachqukИнтересный у тебя алгоритм, но слишком громоздкий.


Время определения простоты числа тут некритично. Я довольствовался сравнением: делиться на 2 ? на 4 ? (битовые операции) - даже про 8 не стал писать. Хотя с корнем интересно, я не знал этого. Алгоритм не громоздкий, он просто очень тупо написан (массивы PNN[] нужно было слить в один и сделать цикл по уже двумерному массиву) - чего громоздкого, это всего лишь одна рекурсивная процедура - первая процедура (Sum_Number) вообще приведена для примера, ее можно замочить ;)


--------------------
I don't like the drugs (but the drugs like me). M.Manson.
PM MAIL ICQ   Вверх
maxim1000
  Дата 11.1.2003, 02:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 33
Всего: 110



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

Код

function quantity(n:integer;minp:integer):integer;
var
  p:integer;
  prim:boolean;
  c:integer;
begin
  if(n<minp)then
    result:=0
  else
  begin
    if(n=minp)then
      result:=1
    else
    begin
      result:=0;
      p:=minp;
      repeat
        if(n=p)then
          result:=result+1
        else
          result:=result+quantity(n-p,p);
        //find next primary number (>p)
        repeat
          inc(p);
          prim:=true;
          c:=2;
          while(c*c<=p)do
          begin
            if(p mod c=0)then
              prim:=false;
            inc©;
          end;
        until(prim or(p>n));
      until(p>n);
   end;
  end;
end;


Это сообщение отредактировал(а) maxim1000 - 9.9.2006, 22:16


--------------------
qqq
PM WWW   Вверх
Chingachguk
Дата 14.1.2003, 01:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1232
Регистрация: 25.3.2002
Где: Москва

Репутация: 1
Всего: 18



Гм. Что-то я не вижу отличия от моего решения ;)

Все та же рекурсия - но только без оптимизации. Так, например, если заменить integer->longint, то она почти мгновенно считает writeln(quantity(100,2));, но вот с writeln(quantity(200,2)); конкретно замирает - и это на P800 ! У меня считало нормально на P120 до 300 ж)


--------------------
I don't like the drugs (but the drugs like me). M.Manson.
PM MAIL ICQ   Вверх
maxim1000
Дата 14.1.2003, 01:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 33
Всего: 110



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


--------------------
qqq
PM WWW   Вверх
Chingachguk
Дата 14.1.2003, 02:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1232
Регистрация: 25.3.2002
Где: Москва

Репутация: 1
Всего: 18



Да фигня ;) интересно, что у тебя выйдет, если ты попробуешь для серьезных чисел посчитать Ж)


--------------------
I don't like the drugs (but the drugs like me). M.Manson.
PM MAIL ICQ   Вверх
Chingachguk
Дата 15.1.2003, 00:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1232
Регистрация: 25.3.2002
Где: Москва

Репутация: 1
Всего: 18



Вот, написал на Паскале для дос то же самое, но более аккуратно, с выделением памяти. Позволяет секунд за 5 на P800 сосчитать для чисел около 1000. Дальше надо либо с расширенной памятью (dpmi и тд) возиться, либо с диском ;)

Примечание: из-под среды не пойдет (не хватит памяти), надо пускать экзе.

Код

{$A-} {no align data}
{$N+} {math coprocessor and 8-bytes reals}
{$G+} {Instructions of processor 286 eanable}
{$M 60000,0,655008} {Stack=60000 bytes, Min Heap=0, MaxHeap= 655008 bytes}
type
 sres = double; {Переменная, способная вместить число сумм}
const
 MaxValidNumber = 1000;
 MaxPTablesNum = 35; {Число P-таблиц для запоминания числа сумм}
type
 Table = array[0..MaxValidNumber] of sres;
 PoTable = ^Table;
var
 P:array[0..MaxValidNumber] of sres;
 PTable:array[1..MaxPTablesNum] of PoTable;
 Simples: array [1..(MaxValidNumber shr 2)+1] of word;
function Is_SimpleNumber(Num:word):boolean;
 var
   i: word;
 begin
   Is_SimpleNumber:=false;
   if ((Num=0) or (Num=1)) then exit;
   if Num>3 then
     begin
       if (Num and 1)=0 then exit;
       if (Num and 3)=0 then exit;
     end;
   Is_SimpleNumber:=true;
   for i:=3 to (Num-1) do
     if (Num mod i)=0 then
       begin
         Is_SimpleNumber:=false;
         exit;
       end;
 end;
{Вычислить число разложений, используя таблицу P[] для Num-1,Num-2..}
function Sum_NumberBest(Num:integer; MaxSimple:word):sres;
 var
   result: sres;
   i: word;
   SimplesNum: word;
   delta: word;
 begin
   Sum_NumberBest:=0;
   SimplesNum:=0;
   if MaxSimple>0 then SimplesNum:=MaxSimple
   else
     begin
       i:=1;
       while (Simples[i]<=Num) do
         begin
           if ((MaxSimple>0) and (Simples[i]>MaxSimple)) then break;
           inc(SimplesNum);
           inc(i);
         end;
     end;
   result:=0;
   {if Is_SimpleNumber(Num)=true then inc(result);}
   {Получили число простых чисел в интервале в SimplesNum}
   {Выполняем переборы коэффициентов}
   for i:=SimplesNum downto 1 do
     begin
       {writeln('Current simple:',Simples[i]);}
       {Разница между заданным числом и старшим текущим простым}
       delta:=Num-Simples[i];
       {+Число разложений через простые числа, не большие текущего простого}
       {Используем для этого таблицу P}
       if delta<=Simples[i] then result:=result+P[delta] else
         begin
           if Simples[i]>Simples[MaxPTablesNum+1] then
             result:=result+Sum_NumberBest(delta,i)
           else
             begin
               if Simples[i]=2 then
                 begin
                   if (delta and 1)=0 then result:=result+1;
                 end
               else
                 if PTable[i-1]^[delta]>0 then result:=result+PTable[i-1]^[delta]
                    else result:=result+Sum_NumberBest(delta,i);
             end;
         end;
     end;
   if MaxSimple>0 then
     if ((Simples[MaxSimple]<=Simples[MaxPTablesNum]) and (Simples[MaxSimple]>=3))
       then PTable[MaxSimple-1]^[Num]:=result;
   Sum_NumberBest:=result;
 end;
var
 i,j,k: word;
begin
 {Выделить память под P-таблицы}
 for i:=1 to MaxPTablesNum do
   begin
     getmem(PTable[i],sizeof(Table));
     if PTable[i]=nil then
       begin
         writeln('Can''t allocate memory !');
         readln;
         exit;
       end;
   end;
 {Запомнить все простые числа в интервале 0..MaxValidNumber
  и обнулить таблицы}
 j:=0;
 for i:=0 to MaxValidNumber do
   begin
     for k:=1 to MaxPTablesNum do PTable[k]^[i]:=0;
     if Is_SimpleNumber(i)=true then
       begin
         inc(j);
         Simples[j]:=i;
         writeln('Number ',Simples[j],' is simple');
       end;
   end;
 writeln('Total: ',j);
 writeln;
 {Найдем следующее простое за MaxValidNumber число}
 i:=MaxValidNumber;
 while not Is_SimpleNumber(i)=true do inc(i);
 Simples[j+1]:=i;
 {Вычисляем число разложений чисел до N, запоминаем их}
 P[0]:=1;
 for i:=1 to 1000 do
   begin
     P[i]:=Sum_NumberBest(i,0);
     writeln('Number of sum for ',i,' is ',P[i]:12);
   end;
 {Освободить память из-под P-таблиц}
 for i:=1 to MaxPTablesNum do freemem(PTable[i],sizeof(Table));
end.



--------------------
I don't like the drugs (but the drugs like me). M.Manson.
PM MAIL ICQ   Вверх
Kuvaldis
Дата 9.9.2006, 21:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


механик-вредитель
***


Профиль
Группа: Участник Клуба
Сообщений: 1189
Регистрация: 16.6.2006
Где: Минск

Репутация: 1
Всего: 61



Нашел еще красивое решение (используется динамическое программирование)
Код

{Длинная арифметика, храним по 4 цифры в элементе массива}

const maxlen=12;

type number=array [0..maxlen] of integer;

     pnumber=^number;

procedure add(var n1,n2,nr:number);

var i,p:integer;

begin

  p:=0;

  for i:=0 to maxlen do

  begin

    p:=p+n1[i]+n2[i];

    nr[i]:=p mod 10000;

    p:=p div 10000;

  end;

end;

procedure setval(var n:number; v:integer);

var i:integer;

begin

  for i:=1 to maxlen do n[i]:=0;

  n[0]:=v;

end;

procedure print(var n:number);

var i,k:integer;

begin

  k:=maxlen;

  while (k>0) and (n[k]=0) do dec(k);

  write(n[k]);

  for i:=k-1 downto 0 do

    if n[i]<10 then write('000',n[i])

    else if n[i]<100 then write('00',n[i])

    else if n[i]<1000 then write('0',n[i])

    else write(n[i]);

  writeln;

end;

 

function isprime(n:longint):boolean;

var i:longint;

begin

  i:=2; 

  while i*i<=n do

  begin

    if n mod i=0 then

    begin

      isprime:=false;

      exit;

    end;

    inc(i);

  end;

  isprime:=true;

end;

 

{Динамическое программирование}

var q:array [2..5000] of pnumber;

    one:number;

procedure init;

var i:integer;

begin

  for i:=2 to 5000 do

  begin

    new(q[i]);

    setval(q[i]^,0);

  end;

  setval(one,1);

end;

procedure fill;

var i,j:integer;

begin 

  for i:=2 to 5000 do

    if isprime(i) then

    begin

      add(q[i]^,one,q[i]^);

      for j:=i+2 to 5000 do

        add(q[j]^,q[j-i]^,q[j]^);

    end;

end;

 

var i:integer;

begin

  init;

  fill;

  readln(i);

  print(q[i]^);

end.



--------------------
Помни - когда ты спишь, враг не дремлет
Спи чаще и дольше, изматывай врага бессоницей
PM MAIL ICQ   Вверх
Executer
Дата 10.9.2006, 02:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 6
Регистрация: 4.8.2006

Репутация: нет
Всего: нет



Это стандартная задача на динамическое программирование.
Вот общий агоритм:
1. Найдем все простые числа.
2. Затем начнем строить все разложения. Посмотрим на простом примере
2 = 2
3 = 3
4 = 2 + 2
5 = 2 + 3
6 = 2 + 2 + 2
6 = 3 + 3
Как можно подсчитать количество разложений числа 6?
А вот как - посмотрим количество разложений числа 6 - 2 и числа 6 - 3, и сложим их.
От сюда выстраивается дп алгоритм. Перебираем все числа от 0 до N, и во вложенном цикле ищем количество разложений для данного числа, заканчивающегося данным простым числом.

А вот программа на java:
(Кстати количество разбиений будет очень велико, поэтому надо брать последнии цифры числа, для числа 5000 программа работает меньше секунды на P4 3.0 Ghz)
Код


public class Primes {
    boolean notprime[] = new boolean[5001];
    int dp[][] = new int[5001][];
    public int number(int N){
        int res = N;
       //находим простые числа
        for(int i = 2; i <= N; i++){
            if(!notprime[i]){
                for(int j = 2; j <= N / i; j++){
                    notprime[i*j] = true;
                }
            }
        }

       //а здесь собственно алгоритм, перебираем числа от 0 до N
        for(int i = 0; i <= N; i++){
            dp[i] = new int[i+1];
            //если это простое число, то мы его можем разложить на само себя
            if(!notprime[i] && i > 1) dp[i][i] = 1;
            for(int j = 1; j <= i; j++){
                //складываем с вариантами разложения, оканчивающихся на меньшее числ
                dp[i][j] += dp[i][j-1];
                //еси j - простое число, то предположим что мы его прибавили к (i - j), тогда  прибавляем количество разложений чиса (i - j)
                if(!notprime[j]){
                    int idx = i - j < j ? i - j : j;
                    dp[i][j] += dp[i - j][idx];
                }
                dp[i][j] %= 100000;
            }
        }
        return dp[N][N];
    }

}


PM MAIL   Вверх
IvanoffAndrey
Дата 12.9.2006, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 157
Регистрация: 8.7.2006
Где: СГАУ

Репутация: нет
Всего: 2



Я бы пробовал искать формулу.
GoldMan, Попробуйте найти функцию f(x), x=есть разлогаемое число, а f - возвращает количесто разложений. Обычно на аллимпиадах местного масштаба редко загибают сложно - переборные задачи (часто ставят ограничение по времени - и перебор не проходит), поэтому наверняка есть иное решение.
 Наверняка эт формула будет связана с арифметическим треугольником.
Щас посижу, попробую, если получится у меня сообщу.



--------------------
Размерность пространства есть число Pi и в каждой точке вселенной оно стремиться к этому числу.
PM MAIL   Вверх
nostromo
Дата 13.9.2006, 08:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 194
Регистрация: 23.3.2006

Репутация: 5
Всего: 10



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


To KuvaldisExecuter:
Ваш метод мне в общем понятен, однако в правильности реалзации не уверен. Если не сложно, приведите пожалуйста значения, которые выдают ваши программы, скажем, для чисел 7, 20 и 1000.

Добавлено @ 08:28 
Да, Executer, как Ваша программа могла посчитать ответ для 5000, если уже ответ для 1400 не влазит в 8-байтное целое, а длинную арифметику Вы, судя по коду, не используете?
PM MAIL   Вверх
Kuvaldis
Дата 13.9.2006, 10:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


механик-вредитель
***


Профиль
Группа: Участник Клуба
Сообщений: 1189
Регистрация: 16.6.2006
Где: Минск

Репутация: 1
Всего: 61



Код на Pascal - с сайта УрГУ. Думаю, что там правильно.

Вот лично мой вариант (программа все тесты на моей универской олимпиаде прошла)
Код

//---------------------------------------------------------------------------
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
//---------------------------------------------------------------------------
#pragma argsused
#pragma hdrstop
//---------------------------------------------------------------------------
int  simple_mas[331] = { 0 };   // все простые из интервала [2..num]
int  Erat_mas[331] = { 0 };      // решето Эратосфена
double  dp[331][331];             // используем динамическое программирование
//---------------------------------------------------------------------------
void Eratosfen(int num);
double find(int num, int count); // основная функция поиска
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
   int  count = 0;


   int  num;
   FILE*  in;       // входящий файл
   FILE*  out;      // выходящий файл
   double res;

   in = fopen("f.in", "r");
   out = fopen("f.out", "w");
   fscanf(in, "%d", &num);

   Eratosfen(num);
   res = find(num, count);
   fprintf(out, "%.0lf", res);
   
   fclose(in);
   fclose(out);  
   return 0;
}
//---------------------------------------------------------------------------
void Eratosfen(int num)
{
    int  i, j;

    for (i = 2; i <= num; i++)
       Erat_mas[i] = i;

    for (i = 2; i <= num; i++)    // для каждого числа
    {
        if (Erat_mas[i] != 0)
        {
            for (j = i * 2; j <= num;  j += i)  // убираем все кратные
            {
                Erat_mas[j] = 0;
            }    
        }
    }
    return;
}
//---------------------------------------------------------------------------
double find(int num, int count) // основная функция поиска
{
    int    res;
    int    curr_num, ind;
    double sum;

    for (int i = 2; i <= num; i++)    // пока не дойдем до нужного числа
    {  
        if (Erat_mas[i] != 0)         // если число - простое
        {
           dp[i][i] = 1;
        }   

        for (int j = 0; simple_mas[j] < i; j++)
        {
            ind = simple_mas[j];
            curr_num = i - ind;                 // старая строка
            sum = 0;
            for (int k = ind; k <= curr_num; k++)
            {
                sum += dp[curr_num][k];
            }
            dp[i][ind] = sum;
        }
    }
    res = 0;
    for (int j = 2; j <= num; j++)
    {
        res += dp[num][j];
    }    
    return res;
}
//---------------------------------------------------------------------------


Я тесты прикрепляю (правда, у нас было ограничение для входящего числа <= 330, но все же)

Это сообщение отредактировал(а) Kuvaldis - 13.9.2006, 10:40

Присоединённый файл ( Кол-во скачиваний: 2 )
Присоединённый файл  F.zip 1,57 Kb


--------------------
Помни - когда ты спишь, враг не дремлет
Спи чаще и дольше, изматывай врага бессоницей
PM MAIL ICQ   Вверх
nostromo
Дата 13.9.2006, 12:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 194
Регистрация: 23.3.2006

Репутация: 5
Всего: 10



То Kuvaldis
посмотрел результаты --- похоже правильные (с моими, по крайней мере, совпадают).

Привожу до кучи и свою реализацию на языке Smalltalk (VisualWorks).
Суть алгоритма в следующем. Пусть есть число X и простое число P. 
Ставится задача нахождения всех вариантов представления X в виде суммы простых слагаемых, каждое из которых не превосходит P.
Эта задача рекурсивно сводится к себе с меньшими параметрами:  
func(X,P) = func(X-P, P) + func(X, предыдущий(P))
(псевдокод)

Идея подобна приводившимся ранее, однако данная ее реализация имеет свои преимущества (прежде всего, простота).

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

Код

Solver>>initialize
    primes := Integer primesUpTo: 5000.
    memoDict := Dictionary new


Solver>>func: x lastIndex: index 
    ^memoDict at: x @ index
        ifAbsent: 
            [| p1 nn |
            x = 0 ifTrue: [^1].
            index <= 0 | (x < 0) ifTrue: [^0].
            p1 := primes at: index.
            "рекурсивный вызов"
            nn := (self func: x - p1 lastIndex: index) 
                        + (self func: x lastIndex: index - 1).
            memoDict at: x @ index put: nn.
            nn]


Счет можно запускать так:
N := 4000.
index := (Integer primesUpTo: N) size.
res := worker func: N lastIndex: index. 

Вот избранные ответы:
7: 3
20: 26
1000: 48278613741845757
4000: 1629425615961660388014828752394491

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

Добавлено @ 12:32 
Забыл, перед запуском расчета, конечно, нужно создать экземпляр класса:
worker := Solver new.
--------------------
На пыльных тропинках далеких планет останутся наши следы.
PM MAIL   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Алгоритмы"

maxim1000

Форум "Алгоритмы" предназначен для обсуждения вопросов, связанных только с алгоритмами и структурами данных, без привязки к конкретному языку программирования и/или программному продукту.


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, maxim1000.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Алгоритмы | Следующая тема »


 




[ Время генерации скрипта: 0.1712 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.