Сито Эратосфена в Эрланге

Я изучаю Erlang. В качестве упражнения я взял алгоритм генерации простых чисел Сито Эратосфена. Вот мой код:

-module(seed2).
-export([get/1]).

get(N) -> WorkList = lists:duplicate(N, empty),
          get(2, N, WorkList, []).

get(thats_the_end, _N, _WorkList, ResultList) -> lists:reverse(ResultList);
get(CurrentPrime, N, WorkList, ResultList) -> ModWorkList = markAsPrime(CurrentPrime, N, WorkList),
                                              NextPrime = findNextPrime(CurrentPrime + 1, N, WorkList),
                                              get(NextPrime, N, ModWorkList, [CurrentPrime|ResultList]).


markAsPrime(CurrentPrime, N, WorkList) when CurrentPrime =< N -> WorkListMod = replace(CurrentPrime, WorkList, prime),
                                                                 markAllMultiples(CurrentPrime, N, 2*CurrentPrime, WorkListMod).

markAllMultiples(_ThePrime, N, TheCurentMark, WorkList) when TheCurentMark > N -> WorkList;
markAllMultiples(ThePrime, N, TheCurrentMark, WorkList) -> WorkListMod = replace(TheCurrentMark, WorkList, marked),
                                                           markAllMultiples(ThePrime, N, TheCurrentMark + ThePrime, WorkListMod).

findNextPrime(Iterator, N, _WorkList) when Iterator > N -> thats_the_end;
findNextPrime(Iterator, N, WorkList) -> I = lists:nth(Iterator, WorkList),
                                        if
                                          I =:= empty -> Iterator;
                                          true -> findNextPrime(Iterator + 1, N, WorkList)
                                        end.

replace(N, L, New)-> {L1, [_H|L2]} = lists:split(N - 1, L),
                     lists:append(L1, [New|L2]).

Этот код действительно работает :). Проблема в том, что у меня такое ощущение, что это не лучшая реализация.

Мой вопрос в том, каким будет «эрлангский» способ реализации «Решета Эратосфена».

Обновлено: Хорошо, решение Андреаса очень хорошее, но медленное. Есть идеи, как это улучшить?

Этот вопрос кажется не по теме, потому что он принадлежит codereview.stackexchange.com

Toto 01.02.2014 21:42

Взгляните на codereview.stackexchange.com/questions/118204/….

Don Branson 21.07.2016 16:09
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
17
2
3 728
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

Я подошел к проблеме, используя параллельную обработку.

Источник

Спасибо за быстрый ответ! Я демонстративно прочитаю ваш пост. Мой вопрос на самом деле не связан с параллелизмом, а просто старым последовательным программированием. Код получается слишком длинным по сравнению с реализацией C, так что, может быть, есть лучший способ?

Roskoto 29.09.2008 00:22

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

EvilTeach 29.09.2008 05:09

Получение параллелизма при работе с ситом - это небольшая проблема, поскольку данные не используются совместно между подпроцессами. Это все усложняет. Это одна из причин, по которой я не использовал этот алгоритм. Вы также должны взглянуть на en.wikipedia.org/wiki/Sieve_of_Atkin для другого взгляда на вещи.

EvilTeach 29.09.2008 05:13

EvilTeach, я проголосовал за ваш ответ, потому что он хороший. Я все еще не могу принять это как окончательный ответ. Если мой код разумный, я очень разочарован функциональным программированием в целом. Этот простой алгоритм намного проще реализовать на C.

Roskoto 29.09.2008 11:29

В порядке. У меня нет проблем с этим. Вот тогда настоящий вопрос. Как можно использовать параллелизм для работы сита?

EvilTeach 29.09.2008 18:00
Ответ принят как подходящий

Вот простая (но не очень быстрая) реализация сита:

-module(primes).
-export([sieve/1]).
-include_lib("eunit/include/eunit.hrl").

sieve([]) ->
    [];
sieve([H|T]) ->          
    List = lists:filter(fun(N) -> N rem H /= 0 end, T),
    [H|sieve(List)];
sieve(N) ->
    sieve(lists:seq(2,N)).

Вау, это именно то, что я искал. Это намного проще и быстрее, чем мое решение. Спасибо !!! Только один вопрос, что такое -include_lib ("unit / include / eunit.hrl"). строка о?

Roskoto 06.10.2008 12:48

О, это образец шаблона, который последовал. Это для использования EUnit, библиотеки тестирования модулей Erlang. Рад, что вы нашли код полезным :)

Andreas 07.10.2008 20:21

Обновлено: Хорошо, решение Андреаса очень хорошее, но медленное. Есть идеи, как это улучшить?

Roskoto 10.10.2008 12:28

Роското, посмотри мой ответ. Этот код ответа, каким бы красивым он ни был, не является Решетом Эратосфена.

Darius Bacon 24.12.2008 08:18

@DariusBacon, почему ты так говоришь?

Agis 04.04.2013 22:46

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

Darius Bacon 05.04.2013 00:22

@Agis Haskellwiki's страница простых чисел обсуждает это в (несколько чрезмерной) длине. бумага, связанный в теперь удаленном ответе, упомянутом выше, выражает то же самое в математике (а не в словах): сито без отложенного пробного деления (как в этом ответе) является квадратичным. сито пробного деления перенесенный ~ n ^ 1.5. правильное сито Эратосфена - ~ n ^ {1.1..1.05} с массивами или ~ n ^ 1.2 с древовидные списки.

Will Ness 19.11.2019 12:16

(указанные выше эмпирические порядки роста относятся к п, количеству произведенных простых чисел).

Will Ness 19.11.2019 12:18

мой самый быстрый код (быстрее, чем у Андреа) с использованием массива:

-module(seed4).
-export([get/1]).

get(N) -> WorkList = array:new([{size, N}, {default, empty}]),
          get(2, N, WorkList, []).

get(thats_the_end, _N, _WorkList, ResultList) -> lists:reverse(ResultList);
get(CurrentPrime, N, WorkList, ResultList) -> ModWorkList = markAsPrime(CurrentPrime, N, WorkList),
                                              NextPrime = findNextPrime(CurrentPrime + 1, N, WorkList),
                                              get(NextPrime, N, ModWorkList, [CurrentPrime|ResultList]).


markAsPrime(CurrentPrime, N, WorkList) when CurrentPrime =< N -> WorkListMod = replace(CurrentPrime, WorkList, prime),
                                                                 markAllMultiples(CurrentPrime, N, 2*CurrentPrime, WorkListMod).

markAllMultiples(_ThePrime, N, TheCurentMark, WorkList) when TheCurentMark > N -> WorkList;
markAllMultiples(ThePrime, N, TheCurrentMark, WorkList) -> WorkListMod = replace(TheCurrentMark, WorkList, marked),
                                                           markAllMultiples(ThePrime, N, TheCurrentMark + ThePrime, WorkListMod).

findNextPrime(Iterator, N, _WorkList) when Iterator > N -> thats_the_end;
findNextPrime(Iterator, N, WorkList) -> I = array:get(Iterator - 1, WorkList),
                                        if
                                          I =:= empty -> Iterator;
                                          true -> findNextPrime(Iterator + 1, N, WorkList)
                                        end.

replace(N, L, New) -> array:set(N - 1, New, L).

Я не изучал их подробно, но я протестировал свою реализацию ниже (которую я написал для задачи Project Euler), и она на порядки быстрее, чем две вышеупомянутые реализации. Это было мучительно медленно, пока я не удалил некоторые пользовательские функции и вместо этого начал искать списки: функции, которые будут делать то же самое. Хорошо усвоить урок, чтобы всегда видеть, есть ли в библиотеке реализация того, что вам нужно сделать - обычно это будет быстрее! Это вычисляет сумму простых чисел до 2 миллионов за 3,6 секунды на iMac 2,8 ГГц ...

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Sum of all primes below Max. Will use sieve of Eratosthenes 
sum_primes(Max) ->
    LastCheck = round(math:sqrt(Max)),
    All = lists:seq(3, Max, 2), %note are creating odd-only array
    Primes = sieve(All, Max, LastCheck),
    %io:format("Primes: ~p~n", [Primes]),
    lists:sum(Primes) + 2. %adding back the number 2 to the list

%sieve of Eratosthenes
sieve(All, Max, LastCheck) ->
    sieve([], All, Max, LastCheck).

sieve(Primes, All, Max, LastCheck) ->
    %swap the first element of All onto Primes 
    [Cur|All2] = All,
    Primes2 = [Cur|Primes],
    case Cur > LastCheck of 
        true ->
            lists:append(Primes2, All2); %all known primes and all remaining from list (not sieved) are prime
        false -> 
            All3 = lists:filter(fun(X) -> X rem Cur =/= 0 end, All2),
            sieve(Primes2, All3, Max, LastCheck)

    end.

Исправление: при тестировании на моей машине моя реализация была на несколько порядков быстрее, чем у Альнитака, но менее чем в 3 раза быстрее, чем у Роското. Различия между ними становятся заметными только при цифрах более 100К или около того.

Jay Mooney 14.11.2008 20:02

Критикуя свой собственный код, я смешал функции сита в сумму. Начнется более чистое сито: sieve (Max) -> All = lists: seq (3, Max, 2), LastCheck = round (math: sqrt (Max)), sieve ([], All, Max, LastCheck).

Jay Mooney 14.11.2008 20:13

Привет, BarryE, Спасибо за интерес к этому вопросу и его решению. Заглянув в ваш код, я нахожу идею Андреаса со списками: filter (rem). Имейте в виду, что rem очень медленная работа, кроме того, она не является частью исходного алгоритма, который использует суммирование.

Roskoto 16.11.2008 00:19

Что делает ваш код быстрее, так это вещь LastCheck = round (math: sqrt (Max)), которая при правильном использовании в решении для массива сделает его самым быстрым решением.

Roskoto 16.11.2008 00:21

Мне вроде как нравится эта тема, то есть простые числа, поэтому я начал немного модифицировать код BarryE, и мне удалось сделать его примерно на 70% быстрее, создав мою собственную функцию lists_filter и позволив использовать оба моих процессора. Я также упростил переключение между двумя версиями. Тестовый запуск показывает:

61> timer:tc(test,sum_primes,[2000000]).
{2458537,142913970581}

Код:



-module(test).

%%-export([sum_primes/1]).
-compile(export_all).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%Sum of all primes below Max. Will use sieve of Eratosthenes 
sum_primes(Max) ->
    LastCheck = round(math:sqrt(Max)),
    All = lists:seq(3, Max, 2), %note are creating odd-only array
    %%Primes = sieve(noref,All, LastCheck),
    Primes = spawn_sieve(All, LastCheck),
    lists:sum(Primes) + 2. %adding back the number 2 to the list


%%sieve of Eratosthenes
sieve(Ref,All, LastCheck) ->
    sieve(Ref,[], All, LastCheck).

sieve(noref,Primes, All = [Cur|_], LastCheck) when Cur > LastCheck ->
    lists:reverse(Primes, All); %all known primes and all remaining from list (not sieved) are prime    
sieve({Pid,Ref},Primes, All=[Cur|_], LastCheck) when Cur > LastCheck ->
    Pid ! {Ref,lists:reverse(Primes, All)}; 
sieve(Ref,Primes, [Cur|All2], LastCheck) ->
    %%All3 = lists:filter(fun(X) -> X rem Cur =/= 0 end, All2),
    All3 = lists_filter(Cur,All2),
    sieve(Ref,[Cur|Primes], All3,  LastCheck).


lists_filter(Cur,All2) ->
    lists_filter(Cur,All2,[]).

lists_filter(V,[H|T],L) ->
    case H rem V of
    0 ->
        lists_filter(V,T,L);
    _ ->
        lists_filter(V,T,[H|L])
    end;
lists_filter(_,[],L) ->
    lists:reverse(L).



%% This is a sloppy implementation ;)
spawn_sieve(All,Last) ->
    %% split the job
    {L1,L2} = lists:split(round(length(All)/2),All),
    Filters = filters(All,Last),
    %%io:format("F:~p~n",[Filters]),
    L3 = lists:append(Filters,L2),
    %%io:format("L1:~w~n",[L1]),
    %%    io:format("L2:~w~n",[L3]),
    %%lists_filter(Cur,All2,[]).
    Pid = self(),
    Ref1=make_ref(),
    Ref2=make_ref(),
    erlang:spawn(?MODULE,sieve,[{Pid,Ref1},L1,Last]),
    erlang:spawn(?MODULE,sieve,[{Pid,Ref2},L3,Last]),
    Res1=receive
         {Ref1,R1} ->
         {1,R1};
         {Ref2,R1} ->
         {2,R1}
     end,
    Res2= receive
          {Ref1,R2} ->
          {1,R2};
          {Ref2,R2} ->
          {2,R2}
      end,
    apnd(Filters,Res1,Res2).


filters([H|T],Last) when H 
    [H|filters(T,Last)];
filters([H|_],_) ->
    [H];
filters(_,_) ->
    [].


apnd(Filters,{1,N1},{2,N2}) ->
    lists:append(N1,subtract(N2,Filters));
apnd(Filters,{2,N2},{1,N1}) ->
    lists:append(N1,subtract(N2,Filters)).



subtract([H|L],[H|T]) ->
    subtract(L,T);
subtract(L=[A|_],[B|_]) when A > B ->
    L;
subtract(L,[_|T]) ->
    subtract(L,T);
subtract(L,[]) ->
    L.

Мой предыдущий пост не был правильно отформатирован. Вот репост кода. Извините за рассылку спама ...


-module(test).

%%-export([sum_primes/1]).
-compile(export_all).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%Sum of all primes below Max. Will use sieve of Eratosthenes 
sum_primes(Max) ->
    LastCheck = round(math:sqrt(Max)),
    All = lists:seq(3, Max, 2), %note are creating odd-only array
    %%Primes = sieve(noref,All, LastCheck),
    Primes = spawn_sieve(All, LastCheck),
    lists:sum(Primes) + 2. %adding back the number 2 to the list


%%sieve of Eratosthenes
sieve(Ref,All, LastCheck) ->
    sieve(Ref,[], All, LastCheck).

sieve(noref,Primes, All = [Cur|_], LastCheck) when Cur > LastCheck ->
    lists:reverse(Primes, All); %all known primes and all remaining from list (not sieved) are prime    
sieve({Pid,Ref},Primes, All=[Cur|_], LastCheck) when Cur > LastCheck ->
    Pid ! {Ref,lists:reverse(Primes, All)}; 
sieve(Ref,Primes, [Cur|All2], LastCheck) ->
    %%All3 = lists:filter(fun(X) -> X rem Cur =/= 0 end, All2),
    All3 = lists_filter(Cur,All2),
    sieve(Ref,[Cur|Primes], All3,  LastCheck).


lists_filter(Cur,All2) ->
    lists_filter(Cur,All2,[]).

lists_filter(V,[H|T],L) ->
    case H rem V of
    0 ->
        lists_filter(V,T,L);
    _ ->
        lists_filter(V,T,[H|L])
    end;
lists_filter(_,[],L) ->
    lists:reverse(L).


%% This is a sloppy implementation ;)
spawn_sieve(All,Last) ->
    %% split the job
    {L1,L2} = lists:split(round(length(All)/2),All),
    Filters = filters(All,Last),
    L3 = lists:append(Filters,L2),
    Pid = self(),
    Ref1=make_ref(),
    Ref2=make_ref(),
    erlang:spawn(?MODULE,sieve,[{Pid,Ref1},L1,Last]),
    erlang:spawn(?MODULE,sieve,[{Pid,Ref2},L3,Last]),
    Res1=receive
         {Ref1,R1} ->
         {1,R1};
         {Ref2,R1} ->
         {2,R1}
     end,
    Res2= receive
          {Ref1,R2} ->
          {1,R2};
          {Ref2,R2} ->
          {2,R2}
      end,
    apnd(Filters,Res1,Res2).


filters([H|T],Last) when H 
    [H|filters(T,Last)];
filters([H|_],_) ->
    [H];
filters(_,_) ->
    [].


apnd(Filters,{1,N1},{2,N2}) ->
    lists:append(N1,subtract(N2,Filters));
apnd(Filters,{2,N2},{1,N1}) ->
    lists:append(N1,subtract(N2,Filters)).



subtract([H|L],[H|T]) ->
    subtract(L,T);
subtract(L=[A|_],[B|_]) when A > B ->
    L;
subtract(L,[_|T]) ->
    subtract(L,T);
subtract(L,[]) ->
    L.

вы можете показать своему боссу это: http://www.sics.se/~joe/apachevsyaws.html. И некоторые другие (классические?) Аргументы erlang:

-постоянная работа, новый код может быть загружен на лету.

- Легко отлаживать, больше не нужно анализировать дампы ядра.

-Простота использования многоядерных процессоров / процессоров

- может быть, легко использовать кластеры?

-кто хочет заниматься указателями и прочим? Разве это не 21 век? ;)

Некоторые пифоллы: - Может показаться, что написать что-то легко и быстро, но производительность может быть отстой. Если я хочу сделать что-то быстро, я обычно пишу 2-4 разных версии одного и того же функция. И часто вам нужно пристально подходить к проблемам, которые могут быть немного отличается от того, что используется.

  • поиск вещей в списках> около 1000 элементов выполняется медленно, попробуйте использовать таблицы ets.

  • строка «abc» занимает намного больше места, чем 3 байта. Так что попробуйте использовать двоичные файлы (а это боль).

В общем, я думаю, что проблема производительности - это то, о чем нужно всегда помнить, когда пишете что-то на erlang. Чуваки из Erlang должны решить это, и я думаю, что они это сделают.

Посмотрите здесь, чтобы найти 4 различных реализации для поиска простых чисел в Erlang (два из которых являются «настоящими» ситами) и для результатов измерения производительности:

http://caylespandon.blogspot.com/2009/01/in-euler-problem-10-we-are-asked-to.html

Вот моя реализация сита, которая использует понимание списков и пытается быть хвостовой рекурсивной. Я переворачиваю список в конце, чтобы отсортировать простые числа:

primes(Prime, Max, Primes,Integers) when Prime > Max ->
    lists:reverse([Prime|Primes]) ++ Integers;
primes(Prime, Max, Primes, Integers) ->
    [NewPrime|NewIntegers] = [ X || X <- Integers, X rem Prime =/= 0 ],
    primes(NewPrime, Max, [Prime|Primes], NewIntegers).

primes(N) ->
    primes(2, round(math:sqrt(N)), [], lists:seq(3,N,2)). % skip odds

Для вычисления простых чисел до 2 мил на моем Mac с частотой 2 ГГц требуется около 2,8 мс.

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

EvilTeach 27.09.2011 06:31

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

bibby 05.11.2011 10:04

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

-module(primes).
-export([primes/1, primes/2]).

primes(X) -> sieve(range(2, X)).
primes(X, Y) -> remove(primes(X), primes(Y)).

range(X, X) -> [X];
range(X, Y) -> [X | range(X + 1, Y)].

sieve([X]) -> [X];
sieve([H | T]) -> [H | sieve(remove([H * X || X <-[H | T]], T))].

remove(_, []) -> [];
remove([H | X], [H | Y]) -> remove(X, Y);
remove(X, [H | Y]) -> [H | remove(X, Y)].

Эратосфен использует дополнения; это «решето Эйлера».

Will Ness 20.05.2016 23:25

Вот мое сито реализации эратосфена C&C, пожалуйста:

    -module(sieve).
    -export([find/2,mark/2,primes/1]).

    primes(N) -> [2|lists:reverse(primes(lists:seq(2,N),2,[]))].

    primes(_,0,[_|T]) -> T;
    primes(L,P,Primes) -> NewList = mark(L,P),
        NewP = find(NewList,P),
        primes(NewList,NewP,[NewP|Primes]).

    find([],_) -> 0;
    find([H|_],P) when H > P -> H;
    find([_|T],P) -> find(T,P). 


    mark(L,P) -> lists:reverse(mark(L,P,2,[])).

    mark([],_,_,NewList) -> NewList;
    mark([_|T],P,Counter,NewList) when Counter rem P =:= 0 -> mark(T,P,Counter+1,[P|NewList]);
    mark([H|T],P,Counter,NewList) -> mark(T,P,Counter+1,[H|NewList]). 

Вот мой образец

S = lists:seq(2,100),
lists:foldl(fun(A,X) -> X--[A] end,S,[Y||X<-S,Y<-S,X<math:sqrt(Y)+1,Y rem X==0]).

:-)

Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением.

sethvargo 01.02.2014 01:51

@sethvargo, как это бывает на самом деле, этот делает отвечает на вопрос. Этот вопрос датируется 2008 годом, когда «покажи мне лучшее» было по теме, и это ответ на вопрос. Вопрос больше не по теме и должен быть закрыт.

Charles 01.02.2014 02:00

Другие вопросы по теме