Concurrent Prime Generator - PullRequest
       10

Concurrent Prime Generator

6 голосов
/ 18 сентября 2008

Я изучаю проблемы на projecteuler.net, чтобы узнать, как программировать на Эрланге, и мне тяжело создать генератор простых чисел, который может создать все простые числа менее 2 миллионов менее чем за минуту. Используя последовательный стиль, я уже написал три типа генераторов, включая Сито Эратосфена, и ни один из них не работает достаточно хорошо.

Я полагал, что параллельное Sieve будет отлично работать, но я получаю сообщения bad_arity, и я не уверен, почему. Любые предложения о том, почему у меня проблема, или как правильно ее кодировать?

Вот мой код, закомментированные разделы - это то, где я пытался сделать вещи параллельными:

-module(primeserver).
-compile(export_all).

start() ->
    register(primes, spawn(fun() -> loop() end)).

is_prime(N) -> rpc({is_prime,N}).

rpc(Request) ->
    primes ! {self(), Request},
    receive
        {primes, Response} ->
            Response
    end.

loop() ->
    receive
        {From, {is_prime, N}} ->
            if
                N  From ! {primes, false};
                N =:= 2 -> From ! {primes, true};
                N rem 2 =:= 0 -> From ! {primes, false};
                true ->
                    Values = is_not_prime(N),
                    Val = not(lists:member(true, Values)),
                    From ! {primes, Val}
            end,
            loop()
    end.

for(N,N,_,F) -> [F(N)];
for(I,N,S,F) when I + S  [F(I)|for(I+S, N, S, F)];
for(I,N,S,F) when I + S =:= N -> [F(I)|for(I+S, N, S, F)];
for(I,N,S,F) when I + S > N -> [F(I)].

get_list(I, Limit) ->
    if
        I 
            [I*A || A 
            []
    end.

is_not_prime(N) ->
    for(3, N, 2, 
        fun(I) -> 
            List = get_list(I,trunc(N/I)),
            lists:member(N,lists:flatten(List))
        end
        ).

    %%L = for(1,N, fun() -> spawn(fun(I) -> wait(I,N) end) end),
    %%SeedList = [A || A  
    %%      lists:foreach(fun(X) ->
    %%              Pid ! {in_list, X} 
    %%                end, SeedList)
    %%        end, L).

%%wait(I,N) ->
%%  List = [I*A || A  lists:member(X,List)
%%  end.

Ответы [ 10 ]

7 голосов
/ 06 марта 2010

Я написал параллельное простое сито Эратосфена, используя Go и каналы.

Вот код: http://github.com/aht/gosieve

Я писал об этом здесь: http://blog.onideas.ws/eratosthenes.go

Программа может просеивать первый миллион простых чисел (все простые числа до 15 485 863) примерно за 10 секунд. Сито является параллельным, но алгоритм в основном синхронный: слишком много точек синхронизации требуется между программами («актерами» - если хотите), и поэтому они не могут свободно перемещаться параллельно.

3 голосов
/ 22 сентября 2008

Ошибка 'badarity' означает, что вы пытаетесь вызвать 'fun' с неправильным количеством аргументов. В этом случае ...

%% L = для (1, N, fun () -> spawn (конец fun (I) -> end (I, N) end)),

Функция for / 3 ожидает веселья от arity 1, а функция spawn / 1 ожидает веселья от arity 0. Попробуйте вместо этого:

L = for(1, N, fun(I) -> spawn(fun() -> wait(I, N) end) end),

Веселье, передаваемое spawn, наследует необходимые части его окружения (а именно I), поэтому нет необходимости передавать его явно.

Хотя вычисление простых чисел всегда доставляет удовольствие, имейте в виду, что это не та проблема, для решения которой Эрланг был предназначен. Erlang был разработан для массового параллелизма в стиле актера. Скорее всего, он будет плохо работать на всех примерах параллельных вычислений. Во многих случаях последовательное решение, скажем, в ML будет настолько быстрым, что Эрлангу не хватит любого количества ядер, и, например, F # и параллельная библиотека задач .NET , безусловно, будут гораздо лучшим средством для подобных операций.

1 голос
/ 26 января 2009

Вы можете найти четыре различных реализации Эрланга для нахождения простых чисел (две из которых основаны на Сите Эратосфена) здесь . Эта ссылка также содержит графики, сравнивающие производительность 4 решений.

1 голос
/ 18 сентября 2008

Другой альтернативой для рассмотрения является использование вероятностной простой генерации. Есть пример этого в книге Джо («главный сервер»), в которой, я думаю, используется Миллер-Рабин ...

0 голосов
/ 27 августа 2009

Два быстрых однопроцессных генератора Erlang Prime; sprimes генерирует все простые числа до 2 м за ~ 2,7 с, а у ~ 3 с на моем компьютере (Macbook с 2,4 ГГц Core 2 Duo). Оба основаны на Сите Эратосфена, но поскольку Erlang лучше всего работает со списками, а не с массивами, оба хранят список не исключенных простых чисел, проверяя делимость текущей головой и сохраняя накопитель проверенных простых чисел. Оба также реализуют простое колесо для первоначального сокращения списка.

-module(primes).
-export([sprimes/1, wheel/3, fprimes/1, filter/2]).    

sieve([H|T], M) when H=< M -> [H|sieve([X || X<- T, X rem H /= 0], M)];
sieve(L, _) -> L.
sprimes(N) -> [2,3,5,7|sieve(wheel(11, [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10], N), math:sqrt(N))].

wheel([X|Xs], _Js, M) when X > M ->
    lists:reverse(Xs);
wheel([X|Xs], [J|Js], M) ->
    wheel([X+J,X|Xs], lazy:next(Js), M);
wheel(S, Js, M) ->
    wheel([S], lazy:lazy(Js), M).

fprimes(N) ->
    fprimes(wheel(11, [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10], N), [7,5,3,2], N).
fprimes([H|T], A, Max) when H*H =< Max ->
    fprimes(filter(H, T), [H|A], Max);
fprimes(L, A, _Max) -> lists:append(lists:reverse(A), L).

filter(N, L) ->
    filter(N, N*N, L, []).
filter(N, N2, [X|Xs], A) when X < N2 ->
    filter(N, N2, Xs, [X|A]);
filter(N, _N2, L, A) ->
    filter(N, L, A).
filter(N, [X|Xs], A) when X rem N /= 0 ->
    filter(N, Xs, [X|A]);
filter(N, [_X|Xs], A) ->
    filter(N, Xs, A);
filter(_N, [], A) ->
    lists:reverse(A).

lazy: lazy / 1 и lazy: next / 1 относятся к простой реализации псевдо-ленивых бесконечных списков:

lazy(L) ->
    repeat(L).

repeat(L) -> L++[fun() -> L end].

next([F]) -> F()++[F];
next(L) -> L.

Генерация простых чисел с помощью сит не является хорошим местом для параллелизма (но он может использовать параллелизм при проверке делимости, хотя операция не достаточно сложна, чтобы оправдать дополнительные издержки всех параллельных фильтров, которые я написал до сих пор).

`

0 голосов
/ 11 марта 2009

вот версия vb

    'Sieve of Eratosthenes 
'http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes 
'1. Create a contiguous list of numbers from two to some highest number n. 
'2. Strike out from the list all multiples of two (4, 6, 8 etc.). 
'3. The list's next number that has not been struck out is a prime number. 
'4. Strike out from the list all multiples of the number you identified in the previous step. 
'5. Repeat steps 3 and 4 until you reach a number that is greater than the square root of n (the highest number in the list). 
'6. All the remaining numbers in the list are prime. 
Private Function Sieve_of_Eratosthenes(ByVal MaxNum As Integer) As List(Of Integer)
    'tested to MaxNum = 10,000,000 - on 1.8Ghz Laptop it took 1.4 seconds
    Dim thePrimes As New List(Of Integer)
    Dim toNum As Integer = MaxNum, stpw As New Stopwatch
    If toNum > 1 Then 'the first prime is 2
        stpw.Start()
        thePrimes.Capacity = toNum 'size the list
        Dim idx As Integer
        Dim stopAT As Integer = CInt(Math.Sqrt(toNum) + 1)
        '1. Create a contiguous list of numbers from two to some highest number n.
        '2. Strike out from the list all multiples of 2, 3, 5. 
        For idx = 0 To toNum
            If idx > 5 Then
                If idx Mod 2 <> 0 _
                AndAlso idx Mod 3 <> 0 _
                AndAlso idx Mod 5 <> 0 Then thePrimes.Add(idx) Else thePrimes.Add(-1)
            Else
                thePrimes.Add(idx)
            End If
        Next
        'mark 0,1 and 4 as non-prime
        thePrimes(0) = -1
        thePrimes(1) = -1
        thePrimes(4) = -1
        Dim aPrime, startAT As Integer
        idx = 7 'starting at 7 check for primes and multiples 
        Do
            '3. The list's next number that has not been struck out is a prime number. 
            '4. Strike out from the list all multiples of the number you identified in the previous step. 
            '5. Repeat steps 3 and 4 until you reach a number that is greater than the square root of n (the highest number in the list). 
            If thePrimes(idx) <> -1 Then ' if equal to -1 the number is not a prime
                'not equal to -1 the number is a prime
                aPrime = thePrimes(idx)
                'get rid of multiples 
                startAT = aPrime * aPrime
                For mltpl As Integer = startAT To thePrimes.Count - 1 Step aPrime
                    If thePrimes(mltpl) <> -1 Then thePrimes(mltpl) = -1
                Next
            End If
            idx += 2 'increment index 
        Loop While idx < stopAT
        '6. All the remaining numbers in the list are prime. 
        thePrimes = thePrimes.FindAll(Function(i As Integer) i <> -1)
        stpw.Stop()
        Debug.WriteLine(stpw.ElapsedMilliseconds)
    End If
    Return thePrimes
End Function
0 голосов
/ 18 сентября 2008

параллельный алгоритм простых чисел: http://www.cs.cmu.edu/~scandal/cacm/node8.html

0 голосов
/ 18 сентября 2008

Сито Эратосфена довольно легко внедрить, но, как вы обнаружили, не самое эффективное. Вы пробовали сито Аткина?

Сито Аткина @ Википедия

0 голосов
/ 18 сентября 2008

Задачи проекта Эйлера (я бы сказал, что большинство первых 50, если не больше) в основном связаны с грубой силой и изобретательностью в выборе границ.

Не забудьте проверить любое, если N простое число (методом грубой силы), вам нужно только посмотреть, делится ли оно на любое простое число до пола (sqrt (N)) + 1, а не N / 2.

Удачи

0 голосов
/ 18 сентября 2008

Я люблю Project Euler.

Что касается главных генераторов, я большой поклонник Сита Эратосфена.

Для чисел менее 2 000 000 вы можете попробовать простую реализацию проверки isPrime. Я не знаю, как бы вы сделали это на эрланге, но логика проста.

For Each NUMBER in LIST_OF_PRIMES
     If TEST_VALUE % NUMBER == 0
          Then FALSE
END
TRUE

if isPrime == TRUE add TEST_VALUE to your LIST_OF_PRIMES

iterate starting at 14 or so with a preset list of your beginning primes. 

c # управлял таким списком для 2 000 000 в лунке ниже отметки в 1 минуту

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

...