Skip to content

Instantly share code, notes, and snippets.

@sld
Created November 1, 2012 13:36
Show Gist options
  • Save sld/3993668 to your computer and use it in GitHub Desktop.
Save sld/3993668 to your computer and use it in GitHub Desktop.
erlang university homework
% 25. Создать приложение с тремя потоками. Потоки работают с массивами одинаковой размерности.
% Массивы содержат положительные числа.
% Потоки производят одно действие над массивом, находят произведение и сумму элементов массива,
% затем проверяют условие равенства сумм трех потоков или равенства произведений трех потоков,
% если условие выполняется, потоки завершают работу. В противном случае, все описанные шаги повторяются.
% Поток может производить над массивом следующие действия; заменить один произвольный четный элемент
% любым нечетным числом или заменить один произвольный нечетный элемент любым четным числом.
% РГР по параллельному программированию
-module (task25).
-export ([replace/3, find_first_diff/2, array_minus/2, index_of/2,
replace_odd_even/2, find_most_freq_elem_ind/1, run/0,
product/1, print_array/2, random_array/1]).
odd(Num) ->
Num rem 2 == 0.
even( Num ) ->
not odd(Num).
oddeven_or_evenodd( E1, E2 ) ->
case odd(E1) and even(E2) of
true ->
true;
false ->
case even(E1) and odd(E2) of
true ->
true;
false ->
false
end
end.
arrays_equal(A, B) ->
(A--B)==[].
check_sums_eq( A, B, C) ->
(A == B) and (B == C).
check_prods_eq( A, B, C) ->
(A == B) and (B == C).
% Проверка равнества суммы или произведения трех массивов
check_sums_or_products_eq( E1, E2 ,E3 ) ->
{From1, Sum1, Prod1} = E1,
{From2, Sum2, Prod2} = E2,
{From3, Sum3, Prod3} = E3,
check_sums_eq(Sum1, Sum2, Sum3) or check_prods_eq( Prod1, Prod2, Prod3 ).
print_array( List, Node ) ->
Printer = fun(E) -> io:format("[~w]E: ~p~n",[Node, E]) end,
lists:foreach(Printer,List).
random_array(N) -> randomize(N, []).
randomize(0, Array) -> Array;
randomize(N, Array) -> randomize(N-1, [random:uniform(300) | Array]).
product( [H | T] ) ->
product( T, H*1 ).
product( [], Prod) ->
Prod;
product( [H | T], Prod) ->
product( T, H*Prod).
index_of(Item, List) -> index_of(Item, List, 0).
index_of(_, [], _) -> not_found;
index_of(Item, [Item|_], Index) -> Index;
index_of(Item, [_|Tl], Index) -> index_of(Item, Tl, Index+1).
array_minus( [H1 | Arr1], [H2 | Arr2] ) ->
array_minus( Arr1, Arr2, [H1 - H2] ).
array_minus( [], [], MinusArr ) ->
lists:reverse( MinusArr );
array_minus( [H1 | Arr1], [H2 | Arr2], MinusArr ) ->
Minus = H1 - H2,
array_minus( Arr1, Arr2, [Minus | MinusArr] ).
%replace([1,2,3], 0, 33) => [33,2,3]
replace( [H | T], Ind, NewEl ) ->
replace( T, [H], 0, Ind, NewEl ).
replace( Tarr, [H | Harr], Ind, Ind, NewEl) ->
lists:reverse(Harr) ++ [ NewEl | Tarr ];
replace( [H | T], Harr, CntInd, Ind, NewEl ) ->
replace( T, [H | Harr], CntInd + 1, Ind, NewEl ).
find_not_zero( Arr ) ->
find_not_zero( Arr, 0 ).
find_not_zero( [], Ind ) ->
{ not_found, 0 };
find_not_zero( [H | T], Ind ) ->
if
not(H == 0) ->
Ind;
true ->
find_not_zero( T, Ind+1)
end.
diff_elem_not_found( Status1, Status2 ) ->
case Status1 of
not_found ->
case Status2 of
not_found ->
{not_found, couple};
found ->
{not_found, first}
end;
found ->
case Status2 of
not_found ->
{not_found, second};
found ->
false
end
end.
% Находим index most frequent value( наиболее часто встречающееся значение ) в массиве
% [1,2,2,3] => 2
% Результат возвращается для отсортированного массива
% Так что лучше возвращать само значение, а не индекс
find_most_freq_elem_ind_sort( NotFoundArr ) ->
[H | SortedList] = lists:sort( NotFoundArr ),
find_most_freq_elem_ind_sort( SortedList, {H , 1}, 1, 0, 1 ).
find_most_freq_elem_ind_sort( [], {H , Cnt}, MaxCnt, MaxInd, CurInd ) ->
case Cnt > MaxCnt of
true ->
CurInd;
false ->
MaxInd
end;
find_most_freq_elem_ind_sort( [H | T], {H , Cnt}, MaxCnt, MaxInd, CurInd ) ->
case Cnt+1 > MaxCnt of
true ->
find_most_freq_elem_ind_sort( T, {H , Cnt+1}, Cnt+1, CurInd, CurInd+1 );
false ->
find_most_freq_elem_ind_sort( T, {H , Cnt+1}, MaxCnt, MaxInd, CurInd+1 )
end;
find_most_freq_elem_ind_sort( [H | T], {El , Cnt}, MaxCnt, MaxInd, CurInd ) ->
find_most_freq_elem_ind_sort( T, {H , 1}, MaxCnt, MaxInd, CurInd+1 ).
% Определяем MFV для исходного массива, путем поиска в отсортированном
find_most_freq_elem_ind( NotFoundArr ) ->
SortedInd = find_most_freq_elem_ind_sort( NotFoundArr ),
SortedList = lists:sort( NotFoundArr ),
El = lists:nth( SortedInd+1, SortedList ),
index_of( El, NotFoundArr ).
% Находим most common value для массива без уникального элемента сравниваем с массивом содержащий
% уникальный элемент -> [1,2,3,2] AND [1,2,3,4] => {filtered, 1}
filter_not_found_diff_elem( NotFoundArr, Ind, Arr ) ->
NewInd = find_most_freq_elem_ind( NotFoundArr ),
{ filtered, NewInd }.
% Находим индекс элемента в массиве Arr1, который отличается от всех в Arr2
% [1,2,3,4] AND [2,3,4,5] => {found, 0}
find_diff_elem_ind( Arr1, Arr2 ) ->
find_diff_elem_ind( Arr1, Arr2, 0).
find_diff_elem_ind( [], Arr2, Cnt) ->
{not_found, diff_elem};
find_diff_elem_ind( [ H | T ], Arr2, Cnt) ->
case lists:member(H, Arr2) of
false ->
{found, Cnt};
true ->
find_diff_elem_ind( T, Arr2, Cnt+1 )
end.
% Возвращает позиции элементов которых нету в обоих массивах одновременно
%find_first_diff([1,2,3], [2,1,4]) -> 2, 2
%find_first_diff([1,2,3,20,30], [2,1,4,30,40]) -> 2, 2
%find_first_diff([1,2,20,30], [2,1,30,40]) -> 2, 3
% Если в одном массиве есть уникальный элемент, а во втором нету
% например [1,2,2,3] AND [1,2,3,4] - то вернется индекс наиболее часто встречающегося
% элемента в первом и уникального во втором массиве -> {diff, 2, 3}
% Если в обоих нету, то вернутся два случайных индекса -> [1,2,2,3] AND [1,1,2,3] -> {diff, 0, 3}
find_first_diff( Arr1, Arr2 ) ->
{Status1, Ind1} = find_diff_elem_ind( Arr1, Arr2 ),
{Status2, Ind2} = find_diff_elem_ind( Arr2, Arr1 ),
case diff_elem_not_found( Status1, Status2 ) of
{not_found, couple} ->
NewInd1 = random:uniform( lists:flatlength(Arr1) - 1 ), %find_most_freq_elem_ind(Arr1),
NewInd2 = random:uniform( lists:flatlength(Arr2) - 1 ), %find_most_freq_elem_ind(Arr2),
{diff, NewInd1, NewInd2};
{not_found, first} ->
{_, NewInd } = filter_not_found_diff_elem( Arr1, Ind2, Arr2 ),
{ diff, NewInd, Ind2 };
{not_found, second} ->
{_, NewInd } = filter_not_found_diff_elem( Arr2, Ind1, Arr1 ),
{ diff, Ind1, NewInd };
false ->
{ diff, Ind1, Ind2 }
end.
% Заменяем четный элемент на нечетный или наоборот
% Если в обоих массивах оба элемента четные или нечетные, то заменяем
% соответственно на нечетный(1) и четный(2)
% возвращает {replaced, RepArr1, RepArr2}
% [10,10,10] AND [21,21,21] -> [10,10,10] AND [10,21,21]
% [10,10,10] AND [20,20,20] -> [1,10,10] AND [1,20,20]
replace_odd_even( Arr1, Arr2 ) ->
{_, Arr1Ind, Arr2Ind } = find_first_diff( Arr1, Arr2 ),
Arr1Val = lists:nth( Arr1Ind+1, Arr1 ),
Arr2Val = lists:nth( Arr2Ind+1, Arr2 ),
case oddeven_or_evenodd( Arr1Val, Arr2Val ) of
true ->
RepArr2 = replace( Arr2, Arr2Ind, Arr1Val ),
{replaced, [], RepArr2};
false ->
case odd( Arr1Val ) of
true ->
RepArr2 = replace( Arr2, Arr2Ind, 1 ),
RepArr1 = replace( Arr1, Arr1Ind, 1 ),
{replaced, RepArr1, RepArr2};
false ->
RepArr2 = replace( Arr2, Arr2Ind, 2 ),
RepArr1 = replace( Arr1, Arr1Ind, 2 ),
{replaced, RepArr1, RepArr2}
end
end.
%%
%% Начинаем работу с процессами
%%
% Имитация критической секции - принадлежит процессу sum_prod_pid
wait_for_processing_arrays( [] ) ->
ok;
wait_for_processing_arrays( Arr ) ->
receive
{ processed, Pid } ->
wait_for_processing_arrays(lists:delete(Pid, Arr))
end.
% Проверяем равенство произведения или суммы для потоков данных по условию задачи
% Если равны то завершаем пересчет массивов
% Иначе отправляем сообщение для пересчета массивов
% (From1 и From2) и (From1 и From3)
% Как только они пересчитаны( заменены четные на нечетные и тд ) отправляем сообщение
% процессу recalculator для пересчета сумм.
sum_prod_check3( [E1, E2, E3] ) ->
{F1, _, _} = E1,
{F2, _, _} = E2,
{F3, _, _} = E3,
% [From1, From2, From3] = lists:sort([F1, F2, F3]),
[From1, From2, From3] = [F1, F2, F3],
case check_sums_or_products_eq( E1, E2, E3 ) of
true ->
From1 ! finish,
From2 ! finish,
From3 ! finish,
recalculator ! stop;
false ->
From1 ! {send_array_to_process_in, From2},
From1 ! {send_array_to_process_in, From3},
wait_for_processing_arrays( [ From1, From2, From3] ),
recalculator ! recalculate
end.
% Проверка равенства произведения или суммы - начинается когда накапливается три массива
% на процессе проверки
sum_prod_check( Arr ) ->
receive
{ sum_prod, From, Sum, Prod } ->
NewArr = [{From, Sum, Prod} | Arr],
case (lists:flatlength(NewArr) == 3) of
true ->
sum_prod_check3( NewArr ),
sum_prod_check( [] );
false ->
case (lists:flatlength(NewArr) > 3) of
true ->
sum_prod_check( [lists:last(NewArr)] );
false ->
sum_prod_check( NewArr )
end
end
end.
log( Pid, Arr ) ->
% ok.
file:write_file("./log", io_lib:fwrite("~w ~p.\n", [Pid, Arr]), [append]).
log( Pid, Arr, From, RepArr) ->
% ok.
file:write_file("./log", io_lib:fwrite("~w --> ~w ~p -- ~p.\n\n", [Pid, From, Arr, RepArr]), [append]).
% Основной процесс работы с массивом
process_array( Arr ) ->
receive
finish ->
io:format( "Finish Node:~w ~n", [self()]),
log( self(), Arr ),
process_array(Arr);
{ sum_prod, SumProdPid } ->
Sum = lists:sum( Arr ),
Prod = product( Arr ),
SumProdPid ! { sum_prod, self(), Sum, Prod},
process_array( Arr );
print_array ->
print_array(Arr, self());
{ send_array_to_process_in, In } ->
In ! {process, self(), Arr},
process_array( Arr );
{ replace, From, RepArr } ->
log(self(), Arr, From, RepArr),
sum_prod_pid ! {processed, self()},
process_array( RepArr );
{ process, From, GetArray } ->
case arrays_equal(GetArray, Arr) of
true ->
sum_prod_pid ! {processed, self()},
sum_prod_pid ! {processed, From},
process_array( Arr );
false ->
{_, Arr1, Arr2} = replace_odd_even( GetArray, Arr ),
log(self(), Arr, From, Arr2),
case not(Arr1 == []) of
true ->
From ! {replace, self(), Arr1},
sum_prod_pid ! {processed, self()},
process_array( Arr2 );
false ->
sum_prod_pid ! {processed, self()},
sum_prod_pid ! {processed, From},
process_array( Arr2 )
end
end
end.
% Функция посылает сообщение на process_array для проверки сумм и произведений в массивах
sumprod_recalculator() ->
receive
recalculate ->
pid1 ! {sum_prod, sum_prod_pid},
pid2 ! {sum_prod, sum_prod_pid},
pid3 ! {sum_prod, sum_prod_pid},
sumprod_recalculator();
stop ->
ok
end.
% Основная функция которая запускает процессы
% Результат записывается в файл log, местоположение которого совпадает с папкой приложения
run() ->
file:write_file("./log", ""),
% Size = 500,
% Arr1 = random_array(Size),
% Arr2 = random_array(Size),
% Arr3 = random_array(Size),
% Arr1 = [15,21,28,18,10,16,29,22,14,3],
% Arr2 = [1,13,14,7,17,5,21,7,5,18],
% Arr3 = [7,6,10,30,18,2,10,13,15,17],
Arr1 = [20,20,20],
Arr2 = [21,21,21],
Arr3 = [32,33,31],
% Arr1 = [2,1,1,25,8,10,25,26,27,2],
% Arr2 = [18,24,27,27,21,12,19,17,30,2],
% Arr3 = [3,20,21,18,12,3,24,13,22,9],
register(sum_prod_pid, spawn( fun() -> sum_prod_check( [] ) end )),
register(recalculator, spawn( fun() -> sumprod_recalculator() end ) ),
register(pid1, spawn( fun() -> process_array( Arr1 ) end ) ),
register(pid2, spawn( fun() -> process_array( Arr2 ) end) ),
register(pid3, spawn( fun() -> process_array( Arr3 ) end) ),
file:write_file("./log", io_lib:fwrite("~w ~p.\n ~w ~p.\n ~w ~p.\n", [whereis(pid1), Arr1, whereis(pid2), Arr2, whereis(pid3), Arr3]), [append]),
recalculator ! recalculate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment